# How spdlog Stopwatch Works for Timing Log Messages

> Understand how spdlog stopwatch times log messages. Learn about this lightweight utility using std::chrono::steady_clock and custom fmt formatting for efficient timing in your logs.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: internals
- Published: 2026-07-12

---

**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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/spdlog/fmt/chrono.h) and use `std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed())` to convert the duration before formatting.

## Practical Usage Examples

```cpp
#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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/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`](https://github.com/gabime/spdlog/blob/main/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.