logman 0.1.0
Modern C++23 header-only logging manager wrapping spdlog with channels, listeners, and structured events
Loading...
Searching...
No Matches
formatters.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <spdlog/details/fmt_helper.h>
8#include <spdlog/pattern_formatter.h>
9
10#include <algorithm>
11#include <cctype>
12#include <cstddef>
13#include <memory>
14#include <string>
15#include <string_view>
16#include <vector>
17
18namespace logman {
19
24class UpperLevelFormatter final : public spdlog::custom_flag_formatter {
25public:
26 void format(const spdlog::details::log_msg& msg,
27 const std::tm& /*tm*/,
28 spdlog::memory_buf_t& dest) override {
29 const auto level_sv = spdlog::level::to_string_view(msg.level);
30 std::string level_name(level_sv.data(), level_sv.size());
31 std::ranges::transform(
32 level_name, level_name.begin(), [](const unsigned char c) { return std::toupper(c); });
33 if (level_name.size() < 8) {
34 const std::size_t pad = 8U - level_name.size();
35 level_name.insert(std::size_t{0}, pad, ' ');
36 }
37 spdlog::details::fmt_helper::append_string_view(level_name, dest);
38 }
39
40 [[nodiscard]] std::unique_ptr<custom_flag_formatter> clone() const override {
41 return std::make_unique<UpperLevelFormatter>();
42 }
43};
44
48class ChannelNameFormatter final : public spdlog::custom_flag_formatter {
49public:
50 static std::string abbreviate(std::string input, const std::size_t size) {
51 if (size == 0) {
52 return {};
53 }
54
55 if (input.size() <= size) {
56 input.append(size - input.size(), ' ');
57 return input;
58 }
59
60 std::vector<std::string_view> parts;
61 {
62 std::string_view s(input);
63 std::size_t start = 0;
64 while (true) {
65 const std::size_t pos = s.find('.', start);
66 if (pos == std::string_view::npos) {
67 parts.emplace_back(s.substr(start));
68 break;
69 }
70 parts.emplace_back(s.substr(start, pos - start));
71 start = pos + 1;
72 }
73 }
74
75 auto build = [&](const std::size_t abbreviated_prefix_count,
76 const bool leading_dot) -> std::string {
77 std::string out;
78 if (leading_dot) {
79 out.push_back('.');
80 }
81 for (std::size_t i = 0; i < parts.size(); ++i) {
82 if (i != 0) {
83 out.push_back('.');
84 }
85 if (const bool is_last = (i + 1 == parts.size());
86 !is_last && i < abbreviated_prefix_count) {
87 if (!parts[i].empty()) {
88 out.push_back(parts[i].front());
89 }
90 } else {
91 out.append(parts[i]);
92 }
93 }
94 return out;
95 };
96
97 for (std::size_t abbr = 1; abbr < parts.size(); ++abbr) {
98 if (std::string candidate = build(abbr, false); candidate.size() <= size) {
99 candidate.append(size - candidate.size(), ' ');
100 return candidate;
101 }
102 }
103
104 while (parts.size() > 1) {
105 parts.erase(parts.begin());
106 if (std::string candidate = build(parts.size() - 1, true); candidate.size() <= size) {
107 candidate.append(size - candidate.size(), ' ');
108 return candidate;
109 }
110 }
111
112 return input.substr(input.size() - size);
113 }
114
115 void format(const spdlog::details::log_msg& msg,
116 const std::tm& /*tm*/,
117 spdlog::memory_buf_t& dest) override {
118 const std::string name =
119 abbreviate(std::string(msg.logger_name.data(), msg.logger_name.size()), 20);
120 spdlog::details::fmt_helper::append_string_view(name, dest);
121 }
122
123 [[nodiscard]] std::unique_ptr<custom_flag_formatter> clone() const override {
124 return std::make_unique<ChannelNameFormatter>();
125 }
126};
127
130inline constexpr std::string_view default_pattern =
131 "%Y-%m-%dT%H:%M:%S.%e%z %^%L%$ %P --- [%6t] %n : %v";
132
133} // namespace logman
n — channel name abbreviated/padded to a fixed width (20 chars).
Definition formatters.hpp:48
L — uppercase level name, right-aligned to 8 characters.
Definition formatters.hpp:24
constexpr std::string_view default_pattern
Default Ghostframe-style pattern.
Definition formatters.hpp:130