Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Logger.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_Logger_h_
5#define _Stroika_Foundation_Execution_Logger_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <cstdarg>
10#include <filesystem>
11
14#include "Stroika/Foundation/Common/Common.h"
17#include "Stroika/Foundation/Execution/Signals.h"
20
21/**
22 * \file
23 *
24 * TODO:
25 * @todo Finish support for Windows Event Manager Log Appender -- WindowsEventLogAppender. Its
26 * printing some data, but very minimally and wrongly handling categories etc. Probably could get close
27 * by specifying hardwired/hacked values in the CTOR args.
28 *
29 * @todo sMsgSentMaybeSuppressed_->ClearOlderThan (sMsgSentMaybeSuppressed_->Ago (sMaxWindow_.load () * kCleanupFactor_));
30 * cleanup. Find a better way (maybe this goes in cache code or here? to clear old values)
31 */
32
34
35 using namespace Common;
36
37 using Characters::String;
38
39 /**
40 * \brief A simple/portable wrapper on syslog/log4j/WindowsEventlog, with features like throttling, de-duping, varargs, etc.
41 *
42 * OVERVIEW:
43 * The point of the Logging Module is to provide a simple, portable wrapper on end-user-targetted
44 * application logging. This form of logging is the kind of logging you leave builtin to your application,
45 * and write focused on end-user readability. It is NOT (primarily) for debugging (for that -
46 * use the Stroika::Foundation::Debug::Trace module).
47 *
48 * The Logger is a singleton object. It can be set at any number of application logging
49 * levels. And it will write information to the backend logger its setup with. But default -
50 * this is none.
51 *
52 * To use the logger and actually get logging - pick a logger rep, and call
53 * SetAppender ();
54 *
55 * \note It is legal to have no appender, in which case logging is silently suppressed.
56 *
57 * This logging API CANNOT be used before main () has started, or after main () has completed
58 * (because this requires a call to setup a logger).
59 *
60 * \note Future Note
61 * Some future version MIGHT handle this through some static Common mechanism,
62 * which might then allow this (such as environment variables, linked variables etc).
63 *
64 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
65 *
66 * \par Example Usage
67 * \code
68 * // Usually near the top of 'main()'
69 * Logger::Activator loggerActivation{Logger::Options{
70 * .fLogBufferingEnabled = true,
71 * .fSuppressDuplicatesThreshold = 15s,
72 * }};
73 * if (dockerContainerFlag) {
74 * Logger::sThe.AddAppender (make_shared<Logger::StreamAppender> (FileOutputStream::New (STDOUT_FILENO, AdoptFDPolicy::eDisconnectOnDestruction)));
75 * }
76 * else {
77 * #if qStroika_HasComponent_syslog
78 * Logger::sThe.AddAppender (make_shared<Logger::SysLogAppender> ("Stroika-Sample-Service"sv));
79 * #elif qStroika_Foundation_Common_Platform_Windows
80 * Logger::sThe.AddAppender (make_shared<Logger::WindowsEventLogAppender> ("Stroika-Sample-Service"sv));
81 * #endif
82 * }
83 * \endcode
84 *
85 * \par Example Usage
86 * \code
87 * Logger::sThe.Log (Logger::eError, "Failed to correct something important in file {}"_f, fileName);
88 * \endcode
89 *
90 * @see DbgTrace
91 *
92 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
93 */
94 class Logger final {
95 public:
96 // See syslog for enumeration of various targets/etc.
97
98 public:
99 class IAppenderRep;
100
101 public:
102 using IAppenderRepPtr [[deprecated ("Since Stroika v3.0d6 - just use shared_ptr<IAppenderRep>")]] = shared_ptr<IAppenderRep>;
103
104 public:
105#if qStroika_HasComponent_syslog
106 class SysLogAppender;
107#endif
108 class FileAppender;
109 class StreamAppender;
110#if qStroika_Foundation_Common_Platform_Windows
111 class WindowsEventLogAppender;
112#endif
113
114 public:
115 /**
116 * \note - Callers who use this must arrange for Logger::Activator to be created first (and destroyed after)
117 * uses.
118 */
119 static Logger sThe;
120
121 public:
122 struct Options;
123
124 public:
125 struct Activator;
126
127 public:
128 Logger (const Logger&) = delete;
129 Logger (Logger&&) = delete;
130
131 private:
132 Logger () = default;
133
134 public:
135 Logger& operator= (const Logger&) = delete;
136 Logger& operator= (Logger&&) = delete;
137
138#if qStroika_Foundation_Debug_AssertionsChecked
139 private:
140 ~Logger ();
141#endif
142
143 public:
144 [[deprecated ("Since Stroika v3.0d6 use GetAppenders - plural")]] nonvirtual shared_ptr<IAppenderRep> GetAppender () const
145 {
146 return GetAppenders ().FirstValue (nullptr);
147 }
148
149 public:
150 [[deprecated ("Since Stroika v3.0d6 use SetAppenders - plural")]] nonvirtual void SetAppender (const shared_ptr<IAppenderRep>& rep)
151 {
152 SetAppenders (rep);
153 }
154
155 public:
156 /**
157 * Note - all Stroika provided appenders are internally synchronized.
158 */
160
161 public:
162 /**
163 * Note - all Stroika provided appenders are internally synchronized.
164 *
165 * However, user-defined appenders are assumed internally synchronized (threadsafe).
166 *
167 * \note require all appenders != nullptr, but if a single rep given, that can be nullptr (and interpreted as removing all).
168 */
169 nonvirtual void SetAppenders (const shared_ptr<IAppenderRep>& rep);
170 nonvirtual void SetAppenders (const Traversal::Iterable<shared_ptr<IAppenderRep>>& appenders);
171
172 public:
173 /**
174 * As if calls GetAppenders/SetAppenders
175 */
176 nonvirtual void AddAppender (const shared_ptr<IAppenderRep>& rep);
177
178 public:
179 /**
180 *
181 * Names are based on syslog from http://unix.superglobalmegacorp.com/Net2/newsrc/sys/syslog.h.html
182 * But the NUMBERS do NOT correspond!
183 *
184 * Lower numbers are less interesting (debug) and higher numbers more important (higher priority).
185 *
186 * \note Common::DefaultNames<> supported
187 */
188 enum class Priority : uint8_t {
189 eDebug = 0, // The message is debug info (not a good use of syslog - consider using DbgTrace)
190 eInfo = 1, // The message is purely informational
191 eNotice = 2, // The message describes a normal but important event
192 eWarning = 3, // The message is a warning
193 eError = 4, // The message describes an error
194 eCriticalError = 5, // The message states a critical condition
195 eAlertError = 6, // Action on the message must be taken immediately
196 eEmergency = 7, // The message says the system is unusable
197
198 Stroika_Define_Enum_Bounds (eDebug, eEmergency)
199 };
200
201 public:
202 static constexpr Priority eDebug = Priority::eDebug;
203 static constexpr Priority eInfo = Priority::eInfo;
204 static constexpr Priority eNotice = Priority::eNotice;
205 static constexpr Priority eWarning = Priority::eWarning;
206 static constexpr Priority eCriticalError = Priority::eCriticalError;
207 static constexpr Priority eError = Priority::eError;
208 static constexpr Priority eAlertError = Priority::eAlertError;
209 static constexpr Priority eEmergency = Priority::eEmergency;
210
211 private:
212 nonvirtual void Shutdown_ ();
213
214 public:
215 /**
216 * This defaults to eInfo. Messages of lower priority (e.g. eDebug) will not be logged to the underlying log
217 * system.
218 *
219 * This is done inline, so in principle, the compiler may eliminate the calls.
220 *
221 * The MIN is min inclusive.
222 */
223 nonvirtual Priority GetMinLogLevel () const;
224
225 public:
226 /**
227 * @see GetMinLogLevel
228 */
229 nonvirtual void SetMinLogLevel (Priority minLogLevel);
230
231 public:
232 /**
233 * @see GetMinLogLevel
234 * This determines if a call to Log() with this argument log-level would
235 * write anything.
236 */
237 nonvirtual bool WouldLog (Priority logLevel) const;
238
239 public:
240 /**
241 * Log buffering is DISABLED by default, since it has some cost. But if enabled, Log () calls
242 * queue an internal message, which another thread wakes up to write. This CAN be critical for performance
243 * reasons, so the caller can freely log things, and not have their thread blocked.
244 *
245 * Beware, this feature CAN mean that something you log, wont make it out of the application if
246 * the application terminates before the log can be flushed.
247 *
248 * \par Example Usage
249 * \code
250 * Log ("QUITTING");
251 * _exit (0);
252 * \endcode
253 *
254 * probably won't get logged. To avoid this issue, call myLogger.Shutdown () (@see ShutdownSingleton)
255 * before quitting, or call Flush ();
256 *
257 * In one application (open-embedded arm linux) I saw a 3ms latency before I added this (2014-05-30).
258 */
259 nonvirtual bool GetBufferingEnabled () const;
260
261 public:
262 /**
263 * @see GetBufferingEnabled ()
264 */
265 nonvirtual void SetBufferingEnabled (bool logBufferingEnabled);
266
267 public:
268 /**
269 * @see GetBufferingEnabled ()
270 *
271 * This has no effect if the buffer is empty or buffering is disabled.
272 */
273 nonvirtual void Flush ();
274
275 public:
276 /**
277 * If GetSuppressDuplicates ().has_value (), then a sequence of N (N > 1) identical messages will be replaced with two messages,
278 * the second of which appears with the added label [N-2 suppressed].
279 *
280 * The duration is the window of time after the last message we wait before emitting the
281 * last message. A good default for this might be 5 or 10 seconds.
282 */
283 nonvirtual optional<Time::Duration> GetSuppressDuplicates () const;
284
285 public:
286 /**
287 * @see GetSuppressDuplicates ()
288 * @see Options::fSuppressDuplicatesThreshold
289 *
290 * \par Example Usage
291 * \code
292 * Logger::sThe.SetSuppressDuplicates (nullopt); // disable the feature
293 * Logger::sThe.SetSuppressDuplicates ("PT5M"_duration); // anything within 5 minutes suppress
294 * Logger::sThe.SetSuppressDuplicates (5min); // ""
295 * \endcode
296 */
297 nonvirtual void SetSuppressDuplicates (const optional<Time::Duration>& suppressDuplicatesThreshold);
298
299 public:
300 /**
301 * Log
302 *
303 * Design Note:
304 * The 'format' parameter must be defined a String (or const wchar_t*), but
305 * not const String&, because:
306 *
307 * 18.10 Other runtime support [support.runtime]
308 * ...
309 * 3 The restrictions that ISO C places on the second parameter to the va_start()
310 * macro in header <stdarg.h> are different in this International Standard.
311 * The parameter parmN is the identifier of the rightmost parameter in the variable parameter
312 * list of the function definition (the one just before the ...). If the parameter
313 * parmN is declared with a function, array, or reference type, or with a type that is
314 * not compatible with the type that results when passing an argument for which there
315 * is no parameter, the behavior is undefined.
316 *
317 * \par Example Usage
318 * \code
319 * Logger::sThe.Log (Logger::eError, "Failed to correct something important in file {}"_f, fileName);
320 * \endcode
321 */
322 [[deprecated ("Since Stroika v3.0d6 - use _f strings for Logging")]] void Log (Priority logLevel, const wchar_t* format, ...);
323 template <typename CHAR_T, typename... ARGS>
324 nonvirtual void Log (Priority logLevel, const Characters::FormatString<CHAR_T>& fmt, ARGS&&... args);
325
326 private:
327 nonvirtual void Log_ (Priority logLevel, const String& msg);
328
329 private:
330 struct Rep_;
331
332 private:
333 shared_ptr<Rep_> fRep_{nullptr}; // unsure if we want to use shared_ptr or unique_ptr but shared among threads so easiest that way
334 Priority fMinLogLevel_{Priority::eInfo}; // Keep out of rep only so we can reference from inlines and put the Rep_ in the .cpp file for better hiding
335 };
336
337 /**
338 */
339 struct Logger::Options {
340 optional<bool> fLogBufferingEnabled;
341 optional<Time::Duration> fSuppressDuplicatesThreshold;
342 optional<Priority> fMinLogLevel;
343 };
344
345 /**
346 * At most one such object may exist. When it does, Logger::sThe is active and usable.
347 * Its illegal to make calls on Logger::sThe when there is no Activator active.
348 *
349 * \par Example Usage
350 * \code
351 * main () {
352 * ...
353 * // near the beginning, before Logger::sThe used
354 * Execution::Logger::Activator logMgrActivator;
355 * \endcode
356 */
358 Activator (const Options& options = {});
359 Activator (const Activator&) = delete;
360 ~Activator ();
361 };
362
363 /**
364 */
365 class Logger::IAppenderRep {
366 public:
367 virtual ~IAppenderRep () = default;
368
369 public:
370 virtual void Log (Priority logLevel, const String& message) = 0;
371 };
372
373#if qStroika_HasComponent_syslog
374 /**
375 */
376 class Logger::SysLogAppender : public Logger::IAppenderRep {
377 public:
378 SysLogAppender (const String& applicationName);
379 SysLogAppender (const String& applicationName, int facility);
380 ~SysLogAppender ();
381
382 public:
383 virtual void Log (Priority logLevel, const String& message) override;
384
385 private:
386 string fApplicationName_; // must save like this because open-syslog appears to KEEP ahold of pointer (http://www.gnu.org/s/hello/manual/libc/openlog.html)
387 };
388#endif
389
390 /**
391 */
392 class Logger::StreamAppender : public Logger::IAppenderRep {
393 public:
394 StreamAppender (const Streams::OutputStream::Ptr<byte>& out);
395 StreamAppender (const Streams::OutputStream::Ptr<Characters::Character>& out);
396
397 public:
398 virtual void Log (Priority logLevel, const String& message) override;
399
400 private:
401 struct Rep_;
402 shared_ptr<Rep_> fRep_;
403 };
404
405 /**
406 */
407 class Logger::FileAppender : public Logger::IAppenderRep {
408 public:
409 FileAppender (const filesystem::path& fileName, bool truncateOnOpen = true);
410
411 public:
412 virtual void Log (Priority logLevel, const String& message) override;
413
414 private:
415 struct Rep_;
416 shared_ptr<Rep_> fRep_;
417 };
418
419#if qStroika_Foundation_Common_Platform_Windows
420 /**
421 */
422 class Logger::WindowsEventLogAppender : public Logger::IAppenderRep {
423 public:
424 WindowsEventLogAppender (const String& eventSourceName);
425
426 public:
427 virtual void Log (Priority logLevel, const String& message) override;
428
429 private:
430 String fEventSourceName_;
431 };
432#endif
433
434 /**
435 * Sensible argument to RegisterDefaultFatalErrorHandlers, if using Logger (but beware - caller must assure Logger::Activate called appropriately)
436 */
437 void DefaultLoggingFatalErrorHandler ([[maybe_unused]] const Characters::SDKChar* msg) noexcept;
438
439 /**
440 * Sensible argument to SignalHandlerRegistry::Get ().SetStandardCrashHandlerSignals(...), if using Logger (but beware - caller must assure Logger::Activate called appropriately)
441 */
442 void DefaultLoggingCrashSignalHandler (Execution::SignalID signal) noexcept;
443
444}
445
446/*
447 ********************************************************************************
448 ***************************** Implementation Details ***************************
449 ********************************************************************************
450 */
451#include "Logger.inl"
452
453#endif /*_Stroika_Foundation_Execution_Logger_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A simple/portable wrapper on syslog/log4j/WindowsEventlog, with features like throttling,...
Definition Logger.h:94
nonvirtual bool WouldLog(Priority logLevel) const
Definition Logger.inl:22
nonvirtual optional< Time::Duration > GetSuppressDuplicates() const
Definition Logger.cpp:315
nonvirtual Priority GetMinLogLevel() const
Definition Logger.inl:14
nonvirtual void SetAppenders(const shared_ptr< IAppenderRep > &rep)
Definition Logger.cpp:239
void Log(Priority logLevel, const wchar_t *format,...)
Definition Logger.inl:27
nonvirtual void SetMinLogLevel(Priority minLogLevel)
Definition Logger.inl:18
nonvirtual Traversal::Iterable< shared_ptr< IAppenderRep > > GetAppenders() const
Definition Logger.cpp:233
nonvirtual void SetSuppressDuplicates(const optional< Time::Duration > &suppressDuplicatesThreshold)
Definition Logger.cpp:321
nonvirtual bool GetBufferingEnabled() const
Definition Logger.cpp:309
nonvirtual void AddAppender(const shared_ptr< IAppenderRep > &rep)
Definition Logger.cpp:250
nonvirtual void SetBufferingEnabled(bool logBufferingEnabled)
Definition Logger.cpp:292
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
conditional_t< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar
Definition SDKChar.h:71
void DefaultLoggingCrashSignalHandler(Execution::SignalID signal) noexcept
Definition Logger.cpp:602
void DefaultLoggingFatalErrorHandler(const Characters::SDKChar *msg) noexcept
Roughly equivalent to std::wformat_string, except that it can be constructed from 'char' string,...