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