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