summaryrefslogtreecommitdiffstats
path: root/server/src/log.cppm
diff options
context:
space:
mode:
authorRutger Broekhoff2026-08-28 18:03:05 +0200
committerRutger Broekhoff2026-08-28 18:03:05 +0200
commit973aec43ea54bbf95b64fbcb636403401d1ca60e (patch)
tree41b7911c420766a9b463245b9296f44c5bf35258 /server/src/log.cppm
downloadroutemon-973aec43ea54bbf95b64fbcb636403401d1ca60e.tar.gz
routemon-973aec43ea54bbf95b64fbcb636403401d1ca60e.zip
Import from e4b104792206ee7ea64bf39c6b7d2c0c230f9d14
Diffstat (limited to 'server/src/log.cppm')
-rw-r--r--server/src/log.cppm148
1 files changed, 148 insertions, 0 deletions
diff --git a/server/src/log.cppm b/server/src/log.cppm
new file mode 100644
index 0000000..d7e2bff
--- /dev/null
+++ b/server/src/log.cppm
@@ -0,0 +1,148 @@
1module;
2
3// Seems like ADL for std::quoted is broken with
4// import std;
5// Might be because the _Quoted_string object is defined in
6// std::__detail, which is not exported by the module.
7#include <iomanip>
8
9export module routemon:log;
10
11import std;
12
13namespace routemon::log {
14
15 export enum class level : std::uint8_t {
16 debug,
17 info,
18 warn,
19 error,
20 };
21
22 namespace {
23
24 auto operator<<(std::ostream& os, level lvl) -> std::ostream& {
25 switch (lvl) {
26 case level::debug: os << "dbg"; break;
27 case level::info: os << "inf"; break;
28 case level::warn: os << "wrn"; break;
29 case level::error: os << "err"; break;
30 }
31 return os;
32 }
33
34 } // namespace (unique)
35
36 export class sink {
37 std::atomic<enum level> lvl_;
38 std::ostream& os_ = std::cout;
39
40 struct tmp_message {
41 level lvl;
42 std::string_view component;
43 std::string_view txt;
44 std::map<std::string, std::string> const& attrs;
45 };
46
47 auto write(tmp_message msg) -> void {
48 auto sos = std::osyncstream{os_};
49 sos << "[" << msg.lvl;
50 if (!msg.component.empty())
51 sos << " " << msg.component;
52 sos << "] " << msg.txt;
53 for (auto const& [k, v] : msg.attrs) {
54 sos << " " << k << "=" << std::quoted(v);
55 }
56 sos << '\n';
57 }
58
59 explicit sink(level lvl)
60 : lvl_{lvl}
61 {}
62
63 friend auto make_sink(level lvl) -> std::shared_ptr<sink>;
64 friend class logger;
65
66 public:
67 [[nodiscard]] auto level() const -> enum level {
68 return lvl_;
69 }
70
71 auto set_level(enum level lvl) -> void {
72 lvl_ = lvl;
73 }
74 };
75
76 export auto make_sink(level lvl) -> std::shared_ptr<sink> {
77 return std::shared_ptr<sink>{new sink{lvl}};
78 }
79
80 export class logger {
81 std::shared_ptr<sink> sink_;
82 std::string component_;
83 std::map<std::string, std::string> attrs_;
84
85 template<log::level lvl>
86 auto log_at(std::string_view fmt, std::format_args args) -> logger& {
87 if (sink_->level() <= lvl)
88 sink_->write(sink::tmp_message{
89 .lvl = lvl,
90 .component = component_,
91 .txt = std::vformat(fmt, args),
92 .attrs = attrs_,
93 });
94 return *this;
95 }
96
97 public:
98 explicit logger(std::shared_ptr<sink> const& sink)
99 : sink_{sink}
100 {
101 if (!sink) {
102 throw std::invalid_argument{"logger sink may not be null"};
103 }
104 }
105
106 [[nodiscard]] auto sub(std::string_view component) const -> logger {
107 auto l = *this;
108 if (l.component_.empty()) {
109 l.component_ = component;
110 } else {
111 l.component_ += ".";
112 l.component_ += component;
113 }
114 return l;
115 }
116
117 [[nodiscard]] auto with(std::string const& k, std::string&& v) const -> logger {
118 auto l = *this;
119 l.attrs_[k] = std::move(v);
120 return l;
121 }
122
123 [[nodiscard]] auto with(std::string const& k, std::string_view v) const -> logger {
124 return with(k, std::string{v});
125 }
126
127 template<class... Args>
128 auto debug(std::format_string<Args...> fmt, Args&&... args) -> logger& {
129 return log_at<level::debug>(fmt.get(), std::make_format_args(args...));
130 }
131
132 template<class... Args>
133 auto info(std::format_string<Args...> fmt, Args&&... args) -> logger& {
134 return log_at<level::info>(fmt.get(), std::make_format_args(args...));
135 }
136
137 template<class... Args>
138 auto warn(std::format_string<Args...> fmt, Args&&... args) -> logger& {
139 return log_at<level::warn>(fmt.get(), std::make_format_args(args...));
140 }
141
142 template<class... Args>
143 auto error(std::format_string<Args...> fmt, Args&&... args) -> logger& {
144 return log_at<level::error>(fmt.get(), std::make_format_args(args...));
145 }
146 };
147
148} // namespace routemon::log