# How spdlog Stopwatch Works for Precision Timing in C++ Logging

> Learn how spdlog stopwatch uses std::chrono to precisely time C++ operations. Get seamless timing data integration into your logs with fmt.

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

---

**spdlog::stopwatch** is a header-only utility class that leverages `std::chrono::steady_clock` to measure elapsed intervals and seamlessly formats timing data into log messages through native `fmt` integration.

The `gabime/spdlog` library provides this lightweight stopwatch feature in [`include/spdlog/stopwatch.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/stopwatch.h), offering zero-overhead timing capabilities that eliminate boilerplate when profiling code sections. Unlike external timing libraries, spdlog's stopwatch integrates directly with the logging pipeline, allowing you to inject precise duration measurements into debug or performance traces with minimal syntax.

## Core Implementation in spdlog Stopwatch

The stopwatch implementation relies on `std::chrono::steady_clock` to ensure monotonic timing unaffected by system clock adjustments.

### Construction and Initialization

When instantiating a `spdlog::stopwatch` object, the constructor captures the current time point from `std::chrono::steady_clock` and stores it in the private member `start_tp_`. This initialization occurs in [`include/spdlog/stopwatch.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/stopwatch.h) at lines 31-38, establishing the baseline for all subsequent elapsed time calculations.

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

// Construction starts the timer immediately
spdlog::stopwatch sw;  // start_tp_ set to steady_clock::now()

```

### Measuring Elapsed Time

The class provides two primary methods for retrieving elapsed duration:

- **elapsed()** – Returns a `std::chrono::duration<double>` representing the interval in seconds since construction or the last reset (lines 39-41 in [`stopwatch.h`](https://github.com/gabime/spdlog/blob/main/stopwatch.h)).
- **elapsed_ms()** – Casts the duration to `std::chrono::milliseconds` for integer-based millisecond granularity (lines 43-45).

Both methods calculate the delta between the stored `start_tp_` and the current steady clock time, ensuring accurate interval measurement even during long-running operations.

### Resetting the Timer

To measure multiple consecutive intervals without destroying the object, the `reset()` method updates `start_tp_` to the current steady clock time (lines 47-48). This effectively restarts the stopwatch without heap allocation or significant computational cost.

```cpp
sw.reset();  // Restart timing from current steady_clock::now()

```

## fmt Library Integration and Formatting

spdlog extends the `fmt` formatting system through a template specialization that enables direct insertion of stopwatch objects into log strings.

### Formatter Specialization

The specialization `fmt::formatter<spdlog::stopwatch>` inherits from `fmt::formatter<double>` and implements a custom `format()` method. This method invokes `sw.elapsed().count()` to extract the raw double value representing seconds, then delegates formatting to the base double formatter (lines 60-66 in [`include/spdlog/stopwatch.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/stopwatch.h)).

Because the formatter treats the elapsed time as a double, you control output precision using standard `fmt` format specifications:

```cpp
spdlog::info("Time: {}", sw);        // Default precision: 0.0123456
spdlog::info("Time: {:.3}", sw);     // 3 decimal places: 0.012
spdlog::info("Time: {:.6}", sw);     // Microsecond precision: 0.012345

```

### Alternative Millisecond Formatting

For native millisecond display without floating-point formatting, include [`spdlog/fmt/chrono.h`](https://github.com/gabime/spdlog/blob/main/spdlog/fmt/chrono.h) and cast the elapsed duration:

```cpp
#include <spdlog/fmt/chrono.h>
using std::chrono::duration_cast;
using std::chrono::milliseconds;

spdlog::info("Elapsed: {}", duration_cast<milliseconds>(sw.elapsed()));
// Output: "Elapsed: 12ms"

```

## Practical spdlog Stopwatch Examples

The following examples demonstrate common patterns for timing code sections and formatting the results.

### Basic Timing with Default Precision

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

int main()
{
    spdlog::stopwatch sw;
    
    // Simulate work
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    
    spdlog::info("Operation completed in {} seconds", sw);
    // Example output: "Operation completed in 0.0501234 seconds"
}

```

### High-Precision Microsecond Logging

```cpp
spdlog::stopwatch sw;
// ... performance critical section ...
spdlog::debug("Critical section: {:.6} seconds", sw);
// Forces 6 decimal places for microsecond resolution

```

### Resetting Between Measurements

```cpp
spdlog::stopwatch sw;

// First phase
process_data();
spdlog::info("Phase 1: {} seconds", sw);

sw.reset();

// Second phase
validate_results();
spdlog::info("Phase 2: {} seconds", sw);

```

### Millisecond-Accurate Timing

```cpp
#include <spdlog/fmt/chrono.h>

spdlog::stopwatch sw;
// ... work ...
spdlog::info("Duration: {}", 
    std::chrono::duration_cast<std::chrono::milliseconds>(sw.elapsed()));
// Output displays as "Duration: 50ms"

```

## Key Source Files

Understanding the stopwatch implementation requires examining these specific files in the `gabime/spdlog` repository:

- **[`include/spdlog/stopwatch.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/stopwatch.h)** – Contains the `stopwatch` class definition, `elapsed()` and `reset()` method implementations, and the `fmt::formatter` specialization that enables automatic string conversion.
- **[`tests/test_stopwatch.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_stopwatch.cpp)** – Unit tests verifying timing accuracy and formatter behavior under various precision settings.
- **[`include/spdlog/fmt/fmt.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/fmt.h)** – Provides the underlying formatting infrastructure that the stopwatch formatter extends.
- **[`include/spdlog/fmt/chrono.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/chrono.h)** – Optional header enabling `std::chrono` duration formatting when millisecond or other time unit display is required.

## Summary

- **spdlog::stopwatch** uses `std::chrono::steady_clock` to provide monotonic timing measurements immune to system clock changes.
- The constructor initializes `start_tp_` immediately, while `reset()` updates this baseline without object reconstruction.
- **elapsed()** returns double-based seconds, and **elapsed_ms()** provides integer milliseconds for different precision requirements.
- Native `fmt` integration via `formatter<spdlog::stopwatch>` allows direct insertion into log strings with customizable decimal precision using format specifiers like `{:.3}`.
- Zero-overhead design makes the stopwatch suitable for production logging and performance profiling without impacting runtime metrics.

## Frequently Asked Questions

### What clock source does spdlog stopwatch use?

The implementation uses `std::chrono::steady_clock` exclusively, as defined in [`include/spdlog/stopwatch.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/stopwatch.h). This clock guarantees monotonic progression regardless of system time adjustments, ensuring that timing measurements remain accurate even if the operating system clock changes during execution.

### How do I display stopwatch results in milliseconds instead of seconds?

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())` within your log statement. While `elapsed_ms()` returns the correct duration type, the chrono formatter provides the cleanest "ms" suffix output without manual string construction.

### Can I reset a spdlog stopwatch without creating a new object?

Yes. Calling `sw.reset()` updates the internal `start_tp_` member to the current steady clock time, effectively restarting the measurement interval. This method avoids allocation overhead and maintains the same stack-allocated object for multiple consecutive timing operations.

### Does using spdlog stopwatch impact application performance?

No. The stopwatch is header-only and performs only trivial arithmetic operations (subtracting two time points) when `elapsed()` is called. The memory footprint consists solely of one `std::chrono::steady_clock::time_point` member, making it suitable for high-frequency logging in performance-critical paths.