Skip to main content

How to Log Metrics in MLflow

· 6 min read
Jagdish Kumawat
Founder @ Dewiride

Metrics are the numbers a run produces: error scores, accuracy, a loss that falls during training. Unlike parameters, a metric can be logged many times, so MLflow keeps its whole history and draws it as a chart. This guide logs single values, a batch, and a metric at ten steps, reads the history back, and shows what happens when a value isn't a number.

Syntax​

Syntax
mlflow.log_metric("<key>", <number>, step=<step>)
mlflow.log_metrics({"<key>": <number>, "<key>": <number>}, step=<step>)
PartWhat it means
mlflow.log_metric(key, value)Logs one number to the active run
mlflow.log_metrics(dictionary)Logs several in one request
keyThe metric's name, with the same allowed characters as a parameter's
valueA number
step=<step>Optional: a whole number for when the value was measured, such as the training round. It "Defaults to zero if unspecified"

Logging the same key again doesn't replace the old value. It adds to the metric's history, which is how a training curve is built.

Prerequisites​

Step 1: Log Metrics​

Create metrics_demo.py:

metrics_demo.py
import mlflow
from mlflow import MlflowClient

mlflow.set_tracking_uri("http://127.0.0.1:8080")
mlflow.set_experiment("metrics-demo")

with mlflow.start_run(run_name="metrics-demo") as run:
mlflow.log_metric("rmse", 0.506)
mlflow.log_metrics({"mae": 0.328, "r2": 0.804})
for step in range(1, 11):
mlflow.log_metric("training_loss", round(1 / step, 3), step=step)

history = MlflowClient().get_metric_history(run.info.run_id, "training_loss")
print("training_loss history:", [(m.step, m.value) for m in history])
print("Latest values:", mlflow.get_run(run.info.run_id).data.metrics)
  • mlflow.log_metric(key, value) logs one number.
  • mlflow.log_metrics(dict) logs several in one request.
  • step=step records when in training the value was measured. Logging training_loss at steps 1 to 10 gives a curve. Here the values are made up (1, 0.5, 0.333…) to keep the example short; in real training they come from your model.
  • get_metric_history() returns every logged value of a metric. get_run(...).data.metrics returns only the latest value of each.
Terminal
uv run python metrics_demo.py
Output
2026/11/13 17:08:27 INFO mlflow.tracking.fluent: Experiment with name 'metrics-demo' does not exist. Creating a new experiment.
🏃 View run metrics-demo at: http://127.0.0.1:8080/#/experiments/6/runs/073244595eb8458882aa7d55547676ba
🧪 View experiment at: http://127.0.0.1:8080/#/experiments/6
training_loss history: [(1, 1.0), (2, 0.5), (3, 0.333), (4, 0.25), (5, 0.2), (6, 0.167), (7, 0.143), (8, 0.125), (9, 0.111), (10, 0.1)]
Latest values: {'rmse': 0.506, 'mae': 0.328, 'r2': 0.804, 'training_loss': 0.1}

Unlike parameters, metrics stay real numbers, and training_loss kept all ten values, while the run's summary shows the last one, 0.1.

Step 2: See the Charts​

Open the run and click Model metrics. Each metric logged once is a bar; training_loss, logged at ten steps, is a line with Step along the bottom:

The Model metrics tab of the metrics-demo run with bar charts for mae at 0.33, r2 at 0.80 and rmse at 0.51, and a line chart of training_loss falling from 1 at step 1 to 0.1 at step 10.

The runs table and the comparison views show each metric's latest value, which is usually what you compare runs by.

Step 3: Log a Value That Isn't a Number​

metrics_not_a_number.py
import mlflow

mlflow.set_tracking_uri("http://127.0.0.1:8080")
mlflow.set_experiment("metrics-demo")

with mlflow.start_run(run_name="metric-text-value"):
mlflow.log_metric("quality", "high")
Terminal
uv run python metrics_not_a_number.py
Output (last lines)
File "/Users/you/source/mlflow-demo/.venv/lib/python3.15/site-packages/mlflow/store/tracking/rest_store.py", line 1414, in log_metric
LogMetric(
run_uuid=run_id,
...<7 lines>...
dataset_digest=metric.dataset_digest,
)
TypeError: must be real number, not str

Metrics must be numbers. A label such as "high" belongs in a tag: How to Add Tags to an MLflow Run. Because the error happened inside the with block, MLflow also marked that run as failed.

Common Mistakes​

MistakeWhat happensDo this instead
Logging text as a metricTypeError: must be real number, not strLog text as a tag
Logging every epoch without stepEvery value gets step 0, MLflow's default, so they all sit at the same stepPass step=epoch
Reading run.data.metrics and expecting the historyYou get only the latest valueUse MlflowClient().get_metric_history()

Troubleshooting​

SymptomLikely causeFix
A metric shows as a bar, not a lineIt was logged onceLog it at several steps to get a line
TypeError: must be real number, not strA value is a string, such as "0.5" read from a fileConvert it with float() before logging

FAQ​

When should something be a metric, not a parameter?​

When it's a result you measure: errors, accuracy, loss, time. Settings you choose are parameters: How to Log Parameters in MLflow.

Can I log a metric for a step I skipped?​

Yes. Steps don't have to be continuous; MLflow stores whatever steps you log.

How do I log metrics for the training and the test data?​

Use different names, such as train_rmse and test_rmse.

Conclusion​

mlflow.log_metric() and mlflow.log_metrics() recorded the run's scores as numbers. Logged at ten steps with step=, training_loss became a history that get_metric_history() returned in full and the Model metrics tab drew as a falling line. A text value was refused with TypeError: must be real number, not str.

Additional Resources​