ROS 2 Lyrical Luth · Lesson 78 of 86
Logging and metrics
Structured logs, latency and rate metrics, percentiles and dashboards.
Estimated time · 45–75 min
Learning goals
What you will understand
- Explain logging and metrics in your own words
- Use it in a small robotics example
- Verify the result instead of assuming it worked
Understand the idea first
The mental model
Logs explain what happened; metrics show how the system behaves over time. Useful robot metrics are loop rate and jitter, callback latency, message age (now minus header stamp), queue depth, battery, CPU and temperature, and counts of errors and recoveries.
Averages hide problems; track percentiles (p50, p95, p99) and the maximum. The nearest-rank percentile of n sorted values is the value at rank ceil(p·n). Publish metrics on a topic (or diagnostics) and let a tool such as Prometheus and Grafana or Foxglove show them. Keep logging out of real-time loops.
Guided lesson
Work through these steps
- 1.
Source the ROS 2 Lyrical environment in a fresh terminal.
- 2.
Read the logging and metrics example and identify what each line does.
- 3.
Build only the affected package, source the workspace overlay, and run the example.
- 4.
Inspect the result from a second terminal with the ros2 CLI or RViz2 and compare it with the expected result.
Worked example
Read it before you run it
std::vector<double> latencies_ms;
// Collected latency samples.
latencies_ms.push_back((now() - msg->header.stamp).seconds() * 1000.0);
// Message age in milliseconds.
std::sort(latencies_ms.begin(), latencies_ms.end());
// Order the samples to read percentiles.
size_t rank = static_cast<size_t>(std::ceil(0.95 * latencies_ms.size()));
// Nearest-rank position for the 95th percentile.
double p95 = latencies_ms[rank - 1];
// The value at that rank (ranks start at 1).
RCLCPP_INFO(get_logger(), "latency p95 %.1f ms max %.1f ms", p95, latencies_ms.back());
// Report the tail, not just the average.What the example is doing
- 1.The ros2 commands query the running graph; they do not prove that a node's internal behavior is correct.
- 2.Build from the workspace root, then source install/setup.bash so the shell can discover the new package.
- 3.Use a second sourced terminal to inspect nodes and interfaces while the example runs.
Watch it run · The robot's computer
See what this code does
The terminal types this lesson's practice solution and runs it. The scene shows the kind of robot behaviour that code drives. It plays by itself and loops.
Demo for Logging and metrics. Expected result: p95=19
Practice code
Now type it yourself
Copy the starter into practice.cpp and replace every TODO with your own code. Build and run it: g++ -std=c++17 -Wall -Wextra -Wpedantic practice.cpp -o practice -pthread && ./practice
#include <algorithm>
#include <cmath>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<double> v(20);
std::iota(v.begin(), v.end(), 1.0); // 1, 2, ..., 20
std::sort(v.begin(), v.end());
// TODO: nearest-rank p95: rank = ceil(0.95 * n); the value is v[rank - 1]
std::cout << "p95=" << p95 << '\n';
return 0;
}Expected output
p95=19Stuck? Show the full solution (try it yourself first)
#include <algorithm>
#include <cmath>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<double> v(20);
std::iota(v.begin(), v.end(), 1.0); // 1, 2, ..., 20
std::sort(v.begin(), v.end());
const auto rank = static_cast<std::size_t>(std::ceil(0.95 * v.size()));
const double p95 = v[rank - 1];
std::cout << "p95=" << p95 << '\n';
return 0;
}Expected result
How you know it worked
- ROS_DISTRO is lyrical in every terminal used for the exercise.
- The complete package from the exercise builds and the expected graph entity appears in a second terminal.
Practical exercise
Apply the lesson
Measure message age for a 30 Hz topic for a minute, report p50, p95, p99 and max, and explain why the average alone would hide the worst frames. Publish the numbers on a metrics topic.
Before you continue
- ROS_DISTRO reports lyrical in every terminal you used
- The example builds and runs without errors
- You checked the result with a ros2 command, RViz2 or a test, not just by reading the code
If something goes wrong
Source /opt/ros/lyrical/setup.bash, build from the workspace root, then source install/setup.bash in that terminal.
Confirm both terminals use Lyrical, the same ROS_DOMAIN_ID, and compatible network/discovery settings.