Step 7 of 7
Challenge: a log formatter
Every service writes logs, and consistent, sortable, greppable log lines matter when you're debugging production at 3 a.m. This challenge combines the module's tools: chrono to break a duration into parts, and std::format to lay them out.
Breaking a duration into fields works like breaking seconds into minutes and seconds, one unit at a time:
#include <chrono>
#include <format>
#include <iostream>
#include <string>
using namespace std::chrono_literals;
std::string pretty(std::chrono::seconds t) {
auto d = std::chrono::duration_cast<std::chrono::days>(t);
t -= d;
auto h = std::chrono::duration_cast<std::chrono::hours>(t);
t -= h;
auto m = std::chrono::duration_cast<std::chrono::minutes>(t);
t -= m;
return std::format("{}d {:02}h {:02}m {:02}s", d.count(), h.count(), m.count(), t.count());
}
int main() {
std::cout << pretty(93784s) << "\n";
std::cout << pretty(59s) << "\n";
std::cout << std::format("[{:<5}] boot\n[{:<5}] boot\n", "INFO", "ERROR");
}
1d 02h 03m 04s
0d 00h 00m 59s
[INFO ] boot
[ERROR] boot
The steps
duration_castto the biggest unit to get the whole number of that unit.- Subtract it from the remaining time.
- Repeat with the next smaller unit.
What's left at the end is the smallest unit. For your log line, go hours, minutes, seconds, then what remains is milliseconds (pad to 3 digits with {:03}). Hours are not split into days, so they can exceed 24.
The level name
A switch over the enum class returns the name, and {:<5} pads INFO and WARN to five characters, so the messages line up in a column no matter the level.
Your turn: write std::string log_line(Level level, std::chrono::milliseconds uptime, std::string_view msg) producing:
[HH:MM:SS.mmm] LEVEL message
- the uptime as zero-padded hours, minutes, seconds and milliseconds (hours can exceed 24)
- the level name (
DEBUG,INFO,WARN,ERROR) left-aligned in 5 characters - one space, then the message
Example: log_line(Level::Warn, 65250ms, "disk 91%") is [00:01:05.250] WARN disk 91%.