How spdlog Stopwatch Works for Timing Log Messages

spdlog::stopwatch is a lightweight utility class that measures elapsed time using std::chrono::steady_clock and provides a custom fmt formatter that allows direct insertion of timing data into log statements.

The spdlog library provides a header-only stopwatch utility for measuring code execution without external dependencies. Located in include/spdlog/stopwatch.h, the spdlog::stopwatch class offers a zero-overhead mechanism to capture high-resolution timestamps and stream them directly into your log output using the library's formatting system.

Implementation Details in stopwatch.h

Construction and Clock Initialization

When a stopwatch object is instantiated, its constructor automatically captures the current time point from std::chrono::steady_clock and stores it in the private member start_tp_. According to the source code in include/spdlog/stopwatch.h (lines 31-38), this initialization requires no explicit start call, making the object ready to measure elapsed time immediately upon creation.

Retrieving Elapsed Time

The class provides two primary methods for accessing the measured interval:

  • elapsed(): Returns a std::chrono::duration<double> representing the number of seconds since construction or the last reset (lines 39-41).
  • elapsed_ms(): Returns the same interval cast to std::chrono::milliseconds for millisecond-level granularity (lines 43-45).

Both methods compute the difference between the current steady clock time and the stored start_tp_ value.

Resetting the Timer

To measure multiple discrete intervals without reconstructing the object, the reset() method updates start_tp_ to the current steady clock time, effectively restarting the measurement period (lines 47-48). This allows the same stopwatch instance to time sequential operations.

Formatting Integration with spdlog

The Custom fmt Formatter

The stopwatch integrates seamlessly with spdlog's formatting system through a template specialization of fmt::formatter<spdlog::stopwatch>. As implemented in include/spdlog/stopwatch.h (lines 60-66), this formatter inherits from fmt::formatter<double> and automatically invokes sw.elapsed().count() when a stopwatch object is passed to a log statement.

When you write spdlog::info("Elapsed: {}", sw), the formatter extracts the double representation of elapsed seconds and inserts it into the final message string.

Precision Control and Format Specifiers

Because the formatter treats the elapsed time as a double, you can use standard fmt format specifiers to control output precision. For example, {:.3} limits output to three decimal places, while the default formatting uses fmt's standard double precision.

For millisecond output, include spdlog/fmt/chrono.h and use std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed()) to convert the duration before formatting.

Practical Usage Examples

#include <spdlog/spdlog.h>
#include <spdlog/stopwatch.h>

int main()
{
    // Simple usage – elapsed time will be printed in seconds
    spdlog::stopwatch sw;
    // ... some work ...
    spdlog::info("Elapsed: {} seconds", sw);          // e.g. "Elapsed: 0.0123456 seconds"

    // Custom precision – show only 3 decimal places
    spdlog::debug("Elapsed: {:.3} seconds", sw);     // e.g. "Elapsed: 0.012 seconds"

    // Milliseconds – use elapsed_ms() with fmt::chrono support
    #include <spdlog/fmt/chrono.h>   // enable chrono formatting
    using std::chrono::duration_cast;
    using std::chrono::milliseconds;
    spdlog::info("Elapsed: {}", duration_cast<milliseconds>(sw.elapsed()));
    // e.g. "Elapsed: 12ms"

    // Reset the timer to measure a new interval
    sw.reset();
    // ... another operation ...
    spdlog::info("Second interval: {:.6} seconds", sw);
}

Summary

  • spdlog::stopwatch uses std::chrono::steady_clock to track elapsed time with high precision in include/spdlog/stopwatch.h.
  • The constructor automatically captures the start time in start_tp_ (lines 31-38).
  • elapsed() returns seconds as a std::chrono::duration<double>, while elapsed_ms() returns milliseconds (lines 39-45).
  • The reset() method restarts the timer by updating the internal time point (lines 47-48).
  • A custom fmt formatter specialization (lines 60-66) enables direct insertion of stopwatch objects into log statements using standard format specifiers like {:.3}.
  • Unit tests in tests/test_stopwatch.cpp verify the stopwatch's timing accuracy and formatting behavior.

Frequently Asked Questions

What clock source does spdlog stopwatch use?

The implementation relies on std::chrono::steady_clock, which guarantees monotonic time progression unaffected by system clock adjustments. This ensures accurate timing intervals even if the system time changes during execution, as the clock never moves backwards.

Can I use spdlog stopwatch without including the main spdlog header?

Yes, you can include only spdlog/stopwatch.h for the timing functionality alone. However, to log the stopwatch values using spdlog's formatting system, you need the main spdlog/spdlog.h header or the specific fmt integration headers to access the custom formatter.

How do I format stopwatch output in milliseconds instead of seconds?

Use the elapsed() method combined with chrono formatting support. Include spdlog/fmt/chrono.h and pass std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed()) to your log statement. Alternatively, you can use elapsed_ms() and cast the result appropriately for formatting.

Is spdlog stopwatch thread-safe?

Individual stopwatch objects are not thread-safe for concurrent access. If multiple threads need to time operations, each thread should maintain its own spdlog::stopwatch instance. The underlying std::chrono::steady_clock is thread-safe for reading the current time, but the start_tp_ member variable is not synchronized.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →