My Project
log.cpp
1 /*
2  * uuid-log - Microcontroller logging framework
3  * Copyright 2019,2021-2024 Simon Arlott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <uuid/log.h>
20 
21 #include <Arduino.h>
22 
23 #include <atomic>
24 #include <cstdarg>
25 #include <cstdint>
26 #include <list>
27 #include <map>
28 #include <memory>
29 #if UUID_LOG_THREAD_SAFE
30 # include <mutex>
31 #endif
32 #include <string>
33 #include <utility>
34 #include <vector>
35 
36 namespace uuid {
37 
38 namespace log {
39 
40 std::atomic<Level> Logger::global_level_{Level::OFF};
41 #if UUID_LOG_THREAD_SAFE
42 std::mutex Logger::mutex_;
43 #endif
44 
46 static Level constrain_level(Level level) {
47  if (level < Level::EMERG) {
48  level = Level::EMERG;
49  } else if (level > Level::TRACE) {
50  level = Level::TRACE;
51  }
52  return level;
53 }
55 
56 Message::Message(uint64_t uptime_ms, Level level, Facility facility, const __FlashStringHelper *name, const std::string &&text)
57  : uptime_ms(uptime_ms), level(level), facility(facility), name(name), text(std::move(text)) {
58 }
59 
60 Logger::Logger(const __FlashStringHelper *name, Facility facility)
61  : name_(name), facility_(facility) {
62 
63 };
64 
65 std::shared_ptr<std::map<Handler*,Level>>& Logger::registered_handlers() {
66  static std::shared_ptr<std::map<Handler*,Level>> handlers = std::make_shared<std::map<Handler*,Level>>();
67 
68  return handlers;
69 }
70 
71 void Logger::register_handler(Handler *handler, Level level) {
72 #if UUID_LOG_THREAD_SAFE
73  std::lock_guard<std::mutex> lock{mutex_};
74 #endif
75  auto& handlers = registered_handlers();
76 
77  handler->handlers_ = handlers;
78  (*handlers)[handler] = level;
80 };
81 
83  auto handlers = handler->handlers_.lock();
84 
85  if (handlers) {
86 #if UUID_LOG_THREAD_SAFE
87  std::lock_guard<std::mutex> lock{mutex_};
88 #endif
89 
90  if (handlers->erase(handler)) {
92  }
93  }
94 };
95 
97 #if UUID_LOG_THREAD_SAFE
98  std::lock_guard<std::mutex> lock{mutex_};
99 #endif
100  auto& handlers = registered_handlers();
101 
102  const auto level = handlers->find(const_cast<Handler*>(handler));
103 
104  if (level != handlers->end()) {
105  return level->second;
106  }
107 
108  return Level::OFF;
109 }
110 
111 void Logger::emerg(const char *format, ...) const {
112  if (enabled(Level::EMERG)) {
113  va_list ap;
114 
115  va_start(ap, format);
116  vlog_internal(Level::EMERG, facility_, format, ap);
117  va_end(ap);
118  }
119 };
120 
121 void Logger::emerg(const __FlashStringHelper *format, ...) const {
122  if (enabled(Level::EMERG)) {
123  va_list ap;
124 
125  va_start(ap, format);
126  vlog_internal(Level::EMERG, facility_, format, ap);
127  va_end(ap);
128  }
129 };
130 
131 void Logger::crit(const char *format, ...) const {
132  if (enabled(Level::CRIT)) {
133  va_list ap;
134 
135  va_start(ap, format);
136  vlog_internal(Level::CRIT, facility_, format, ap);
137  va_end(ap);
138  }
139 };
140 
141 void Logger::crit(const __FlashStringHelper *format, ...) const {
142  if (enabled(Level::CRIT)) {
143  va_list ap;
144 
145  va_start(ap, format);
146  vlog_internal(Level::CRIT, facility_, format, ap);
147  va_end(ap);
148  }
149 };
150 
151 void Logger::alert(const char *format, ...) const {
152  if (enabled(Level::ALERT)) {
153  va_list ap;
154 
155  va_start(ap, format);
156  vlog_internal(Level::ALERT, facility_, format, ap);
157  va_end(ap);
158  }
159 };
160 
161 void Logger::alert(const __FlashStringHelper *format, ...) const {
162  if (enabled(Level::ALERT)) {
163  va_list ap;
164 
165  va_start(ap, format);
166  vlog_internal(Level::ALERT, facility_, format, ap);
167  va_end(ap);
168  }
169 };
170 void Logger::err(const char *format, ...) const {
171  if (enabled(Level::ERR)) {
172  va_list ap;
173 
174  va_start(ap, format);
175  vlog_internal(Level::ERR, facility_, format, ap);
176  va_end(ap);
177  }
178 };
179 
180 void Logger::err(const __FlashStringHelper *format, ...) const {
181  if (enabled(Level::ERR)) {
182  va_list ap;
183 
184  va_start(ap, format);
185  vlog_internal(Level::ERR, facility_, format, ap);
186  va_end(ap);
187  }
188 };
189 
190 void Logger::warning(const char *format, ...) const {
191  if (enabled(Level::WARNING)) {
192  va_list ap;
193 
194  va_start(ap, format);
195  vlog_internal(Level::WARNING, facility_, format, ap);
196  va_end(ap);
197  }
198 };
199 
200 void Logger::warning(const __FlashStringHelper *format, ...) const {
201  if (enabled(Level::WARNING)) {
202  va_list ap;
203 
204  va_start(ap, format);
205  vlog_internal(Level::WARNING, facility_, format, ap);
206  va_end(ap);
207  }
208 };
209 
210 void Logger::notice(const char *format, ...) const {
211  if (enabled(Level::NOTICE)) {
212  va_list ap;
213 
214  va_start(ap, format);
215  vlog_internal(Level::NOTICE, facility_, format, ap);
216  va_end(ap);
217  }
218 };
219 
220 void Logger::notice(const __FlashStringHelper *format, ...) const {
221  if (enabled(Level::NOTICE)) {
222  va_list ap;
223 
224  va_start(ap, format);
225  vlog_internal(Level::NOTICE, facility_, format, ap);
226  va_end(ap);
227  }
228 };
229 
230 void Logger::info(const char *format, ...) const {
231  if (enabled(Level::INFO)) {
232  va_list ap;
233 
234  va_start(ap, format);
235  vlog_internal(Level::INFO, facility_, format, ap);
236  va_end(ap);
237  }
238 };
239 
240 void Logger::info(const __FlashStringHelper *format, ...) const {
241  if (enabled(Level::INFO)) {
242  va_list ap;
243 
244  va_start(ap, format);
245  vlog_internal(Level::INFO, facility_, format, ap);
246  va_end(ap);
247  }
248 };
249 
250 void Logger::debug(const char *format, ...) const {
251  if (enabled(Level::DEBUG)) {
252  va_list ap;
253 
254  va_start(ap, format);
255  vlog_internal(Level::DEBUG, facility_, format, ap);
256  va_end(ap);
257  }
258 };
259 
260 void Logger::debug(const __FlashStringHelper *format, ...) const {
261  if (enabled(Level::DEBUG)) {
262  va_list ap;
263 
264  va_start(ap, format);
265  vlog_internal(Level::DEBUG, facility_, format, ap);
266  va_end(ap);
267  }
268 };
269 
270 void Logger::trace(const char *format, ...) const {
271  if (enabled(Level::TRACE)) {
272  va_list ap;
273 
274  va_start(ap, format);
275  vlog_internal(Level::TRACE, facility_, format, ap);
276  va_end(ap);
277  }
278 };
279 
280 void Logger::trace(const __FlashStringHelper *format, ...) const {
281  if (enabled(Level::TRACE)) {
282  va_list ap;
283 
284  va_start(ap, format);
285  vlog_internal(Level::TRACE, facility_, format, ap);
286  va_end(ap);
287  }
288 };
289 
290 void Logger::log(Level level, const char *format, ...) const {
291  level = constrain_level(level);
292 
293  if (enabled(level)) {
294  va_list ap;
295 
296  va_start(ap, format);
297  vlog_internal(level, facility_, format, ap);
298  va_end(ap);
299  }
300 };
301 
302 void Logger::log(Level level, const __FlashStringHelper *format, ...) const {
303  level = constrain_level(level);
304 
305  if (enabled(level)) {
306  va_list ap;
307 
308  va_start(ap, format);
309  vlog_internal(level, facility_, format, ap);
310  va_end(ap);
311  }
312 };
313 
314 void Logger::log(Level level, Facility facility, const char *format, ...) const {
315  level = constrain_level(level);
316 
317  if (enabled(level)) {
318  va_list ap;
319 
320  va_start(ap, format);
321  vlog_internal(level, facility, format, ap);
322  va_end(ap);
323  }
324 };
325 
326 void Logger::log(Level level, Facility facility, const __FlashStringHelper *format, ...) const {
327  level = constrain_level(level);
328 
329  if (enabled(level)) {
330  va_list ap;
331 
332  va_start(ap, format);
333  vlog_internal(level, facility, format, ap);
334  va_end(ap);
335  }
336 };
337 
338 void Logger::vlog(Level level, const char *format, va_list ap) const {
339  level = constrain_level(level);
340 
341  if (enabled(level)) {
342  vlog_internal(level, facility_, format, ap);
343  }
344 }
345 
346 void Logger::vlog(Level level, Facility facility, const char *format, va_list ap) const {
347  level = constrain_level(level);
348 
349  if (enabled(level)) {
350  vlog_internal(level, facility, format, ap);
351  }
352 }
353 
354 void Logger::vlog(Level level, const __FlashStringHelper *format, va_list ap) const {
355  level = constrain_level(level);
356 
357  if (enabled(level)) {
358  vlog_internal(level, facility_, format, ap);
359  }
360 }
361 
362 void Logger::vlog(Level level, Facility facility, const __FlashStringHelper *format, va_list ap) const {
363  level = constrain_level(level);
364 
365  if (enabled(level)) {
366  vlog_internal(level, facility, format, ap);
367  }
368 }
369 
370 void Logger::vlog_internal(Level level, Facility facility, const char *format, va_list ap) const {
371  std::vector<char> text(MAX_LOG_LENGTH + 1);
372 
373  if (vsnprintf(text.data(), text.size(), format, ap) <= 0) {
374  return;
375  }
376 
377  dispatch(level, facility, text);
378 }
379 
380 void Logger::vlog_internal(Level level, Facility facility, const __FlashStringHelper *format, va_list ap) const {
381  std::vector<char> text(MAX_LOG_LENGTH + 1);
382 
383  if (vsnprintf_P(text.data(), text.size(), reinterpret_cast<PGM_P>(format), ap) <= 0) {
384  return;
385  }
386 
387  dispatch(level, facility, text);
388 }
389 
390 void Logger::logp(Level level, const char *text) const {
391  logp(level, facility_, text);
392 }
393 
394 void Logger::logp(Level level, Facility facility, const char *text) const {
395  level = constrain_level(level);
396 
397  if (enabled(level)) {
398  std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text);
399  dispatch(message);
400  }
401 }
402 
403 void Logger::dispatch(Level level, Facility facility, std::vector<char> &text) const {
404  std::shared_ptr<Message> message = std::make_shared<Message>(get_uptime_ms(), level, facility, name_, text.data());
405  text.resize(0);
406  text.shrink_to_fit();
407  dispatch(message);
408 }
409 
410 inline void Logger::dispatch(const std::shared_ptr<Message> &message) const {
411 #if UUID_LOG_THREAD_SAFE
412  std::lock_guard<std::mutex> lock{mutex_};
413 #endif
414 
415  for (auto &handler : *registered_handlers()) {
416  if (message->level <= handler.second) {
417  *handler.first << message;
418  }
419  }
420 }
421 
422 /* Mutex already locked by caller. */
424  Level level = Level::OFF;
425 
426  for (auto &handler : *registered_handlers()) {
427  if (level < handler.second) {
428  level = handler.second;
429  }
430  }
431 
433 }
434 
435 } // namespace log
436 
437 } // namespace uuid
uuid::log::Handler::handlers_
std::weak_ptr< std::map< Handler *, Level > > handlers_
Reference to registered log handlers.
Definition: log.h:339
uuid::log::Logger::err
void err(const char *format,...) const
Log a message at level Level::ERR.
Definition: log.cpp:170
uuid::get_uptime_ms
uint64_t get_uptime_ms()
Get the current uptime as a 64-bit milliseconds value.
Definition: get_uptime_ms.cpp:30
uuid::log::Logger::notice
void notice(const char *format,...) const
Log a message at level Level::NOTICE.
Definition: log.cpp:210
uuid::log::Logger::name_
const __FlashStringHelper * name_
Definition: log.h:777
uuid::log::Logger::trace
void trace(const char *format,...) const
Log a message at level Level::TRACE.
Definition: log.cpp:270
uuid::log::Facility
Facility
Facility type of the process logging a message.
Definition: log.h:103
uuid::log::Logger::dispatch
void dispatch(Level level, Facility facility, std::vector< char > &text) const
Dispatch a log message to all handlers that are registered to handle messages of the specified level.
Definition: log.cpp:403
uuid::log::Logger::Logger
Logger(const __FlashStringHelper *name, Facility facility=Facility::LOCAL0)
Create a new logger with the given name and logging facility.
Definition: log.cpp:60
uuid::log::Logger::info
void info(const char *format,...) const
Log a message at level Level::INFO.
Definition: log.cpp:230
uuid::log::Logger::level
Level level() const
Get the log level.
Definition: log.h:440
uuid::log::Logger::facility_
const Facility facility_
Definition: log.h:778
uuid::log::Message::Message
Message(uint64_t uptime_ms, Level level, Facility facility, const __FlashStringHelper *name, const std::string &&text)
Create a new log message (not directly useful).
Definition: log.cpp:56
uuid::log::Level
Level
Severity level of log messages.
Definition: log.h:84
uuid::log::Logger::refresh_log_level
static void refresh_log_level()
Refresh the minimum global log level across all handlers.
Definition: log.cpp:423
uuid::log::Logger::logp
void logp(Level level, const char *text) const
Log a plain message (without formatting) at the specified level.
Definition: log.cpp:390
uuid::log::Handler
Logger handler used to process log messages.
Definition: log.h:290
uuid::log::Logger::register_handler
static void register_handler(Handler *handler, Level level)
Register a log handler.
Definition: log.cpp:71
uuid::log::Logger::facility
Facility facility() const
Get the default logging facility for new messages of this logger.
Definition: log.h:430
uuid::log::Logger::debug
void debug(const char *format,...) const
Log a message at level Level::DEBUG.
Definition: log.cpp:250
uuid::log::Logger::get_log_level
static Level get_log_level(const Handler *handler)
Get the current log level of a handler.
Definition: log.cpp:96
uuid::log::Logger::global_level_
static std::atomic< Level > global_level_
Definition: log.h:772
uuid::log::Logger::vlog
void vlog(Level level, const char *format, va_list ap) const
Log a message at the specified level.
Definition: log.cpp:338
uuid::log::Logger::unregister_handler
static void unregister_handler(Handler *handler)
Unregister a log handler.
Definition: log.cpp:82
uuid::log::Logger::log
void log(Level level, const char *format,...) const
Log a message with default facility.
Definition: log.cpp:290
uuid::log::Logger::enabled
bool enabled(Level level) const
Determine if the specified log level is enabled by the effective log level.
Definition: log.h:422
uuid
Common utilities.
Definition: get_uptime_ms.cpp:28
uuid::log::Logger::warning
void warning(const char *format,...) const
Log a message at level Level::WARNING.
Definition: log.cpp:190
uuid::log::Logger::MAX_LOG_LENGTH
static constexpr size_t MAX_LOG_LENGTH
This is the maximum length of any log message.
Definition: log.h:357
uuid::log::Logger::vlog_internal
void vlog_internal(Level level, Facility facility, const char *format, va_list ap) const
Log a message at the specified level and facility without checking that the specified level is enable...
Definition: log.cpp:370
uuid::log::Logger::emerg
void emerg(const char *format,...) const
Log a message at level Level::EMERG.
Definition: log.cpp:111
uuid::log::Logger::mutex_
static std::mutex mutex_
Definition: log.h:774
uuid::log::Logger::alert
void alert(const char *format,...) const
Log a message at level Level::ALERT.
Definition: log.cpp:151
uuid::log::Logger::crit
void crit(const char *format,...) const
Log a message at level Level::CRIT.
Definition: log.cpp:131
uuid::log::Logger::registered_handlers
static std::shared_ptr< std::map< Handler *, Level > > & registered_handlers()
Get registered log handlers.
Definition: log.cpp:65