Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Logger.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#if qStroika_HasComponent_syslog
7#include <syslog.h>
8#endif
9
10#include "Stroika/Foundation/Cache/SynchronizedCallerStalenessCache.h"
13#include "Stroika/Foundation/Characters/SDKChar.h"
15#include "Stroika/Foundation/Containers/Collection.h"
17#include "Stroika/Foundation/Debug/Debugger.h"
19#include "Stroika/Foundation/Execution/BlockingQueue.h"
20#include "Stroika/Foundation/Execution/Common.h"
22#include "Stroika/Foundation/Execution/Process.h"
25#include "Stroika/Foundation/Execution/TimeOutException.h"
27#include "Stroika/Foundation/Streams/TextToBinary.h"
29
30#include "Logger.h"
31
32using std::byte;
33
34using namespace Stroika::Foundation;
37using namespace Stroika::Foundation::Common;
38using namespace Stroika::Foundation::Execution;
39using namespace Stroika::Foundation::Traversal;
40using namespace IO::FileSystem;
41
43using Time::Duration;
44
45// Comment this in to turn on aggressive noisy DbgTrace in this module
46//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
47
48/*
49 ********************************************************************************
50 ******************************** Execution::Logger *****************************
51 ********************************************************************************
52 */
53struct Logger::Rep_ : enable_shared_from_this<Logger::Rep_> {
54 using PriorityAndMessageType_ = pair<Logger::Priority, String>;
55 bool fBufferingEnabled_{false};
58 // @todo FIX - fOutQMaybeNeedsFlush_ setting can cause race - maybe lose this optimization - pretty harmless, but can lose a message
59 // race at end of Flush_()
60 bool fOutQMaybeNeedsFlush_{true}; // slight optimization when using buffering
61 Synchronized<optional<Duration>> fSuppressDuplicatesThreshold_;
62
63 struct LastMsgInfoType_ {
64 Time::TimePointSeconds fLastSentAt{};
65 unsigned int fRepeatCount_{};
66 };
67 Synchronized<Mapping<PriorityAndMessageType_, LastMsgInfoType_>> fLastMessages_; // if suppressing duplicates, must save all messages in timerange of suppression to compare with, and track suppression counts
68
69 Synchronized<Execution::Thread::Ptr> fBookkeepingThread_;
70 atomic<Time::DurationSeconds> fMaxWindow_{};
72
73 Rep_ () = default;
74 void FlushSuppressedDuplicates_ (bool forceEvenIfNotOutOfDate = false)
75 {
76#if USE_NOISY_TRACE_IN_THIS_MODULE_
77 Debug::TraceContextBumper ctx{"Logger::Rep_::FlushSuppressedDuplicates_"};
78#endif
79 auto lastMsgsLocked = fLastMessages_.rwget ();
80 /**
81 * @todo restructure so we dont hold the lock while we call the tmp->Log() - since that append could in principle do something calling us back/deadlock
82 * Maybe queue up the messages/and push them at the end of the loop. Advantage of no deadlock, but disavantage of
83 * there being a window where messages could get lost (error on tmp->Log - not sure we can handle that anyhow).
84 * -- LGP 2022-08-24
85 */
86 if (not lastMsgsLocked->empty ()) {
87 Time::Duration suppressDuplicatesThreshold = fSuppressDuplicatesThreshold_.cget ()->value_or (0s);
88 for (auto i = lastMsgsLocked->begin (); i != lastMsgsLocked->end ();) {
89 bool writeThisOne = forceEvenIfNotOutOfDate or i->fValue.fLastSentAt + suppressDuplicatesThreshold < Time::GetTickCount ();
90 if (writeThisOne) {
91 switch (i->fValue.fRepeatCount_) {
92 case 0:
93 break; // nothing to write
94 case 1:
95 // avoid races and critical sections (appender internally threadsafe)
96 for (shared_ptr<IAppenderRep> tmp : fAppenders_.load ()) {
97 tmp->Log (i->fKey.first, i->fKey.second);
98 }
99 break;
100 default:
101 // avoid races and critical sections (appender internally threadsafe)
102 for (shared_ptr<IAppenderRep> tmp : fAppenders_.load ()) {
103 tmp->Log (i->fKey.first, "[{} duplicates suppressed]: {}"_f(i->fValue.fRepeatCount_ - 1, i->fKey.second));
104 }
105 break;
106 }
107 lastMsgsLocked->Remove (i, &i);
108 }
109 else {
110 ++i;
111 }
112 }
113 }
114 }
115 void Flush_ ()
116 {
117#if USE_NOISY_TRACE_IN_THIS_MODULE_
118 Debug::TraceContextBumper ctx{"Logger::Rep_::Flush_"};
119#endif
120 if (not fAppenders_.cget ()->empty ()) {
121 while (true) {
122 optional<PriorityAndMessageType_> p = fOutMsgQ_.RemoveHeadIfPossible ();
123 if (p.has_value ()) {
124 // avoid races and critical sections (between check and invoke)
125 for (shared_ptr<IAppenderRep> tmp : fAppenders_.load ()) {
126 tmp->Log (p->first, p->second);
127 }
128 }
129 else {
130 return; // no more entries
131 }
132 }
133 }
134 fOutQMaybeNeedsFlush_ = false;
135 }
136 void UpdateBookkeepingThread_ ()
137 {
138 Debug::TraceContextBumper ctx{"Logger::Rep_::UpdateBookkeepingThread_"};
139 {
140 auto bktLck = fBookkeepingThread_.rwget ();
141 if (bktLck.cref () != nullptr) {
142 bktLck->AbortAndWaitForDone ();
143 bktLck.store (Thread::Ptr{});
144 }
145 }
146
147 Time::Duration suppressDuplicatesThreshold = fSuppressDuplicatesThreshold_.cget ()->value_or (0s);
148 bool suppressDuplicates = suppressDuplicatesThreshold > 0s;
149 static const String kThreadName_{"Logger Bookkeeping"sv};
150 if (suppressDuplicates or fBufferingEnabled_) {
151 Thread::Ptr newBookKeepThread;
152 shared_ptr<Rep_> useRepInThread = shared_from_this (); // capture by value the shared_ptr
153 if (suppressDuplicates) {
154 newBookKeepThread = Thread::New (
155 [suppressDuplicatesThreshold, useRepInThread] () {
156 Debug::TraceContextBumper ctx1{"Logger::Rep_::UpdateBookkeepingThread_... internal thread/1"};
157 while (true) {
158 Duration time2Wait = max<Duration> (2s, suppressDuplicatesThreshold); // never wait less than this
159 useRepInThread->FlushSuppressedDuplicates_ ();
160 if (auto p = useRepInThread->fOutMsgQ_.RemoveHeadIfPossible (time2Wait)) {
161 // avoid races and critical sections (between check and invoke)
162 for (shared_ptr<IAppenderRep> tmp : useRepInThread->fAppenders_.load ()) {
163 IgnoreExceptionsExceptThreadAbortForCall (tmp->Log (p->first, p->second));
164 }
165 }
166 }
167 },
168 kThreadName_);
169 }
170 else {
171 newBookKeepThread = Thread::New (
172 [useRepInThread] () {
173 Debug::TraceContextBumper ctx1{"Logger::Rep_::UpdateBookkeepingThread_... internal thread/2"};
174 while (true) {
175 AssertNotNull (useRepInThread);
176 auto p = useRepInThread->fOutMsgQ_.RemoveHead ();
177 // avoid races and critical sections (between check and invoke)
178 for (shared_ptr<IAppenderRep> tmp : useRepInThread->fAppenders_.load ()) {
179 tmp->Log (p.first, p.second);
180 }
181 }
182 },
183 kThreadName_);
184 }
185 newBookKeepThread.SetThreadPriority (Thread::Priority::eBelowNormal);
186 newBookKeepThread.Start ();
187 fBookkeepingThread_ = newBookKeepThread;
188 }
189
190 // manually push out pending messages
191 Flush_ ();
192 }
193};
194
195#if qStroika_Foundation_Debug_AssertionsChecked
196Logger::~Logger ()
197{
198 Assert (&sThe == this);
199 Assert (fRep_ == nullptr); // since Stroika 2.1.1, must use activator and destroy it before this is destroyed
200}
201#endif
202
203void Logger::Shutdown_ ()
204{
205 Debug::TraceContextBumper ctx{"Logger::Shutdown"};
206 // @todo FIX to assure all shutdown properly...
207 // But this is OK for now pragmatically
208#if 1
209 // see http://stroika-bugs.sophists.com/browse/STK-917
210 bool changed = false;
211 RequireNotNull (fRep_); // not yet destroyed
212 {
213 [[maybe_unused]] lock_guard critSec{fRep_->fSuppressDuplicatesThreshold_};
214 if (fRep_->fSuppressDuplicatesThreshold_.load ()) {
215 fRep_->fSuppressDuplicatesThreshold_.store (nullopt);
216 changed = true;
217 }
218 }
219 if (fRep_->fBufferingEnabled_) {
220 fRep_->fBufferingEnabled_ = false;
221 changed = true;
222 }
223 if (changed) {
224 fRep_->UpdateBookkeepingThread_ ();
225 }
226#endif
227 SetSuppressDuplicates (nullopt);
228 SetBufferingEnabled (false);
229 Flush ();
230 Ensure (fRep_->fBookkeepingThread_.load () == nullptr);
231}
232
233auto Logger::GetAppenders () const -> Traversal::Iterable<shared_ptr<IAppenderRep>>
234{
235 AssertNotNull (fRep_); // must be called while Logger::Activator exists (see Logger class description)
236 return fRep_->fAppenders_;
237}
238
239void Logger::SetAppenders (const shared_ptr<IAppenderRep>& rep)
240{
241 SetAppenders (rep == nullptr ? Collection<shared_ptr<IAppenderRep>>{} : Collection<shared_ptr<IAppenderRep>>{rep});
242}
243
244void Logger::SetAppenders (const Iterable<shared_ptr<IAppenderRep>>& reps)
245{
246 AssertNotNull (fRep_); // must be called while Logger::Activator exists
247 fRep_->fAppenders_ = Collection<shared_ptr<IAppenderRep>>{reps};
248}
249
250void Logger::AddAppender (const shared_ptr<IAppenderRep>& rep)
251{
252 RequireNotNull (rep);
253 AssertNotNull (fRep_); // must be called while Logger::Activator exists
254 fRep_->fAppenders_.rwget ()->Add (rep);
255}
256
257void Logger::Log_ (Priority logLevel, const String& msg)
258{
259 AssertNotNull (fRep_); // must be called while Logger::Activator exists
260 if (not fRep_->fAppenders_->empty ()) {
261 auto p = make_pair (logLevel, msg);
262 if (fRep_->fSuppressDuplicatesThreshold_.cget ()->has_value ()) {
263 auto lastMsgLocked = fRep_->fLastMessages_.rwget ();
264 // @todo fix performance when we fix http://stroika-bugs.sophists.com/browse/STK-928 -
265 if (auto msgInfo = lastMsgLocked->Lookup (p)) {
266 Rep_::LastMsgInfoType_ mi = *msgInfo;
267 ++mi.fRepeatCount_;
268 mi.fLastSentAt = Time::GetTickCount ();
269 lastMsgLocked->Add (p, mi); // this is the 928 - part - above - that could be Update
270 return; // so will be handled later
271 }
272 else {
273 lastMsgLocked->Add (p, Rep_::LastMsgInfoType_{Time::GetTickCount ()}); // add empty one so we can recognize this as a DUP
274 }
275 }
276 if (GetBufferingEnabled ()) {
277 fRep_->fOutQMaybeNeedsFlush_ = true;
278 fRep_->fOutMsgQ_.AddTail (p);
279 }
280 else {
281 if (fRep_->fOutQMaybeNeedsFlush_) {
282 fRep_->Flush_ (); // in case recently disabled
283 }
284 // avoid races/deadlocks and critical sections (between check and invoke)
285 for (shared_ptr<IAppenderRep> tmp : fRep_->fAppenders_.load ()) {
286 tmp->Log (p.first, p.second);
287 }
288 }
289 }
290}
291
292void Logger::SetBufferingEnabled (bool logBufferingEnabled)
293{
294 Debug::TraceContextBumper ctx{"Logger::SetBufferingEnabled", "logBufferingEnabled={}"_f, logBufferingEnabled};
295 RequireNotNull (fRep_);
296 if (fRep_->fBufferingEnabled_ != logBufferingEnabled) {
297 fRep_->fBufferingEnabled_ = logBufferingEnabled;
298 fRep_->UpdateBookkeepingThread_ ();
299 }
300}
301
303{
304 Debug::TraceContextBumper ctx{"Logger::Flush"};
305 RequireNotNull (fRep_);
306 fRep_->Flush_ ();
307}
308
310{
311 RequireNotNull (fRep_);
312 return fRep_->fBufferingEnabled_;
313}
314
315optional<Time::Duration> Logger::GetSuppressDuplicates () const
316{
317 RequireNotNull (fRep_);
318 return fRep_->fSuppressDuplicatesThreshold_.load ();
319}
320
321void Logger::SetSuppressDuplicates (const optional<Duration>& suppressDuplicatesThreshold)
322{
323 Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs ("Logger::SetSuppressDuplicates", "suppressDuplicatesThreshold={}"_f,
324 suppressDuplicatesThreshold)};
325 Require (not suppressDuplicatesThreshold.has_value () or *suppressDuplicatesThreshold > 0.0s);
326 RequireNotNull (fRep_); // not destroyed
327 [[maybe_unused]] lock_guard critSec{fRep_->fSuppressDuplicatesThreshold_};
328 if (fRep_->fSuppressDuplicatesThreshold_ != suppressDuplicatesThreshold) {
329 fRep_->fSuppressDuplicatesThreshold_ = suppressDuplicatesThreshold;
330 fRep_->UpdateBookkeepingThread_ ();
331 }
332}
333
334#if qStroika_Foundation_Debug_DefaultTracingOn
335void Logger::Log (Priority logLevel, const wchar_t* format, ...)
336{
337 va_list argsList;
338 va_start (argsList, format);
339 String msg = Characters::FormatV (format, argsList);
340 va_end (argsList);
341 DbgTrace ("Logger::Log ({}, \"{}\")"_f, logLevel, msg);
342 if (WouldLog (logLevel)) {
343 Log_ (logLevel, msg);
344 }
345 else {
346 DbgTrace ("...suppressed by WouldLog"_f);
347 }
348}
349#endif
350
351#if qStroika_HasComponent_syslog
352/*
353 ********************************************************************************
354 ************************ Execution::SysLogAppender *****************************
355 ********************************************************************************
356 */
357namespace {
358 string mkMsg_ (const String& applicationName)
359 {
360 return Characters::CString::Format ("%s[%d]", applicationName.AsNarrowSDKString (Characters::eIgnoreErrors).c_str (), GetCurrentProcessID ());
361 }
362}
363Logger::SysLogAppender::SysLogAppender (const String& applicationName)
364 : fApplicationName_{mkMsg_ (applicationName)}
365{
366 ::openlog (fApplicationName_.c_str (), 0, LOG_DAEMON); // not sure what facility to pass?
367}
368
369Logger::SysLogAppender::SysLogAppender (const String& applicationName, int facility)
370 : fApplicationName_{mkMsg_ (applicationName)}
371{
372 ::openlog (fApplicationName_.c_str (), 0, facility);
373}
374
375Logger::SysLogAppender::~SysLogAppender ()
376{
377 ::closelog ();
378}
379
380void Logger::SysLogAppender::Log (Priority logLevel, const String& message)
381{
382 DbgTrace ("SYSLOG: {}: {}"_f, DefaultNames<Logger::Priority> ().GetName (logLevel), message);
383 int sysLogLevel = LOG_NOTICE; // doesn't matter cuz assert error if hit
384 switch (logLevel) {
385 case Priority::eDebug:
386 sysLogLevel = LOG_DEBUG;
387 break;
388 case Priority::eInfo:
389 sysLogLevel = LOG_INFO;
390 break;
391 case Priority::eNotice:
392 sysLogLevel = LOG_NOTICE;
393 break;
394 case Priority::eWarning:
395 sysLogLevel = LOG_WARNING;
396 break;
397 case Priority::eError:
398 sysLogLevel = LOG_ERR;
399 break;
400 case Priority::eCriticalError:
401 sysLogLevel = LOG_CRIT;
402 break;
403 case Priority::eAlertError:
404 sysLogLevel = LOG_ALERT;
405 break;
406 case Priority::eEmergency:
407 sysLogLevel = LOG_EMERG;
408 break;
409 default:
411 }
412 // According to http://pubs.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_09.html#tag_02_09_01 this is threadsafe
413 ::syslog (sysLogLevel, "%s", message.AsNarrowSDKString (Characters::eIgnoreErrors).c_str ());
414}
415#endif
416
417/*
418 ********************************************************************************
419 ************************** Execution::StreamAppender ***************************
420 ********************************************************************************
421 */
422struct Logger::StreamAppender::Rep_ {
423public:
424 template <typename T>
425 Rep_ (const T& out)
426 : fWriter_ (Streams::TextToBinary::Writer::New (out))
427 {
428 }
429 void Log (Priority logLevel, const String& message)
430 {
431 //@todo tmphack - write date and write logLevel??? and use TextStream API that does \r or \r\n as appropriate
432 fWriter_.rwget ()->Write (
433 "[{:5}][{:16}] {}\n"_f(Common::DefaultNames<Logger::Priority>{}.GetName (logLevel), Time::DateTime::Now ().Format (), message));
434 }
435
436private:
437 Synchronized<Streams::OutputStream::Ptr<Characters::Character>> fWriter_; // All Stroika-provided appenders must be internally synchronized - http://stroika-bugs.sophists.com/browse/STK-610
438};
439
440Logger::StreamAppender::StreamAppender (const Streams::OutputStream::Ptr<byte>& out)
441 : fRep_ (make_shared<Rep_> (out))
442{
443}
444
445Logger::StreamAppender::StreamAppender (const Streams::OutputStream::Ptr<Characters::Character>& out)
446 : fRep_ (make_shared<Rep_> (out))
447{
448}
449
450void Logger::StreamAppender::Log (Priority logLevel, const String& message)
451{
452 fRep_->Log (logLevel, message);
453}
454
455/*
456 ********************************************************************************
457 ************************** Execution::FileAppender *****************************
458 ********************************************************************************
459 */
460struct Logger::FileAppender::Rep_ {
461public:
462 Rep_ (const filesystem::path& fileName, bool truncateOnOpen)
463 : fOut_ (StreamAppender (FileOutputStream::New (fileName, truncateOnOpen ? FileOutputStream::eStartFromStart : FileOutputStream::eAppend)))
464 {
465 }
466 void Log (Priority logLevel, const String& message)
467 {
468 fOut_.Log (logLevel, message);
469 }
470
471private:
472 StreamAppender fOut_; // no need to synchronize since our Logger::StreamAppender class is internally synchronized
473};
474
475Logger::FileAppender::FileAppender (const filesystem::path& fileName, bool truncateOnOpen)
476 : fRep_ (make_shared<Rep_> (fileName, truncateOnOpen))
477{
478}
479
480void Logger::FileAppender::Log (Priority logLevel, const String& message)
481{
482 fRep_->Log (logLevel, message);
483}
484
485#if qStroika_Foundation_Common_Platform_Windows
486/*
487 ********************************************************************************
488 ********************** Execution::WindowsEventLogAppender **********************
489 ********************************************************************************
490 */
491Logger::WindowsEventLogAppender::WindowsEventLogAppender (const String& eventSourceName)
492 : fEventSourceName_{eventSourceName}
493{
494}
495
496void Logger::WindowsEventLogAppender::Log (Priority logLevel, const String& message)
497{
498 /*
499 * VERY QUICK HACK - AT LEAST DUMPS SOME INFO TO EVENTLOG - BUT MUCH TWEAKING LEFT TODO
500 */
501 WORD eventType = EVENTLOG_ERROR_TYPE;
502 switch (logLevel) {
503 case Priority::eDebug:
504 eventType = EVENTLOG_INFORMATION_TYPE;
505 break;
506 case Priority::eInfo:
507 eventType = EVENTLOG_INFORMATION_TYPE;
508 break;
509 case Priority::eNotice:
510 eventType = EVENTLOG_INFORMATION_TYPE;
511 break;
512 case Priority::eWarning:
513 eventType = EVENTLOG_WARNING_TYPE;
514 break;
515 case Priority::eError:
516 eventType = EVENTLOG_ERROR_TYPE;
517 break;
518 case Priority::eAlertError:
519 eventType = EVENTLOG_ERROR_TYPE;
520 break;
521 case Priority::eEmergency:
522 eventType = EVENTLOG_ERROR_TYPE;
523 break;
524 }
525 constexpr auto CATEGORY_Normal = 0x00000001L;
526 WORD eventCategoryID = CATEGORY_Normal;
527 // See SPR#565 for weirdness - where I cannot really get these paid attention to
528 // by the Windows EventLog. So had to use the .Net eventlogger. It SEEMS
529 constexpr auto EVENT_Message = 0x00000064L;
530 const DWORD kEventID = EVENT_Message;
531 HANDLE hEventSource = ::RegisterEventSource (NULL, fEventSourceName_.AsSDKString ().c_str ());
532 Verify (hEventSource != NULL);
533 [[maybe_unused]] auto&& cleanup = Execution::Finally ([hEventSource] () noexcept { Verify (::DeregisterEventSource (hEventSource)); });
534 SDKString tmp = message.AsSDKString ();
535 const Characters::SDKChar* msg = tmp.c_str ();
536 constexpr PSID kUserSid_ = nullptr;
537 constexpr bool kWriteOldWay_ = true;
538 if constexpr (kWriteOldWay_) {
539 auto rslt = ::ReportEvent (hEventSource, eventType, eventCategoryID, kEventID, kUserSid_, (WORD)1, 0, &msg, nullptr);
540 if (rslt == 0) {
541 DbgTrace ("lasterr={}"_f, ::GetLastError ());
542 WeakAssertNotReached (); // shouldn't happen, but I saw on 2025-01-07 in WTF, so now logging more details - but fundamentally no real
543 // change here
544 }
545 }
546 else {
547 // Doing something like this appears to require using the message compiler, which is crazy; maybe a better way...
548 DWORD dataSize = static_cast<DWORD> ((tmp.size () + 1) * sizeof (SDKChar));
549 Verify (::ReportEvent (hEventSource, eventType, eventCategoryID, kEventID, kUserSid_, 0, dataSize, nullptr, (LPVOID)msg));
550 }
551}
552#endif
553
554/*
555 ********************************************************************************
556 ************************** Logger::Activator ***********************************
557 ********************************************************************************
558 */
559Logger::Activator::Activator (const Options& options)
560{
561 Debug::TraceContextBumper ctx{"Logger::Activator::Activator"};
562 Assert (sThe.fRep_ == nullptr); // only one activator object at a time
563 sThe.fRep_ = make_shared<Rep_> ();
564 sThe.SetSuppressDuplicates (options.fSuppressDuplicatesThreshold);
565 if (options.fLogBufferingEnabled) {
566 sThe.SetBufferingEnabled (*options.fLogBufferingEnabled);
567 }
568 if (options.fMinLogLevel) {
569 sThe.SetMinLogLevel (*options.fMinLogLevel);
570 }
571}
572
573Logger::Activator::~Activator ()
574{
575 Debug::TraceContextBumper ctx{"Logger::Activator::~Activator"};
576 Assert (sThe.fRep_ != nullptr); // this is the only way it gets cleared so better not be null
577 sThe.Shutdown_ (); // @todo maybe cleanup this code now that we have activator architecture?
578 sThe.fRep_.reset ();
579}
580
581/*
582 ********************************************************************************
583 ***************** Execution::DefaultLoggingFatalErrorHandler *******************
584 ********************************************************************************
585 */
587{
589 DbgTrace ("Fatal Error: {} encountered"_f, String::FromSDKString (msg));
590 Logger::sThe.Log (Logger::eCriticalError, "Fatal Error: {}; Aborting..."_f, String::FromSDKString (msg));
591 Logger::sThe.Log (Logger::eCriticalError, "Backtrace: {}"_f, Debug::BackTrace::Capture ());
592 Logger::sThe.Log (Logger::eCriticalError, "Uncaught exception: {}"_f, std::current_exception ());
594 std::_Exit (EXIT_FAILURE); // skip
595}
596
597/*
598 ********************************************************************************
599 ***************** Execution::DefaultLoggingCrashSignalHandler ******************
600 ********************************************************************************
601 */
602void Execution::DefaultLoggingCrashSignalHandler (Execution::SignalID signal) noexcept
603{
605 DbgTrace ("Fatal Signal: {} encountered"_f, Execution::SignalToName (signal));
606 Logger::sThe.Log (Logger::eCriticalError, "Fatal Signal: {}; Aborting..."_f, Execution::SignalToName (signal));
607 Logger::sThe.Log (Logger::eCriticalError, "Backtrace: {}"_f, Debug::BackTrace::Capture ());
609 std::_Exit (EXIT_FAILURE); // skip
610}
#define AssertNotNull(p)
Definition Assertions.h:333
#define WeakAssertNotReached()
Definition Assertions.h:467
#define RequireNotReached()
Definition Assertions.h:385
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
#define DbgTrace
Definition Trace.h:309
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:270
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
Definition LRUCache.h:94
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1049
static String FromSDKString(const SDKChar *from)
Definition String.inl:447
nonvirtual string AsNarrowSDKString() const
Definition String.inl:830
nonvirtual SDKString AsSDKString() const
Definition String.inl:802
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
nonvirtual optional< T > RemoveHeadIfPossible(Time::DurationSeconds timeout=0s)
nonvirtual bool WouldLog(Priority logLevel) const
Definition Logger.inl:22
nonvirtual optional< Time::Duration > GetSuppressDuplicates() const
Definition Logger.cpp:315
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
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
nonvirtual WritableReference rwget()
get a read-write smart pointer to the underlying Synchronized<> object, holding the full lock the who...
nonvirtual ReadableReference cget() const
get a read-only smart pointer to the underlying Synchronized<> object, holding the readlock the whole...
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread ob...
Definition Thread.h:334
nonvirtual void SetThreadPriority(Priority priority=Priority::eNormal) const
Definition Thread.cpp:689
OutputStream<>::Ptr is Smart pointer to a stream-based sink of data.
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
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
basic_string< SDKChar > SDKString
Definition SDKString.h:38
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
Definition Thread.cpp:955
void DefaultLoggingCrashSignalHandler(Execution::SignalID signal) noexcept
Definition Logger.cpp:602
void DefaultLoggingFatalErrorHandler(const Characters::SDKChar *msg) noexcept
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31