ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
loxpimpl.cpp
1// #################################################################################################
2// alib::lox - ALox Logging Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14// ====================================== Global Fragment ======================================
19// =========================================== Module ==========================================
20#if ALIB_C20_MODULES
21 module ALib.ALox.Impl;
22 import ALib.ALox;
23 import ALib.Lang;
24 import ALib.EnumOps;
25 import ALib.Strings;
27 import ALib.Boxing;
28 import ALib.EnumRecords;
30 import ALib.Variables;
31 import ALib.Camp;
32 import ALib.Camp.Base;
33 import ALib.Bootstrap;
34#else
35# include "ALib.Bootstrap.H"
36# include "ALib.Lang.H"
37# include "ALib.Strings.H"
39# include "ALib.Boxing.H"
41# include "ALib.Variables.H"
42# include "ALib.Camp.H"
43# include "ALib.Camp.Base.H"
44# include "ALib.ALox.H"
45# include "ALib.ALox.Impl.H"
46#endif
47// ====================================== Implementation =======================================
48#if !ALIB_SINGLE_THREADED
49# define UNDEFINED_THREAD threads::UNDEFINED
50#else
51# define UNDEFINED_THREAD 0
52#endif
53
55 ALIB_COMMA alib::variables::Priority>, vt_lox_pair_verby_prio )
56
58
59# include "ALib.Lang.CIFunctions.H"
60
61namespace alib { namespace lox { namespace detail {
62
63/// Domain substitution rules.
64struct DomainSubstitutionRule
65{
66 /// Rule types.
67 public: enum class Type
68 {
69 Exact, ///< Exact match.
70 StartsWith, ///< Starts with match.
71 EndsWith, ///< Ends with match.
72 Substring ///< Any substring.
73 };
74
75 Type type; ///< Denotes the type of the rule, depending of what
76 ///< was set in originally as search path
77 NString32 Search; ///< The path to search.
78 NString32 Replacement; ///< The replacement.
79
80 /// Constructor.
81 /// @param s The path to search.
82 /// @param r The replacement.
83 DomainSubstitutionRule( const NString& s, const NString& r )
84 {
85 Search .DbgDisableBufferReplacementWarning();
86 Replacement.DbgDisableBufferReplacementWarning();
87
88 // get type and adjust given search parameter
89 integer startPos= 0;
90 integer length= s.Length();
91 if ( s.CharAtStart() == '*' )
92 {
93 ++startPos;
94 --length;
95 if ( s.CharAtEnd() == '*' )
96 {
97 type= Type::Substring;
98 --length;
99 }
100 else
101 type= Type::EndsWith;
102 }
103 else
104 {
105 if ( s.CharAtEnd() == '*' )
106 {
107 type= Type::StartsWith;
108 --length;
109 }
110 else
111 type= Type::Exact;
112 }
113 Search._( s, startPos, length );
114
115 // minimum rule check
116 if ( ( ( type == Type::Exact
117 || type == Type::StartsWith )
118 && Search.CharAtStart() != '/'
119 )
120 || ( type == Type::EndsWith
121 && Search.CharAtEnd() == '/'
122 )
123 )
124 Search.Reset(); // illegal rule
125
126
127 Replacement= r;
128 }
129}; // struct DomainSubstitutionRule
130
131
132//==================================================================================================
133/// Implementation struct for class \alib{lox;Lox} following the
134/// \https{Pimpl Idiom,en.cppreference.com/w/cpp/language/pimpl}.
135//==================================================================================================
136struct LoxImpl
137{
138 /// The self contained monotonic allocator, that also contains this struct itself.
139 MonoAllocator& monoAllocator;
140
141 /// A pool allocator that uses #monoAllocator as its source.
142 PoolAllocator poolAllocator;
143
144 /// Snapshot taken before embedding the lox in the #monoAllocator.
145 monomem::Snapshot beforeLox;
146
147 #if !ALIB_SINGLE_THREADED
148 /// A mutex to control parallel access.
149 threads::RecursiveLock Lock;
150 #endif
151
152 /// Counts the number of nested (recursive) acquirements.
153 int AcquirementsCount;
154
155 /// A counter for the quantity of calls. The count includes logs that were suppressed by
156 /// disabled <em>Log Domain</em> and those suppressed by the optional log condition parameter.
157 integer CntLogCalls =0;
158
159 /// A list of a list of logables used for (recursive) logging.
160 StdVectorMono<BoxesMA*> logableContainers;
161
162 /// A list of a list of logables used for (recursive) internal logging.
163 StdVectorMono<BoxesMA*> internalLogables;
164
165 /// The recursion counter for internal logging.
166 integer internalLogRecursionCounter = 0;
167
168 /// Information about the source code, method, thread, etc. invoking a log call
169 ScopeInfo scopeInfo;
170
171 /// The root domain \"/\". All registered domains become a sub domain of this root.
172 /// If a <em>Sub-Log Domain's Verbosity</em> is not explicitly set, such sub domain inherits
173 /// the verbosity of its parent.
174 Domain* domains;
175
176 /// The root domain for internal <em>Log Domains</em>.
177 Domain* internalDomains;
178
179 /// Scope Domains
180 ScopeStore<NString , true > scopeDomains;
181
182 /// Prefix logables store
183 ScopeStore<PrefixLogable*, true > scopePrefixes;
184
185 /// Log once counters
186 ScopeStore<SSMap<int>* , false> scopeLogOnce;
187
188 /// Log data store
189 ScopeStore<SSMap<Box>* , false> scopeLogData;
190
191 /// Used for tabular output of logger lists
192 integer maxLoggerNameLength =0;
193
194 /// Used for tabular output of logger lists
195 integer maxDomainPathLength;
196
197 /// A key value used in stores if no key is given (global object).
198 const NString noKeyHashKey = "$";
199
200
201 /// The list of domain substitution rules.
203 DomainSubstitutionRule> domainSubstitutions;
204
205 /// Flag if a warning on circular rule detection was logged.
206 bool oneTimeWarningCircularDS =false;
207
208 /// Flag used with configuration variable LOXNAME_DUMP_STATE_ON_EXIT.
209 bool loggerAddedSinceLastDebugState =false;
210
211 /// Constructor.
212 /// @param ma The externally created, self-contained monotonic allocator, that also contains
213 /// this instance.
214 /// @param name The lox's name.
215 LoxImpl( MonoAllocator* ma, const NString& name )
216 : monoAllocator ( *ma )
217 , poolAllocator ( monoAllocator )
218 , AcquirementsCount ( 0 )
219 , logableContainers ( monoAllocator )
220 , internalLogables ( monoAllocator )
221 , scopeInfo ( name , monoAllocator )
222 , scopeDomains ( scopeInfo, monoAllocator )
223 , scopePrefixes ( scopeInfo, monoAllocator )
224 , scopeLogOnce ( scopeInfo, monoAllocator )
225 , scopeLogData ( scopeInfo, monoAllocator )
226 , domainSubstitutions( monoAllocator )
227 {
229 LI::init(this);
230 }
231
232 /// Destructor.
233 ~LoxImpl()
234 {
235 // unregister each logger in std domains and remove it in internals
236 for ( int i= domains->CountLoggers() - 1 ; i >= 0 ; --i )
237 {
238 Logger* logger= domains->GetLogger( i );
239 int ii= internalDomains->GetLoggerNo( logger );
240 if ( ii >= 0 )
241 internalDomains->RemoveLogger( ii );
242 logger->AcknowledgeLox( this, lang::ContainerOp::Remove );
243 }
244
245 // unregister remaining loggers in internal domains
246 for ( int i= internalDomains->CountLoggers() - 1 ; i >= 0 ; --i )
247 {
248 Logger* logger= internalDomains->GetLogger( i );
249 logger->AcknowledgeLox( this, lang::ContainerOp::Remove );
250 }
251 }
252
253 /// Returns the number of (recursive) acquirements of this \b Lox.
254 /// If greater than \c 1, this is either recursive logging or a user has explicitly
255 /// acquired this lox repeatedly (which is not recommended to do).
256 ///
257 /// @return The number of acquirements.
258 int CountAcquirements() const noexcept { return AcquirementsCount; }
259
260 /// Shortcut to allocate arbitrary objects in #poolAllocator.
261 /// @tparam T The type to allocate.
262 /// @tparam TArgs Types of variadic parameters given with parameter \p{args}.
263 /// Deduced by the compiler.
264 /// @param args Variadic parameters to be forwarded to the constructor of type \p{T}.
265 /// @return The allocated object.
266 template<typename T, typename... TArgs>
267 T* newPO(TArgs&&... args) { return poolAllocator().New<T>(std::forward<TArgs>(args)...); }
268
269 /// Shortcut to delete arbitrary objects in #poolAllocator.
270 /// @tparam T The type to allocate. Deduced by the compiler.
271 /// @param o The allocated object.
272 template<typename T>
273 void deletePO(T* o) { poolAllocator().Delete<T>(o); }
274
275}; // struct LoxImpl
276
277
278#define ASSERT_ACQUIRED ALIB_ASSERT_ERROR( impl->CountAcquirements() >0,"ALOX","Lox not acquired" )
279
280// #################################################################################################
281// Constructors/destructor
282// #################################################################################################
283LoxImpl* LI::Construct(const NString& name)
284{
285 lang::HeapAllocator ha;
286 MonoAllocator* selfContainedMA= MonoAllocator::Create( ALIB_DBG(nullptr,) ha, 8* 1024 );
287 ALIB_DBG( selfContainedMA->DbgName= NCString(*selfContainedMA, NString128("Lox") << name).Buffer(); )
288 auto snapShot= selfContainedMA->TakeSnapshot();
289 LoxImpl* result= (*selfContainedMA)().New<LoxImpl>( selfContainedMA, name );
290 result->beforeLox= snapShot;
291 return result;
292}
293
294void LI::Destruct( LoxImpl* impl )
295{
296 auto& ma= impl->monoAllocator;
297 impl->~LoxImpl();
298 lang::Destruct(ma); // just destruct, as this is self-contained
299}
300
301const NString& LI::GetName(LoxImpl* impl)
302{
303 return impl->scopeInfo.loxName;
304}
305
306integer& LI::GetLogCounter(LoxImpl* impl)
307{
308 return impl->CntLogCalls;
309}
310
311#if !ALIB_SINGLE_THREADED
312RecursiveLock& LI::getLock(LoxImpl* impl)
313{
314 ALIB_DBG( assert::SingleThreaded(); )
315 return impl->Lock;
316}
317#endif
318
319void LI::Acquire(LoxImpl* impl, const lang::CallerInfo& ci )
320{
321 #if !ALIB_SINGLE_THREADED
322 ALIB_REL_DBG( impl->Lock.AcquireRecursive();
323 , impl->Lock.AcquireRecursive(ci); )
324 #else
325 ALIB_DBG( assert::SingleThreaded());
326 #endif
327 ++impl->AcquirementsCount;
328 impl->scopeInfo.Set( ci );
329}
330
331void LI::Release(LoxImpl* impl)
332{
333 impl->scopeInfo.PopNestedScope();
334 --impl->AcquirementsCount;
335 #if !ALIB_SINGLE_THREADED
336 impl->Lock.ReleaseRecursive(ALIB_CALLER_PRUNED);
337 #endif
338}
339
340void LI::init(LoxImpl* impl)
341{
342 impl->logableContainers.reserve(5); // 5 equals the recursive logging warning threshold
343
344 // create domain trees
345 impl->domains = impl->monoAllocator().New<Domain>(impl->monoAllocator, impl->poolAllocator, "" );
346 impl->internalDomains= impl->monoAllocator().New<Domain>(impl->monoAllocator, impl->poolAllocator, "$" );
347
348 // create internal Subdomains
349 const NString internalDomainList[]= {"LGR","DMN", "PFX", "THR", "LGD", "VAR" };
350 for ( auto& it : internalDomainList )
351 impl->internalDomains->Find( it, 1, nullptr );
352 impl->maxDomainPathLength= Lox::InternalDomains.Length() + 3;
353
354 // read domain substitution rules from configuration
355 Variable var= variables::CampVariable(ALOX);
356 {
358 var.Declare( Variables::DOMAIN_SUBSTITUTION,
359 #if !ALIB_CHARACTERS_WIDE
360 GetName(impl)
361 #else
362 String128( GetName( impl ) )
363 #endif
364 );
365 }
366 if ( var.IsDefined() )
367 {
368 for( int ruleNo= 0 ; ruleNo < var.Size() ; ++ruleNo )
369 {
370 Substring rule= var.GetString(ruleNo);
371 if( rule.Trim().IsEmpty() )
372 continue;
373
374 integer idx= rule.IndexOf( A_CHAR("->") );
375 if ( idx > 0 )
376 {
377 NString64 domainPath ( rule.Substring<NC>( 0, idx ) ); domainPath .Trim();
378 NString64 replacement( rule.Substring<NC>( idx + 2, rule.Length() - idx - 2 ) ); replacement.Trim();
379 SetDomainSubstitutionRule( impl, domainPath, replacement );
380 }
381 else
382 {
383 // using alib warning here as we can't do internal logging in the constructor
384 ALIB_WARNING( "ALOX", "Syntax error in variable \"{}\".", var )
385 }
386 }
387 }
388
389}
390
391void LI::Reset(LoxImpl* impl)
392{
393 #if ALOX_DBG_LOG
394 if( impl == Log::Get()->impl && Log::DebugLogger != nullptr )
395 {
397 Log::RemoveDebugLogger( Log::Get() );
399 }
400 #endif
401
402
403 SetSourcePathTrimRule( impl, nullptr, lang::Inclusion::Include,
404 999999, // code for clearing
405 lang::Case::Ignore, NULL_NSTRING, lang::Reach::Global, Priority::NONE );
406
407 // clear the monotonic allocator and rebuild the impl
408 MonoAllocator* loxMA = &impl->monoAllocator;
409 auto snapshot = impl->beforeLox;
410 NString128 name( impl->scopeInfo.loxName);
411 (*loxMA)().Delete(impl);
412 loxMA->Reset(snapshot);
413 ALIB_DBG( auto* dbgNewAddress= )
414 (*loxMA)().New<LoxImpl>(loxMA, name ); // creates the impl at the same position, therefore, it does not need
415 // to be stored (and passed back)
416 ALIB_ASSERT_ERROR( impl == dbgNewAddress, "ALOX", "Internal error. This must never happen." )
417 impl->beforeLox= snapshot;
418}
419
420void LI::SetFileNameCacheCapacity( LoxImpl* impl, integer numberOfLists, integer entriesPerList )
421{
422 impl->scopeInfo.SetFileNameCacheCapacity( numberOfLists, entriesPerList );
423}
424
425#if ALIB_DEBUG_MEMORY
426MonoAllocator& LI::DbgGetMonoAllocator( LoxImpl* impl ) { return impl->monoAllocator; }
427#endif
428
429void LI::SetSourcePathTrimRule( LoxImpl* impl,
430 const NCString& path,
431 lang::Inclusion includeString ,
432 int trimOffset ,
433 lang::Case sensitivity ,
434 const NString& trimReplacement,
435 lang::Reach reach ,
436 Priority priority )
437
438{
439 impl->scopeInfo.SetSourcePathTrimRule( path, includeString, trimOffset, sensitivity,
440 trimReplacement, reach, priority );
441}
442
443void LI::SetDomain( LoxImpl* impl, const NString& scopeDomain, Scope scope, threads::Thread* thread )
444{
445 if ( !isThreadRelatedScope( impl, scope ) )
446 return;
447 setDomain( impl, scopeDomain, scope, false, thread );
448}
449
450// #################################################################################################
451// Methods
452// #################################################################################################
453Logger* LI::GetLogger(LoxImpl* impl, const NString& loggerName )
454{
455 ASSERT_ACQUIRED
456
457 // search logger
458 Logger* logger;
459 if ( (logger= impl->domains ->GetLogger( loggerName ) ) != nullptr ) return logger;
460 if ( (logger= impl->internalDomains->GetLogger( loggerName ) ) != nullptr ) return logger;
461
462 // not found
463 BoxesMA& logables= acquireInternalLogables(impl);
464 logables.Add( "No logger named {!Q} found.", loggerName );
465 logInternal( impl, Verbosity::Warning, "LGR", logables );
466 return nullptr;
467}
468
469//! @cond NO_DOX
470namespace {
471void writeVerbVarRecursive( Domain& domain, int loggerNo, CVVerbosities& verbosities,
472 Verbosity parentVerbosity )
473{
474 auto verbosity= domain.GetVerbosity( loggerNo );
475 if( parentVerbosity != verbosity || verbosities.ExportAll )
476 verbosities.Add(String256(domain.FullPath) << '=' << verbosity );
477
478 // loop over all subdomains (recursion)
479 for ( Domain& subDomain : domain.SubDomains )
480 writeVerbVarRecursive( subDomain, loggerNo, verbosities, verbosity );
481}
482} // anonymous namespace
483//! @endcond
484
485void LI::writeVerbositiesOnLoggerRemoval( LoxImpl* impl, Logger* logger )
486{
487DOX_MARKER([DOX_VARIABLES_REPLACEMENTS2])
488Variable var= variables::CampVariable(ALOX);
490 // a local array of boxes of size two, to fill variable placeholders
491 Box replacements[2]=
492 {
493 GetName( impl ), // name of this Lox
494 logger->GetName() // name of the Logger
495 };
496
497 // declare the individually named variable
498 var.Declare( Variables::VERBOSITY, replacements );
499}
500DOX_MARKER( [DOX_VARIABLES_REPLACEMENTS2])
501 // we do not care about the writing rights.
502 (void) var.Define();
503 auto& cvVerb= var.Get<CVVerbosities>();
504 cvVerb.Clear();
505
506 // collect verbosities
507 {
508 int loggerNoMainDom= impl->domains ->GetLoggerNo( logger );
509 int loggerNoIntDom= impl->internalDomains->GetLoggerNo( logger );
510
511 if ( loggerNoMainDom >= 0 ) writeVerbVarRecursive( *impl->domains , loggerNoMainDom, cvVerb, Verbosity(-1) );
512 if ( loggerNoIntDom >= 0 ) writeVerbVarRecursive( *impl->internalDomains, loggerNoIntDom , cvVerb, Verbosity(-1) );
513 }
514
515 // internal logging
516 {
517 // get variable name. Needs shared acquisition
518 String256 varName;
520 varName << var; // this is needed because we are logging the name of a variable!
521 }
522 BoxesMA& logables= acquireInternalLogables(impl);
523 logables.Add( "Verbosities for logger {!Q} written to variable {!Q}",
524 logger->GetName(), varName );
525 logInternal( impl, Verbosity::Info, "VAR", logables );
526 }
527
528 // verbose logging of the value written
529 {
530 BoxesMA& logables= acquireInternalLogables(impl);
531 logables.Add(" Value:");
532 for( auto& it : cvVerb )
533 logables.Add( "\n ", it );
534 logInternal( impl, Verbosity::Verbose, "VAR", logables );
535 }
536}
537
538void LI::dumpStateOnLoggerRemoval(LoxImpl* impl)
539{
540 if( !impl->loggerAddedSinceLastDebugState )
541 return;
542 impl->loggerAddedSinceLastDebugState= false;
543
544
545 Variable variable= variables::CampVariable(ALOX, Variables::DUMP_STATE_ON_EXIT,
547 GetName( impl )
548 #else
549 String128( GetName( impl ) )
550 #endif
551 );
552 if( !variable.IsDefined() )
553 return;
554 NString64 domain;
555 Verbosity verbosity= Verbosity::Info;
556 Substring tok;
557 bool error= false;
558 StateInfo flags= StateInfo::NONE;
559 Tokenizer tknzr;
560 tknzr.Set(variable, ',', true);
561 while( tknzr.HasNext() )
562 {
563 tok= tknzr.Next();
564
565 // read log domain and verbosity
566 if( tok.IndexOf( '=' ) > 0 )
567 {
568 if( tok.ConsumePartOf<lang::Case::Ignore, lang::Whitespaces::Trim>( A_CHAR("verbosity"), 1) )
569 {
570 if( tok.ConsumeChar<lang::Case::Sensitive, lang::Whitespaces::Trim>( '=' ) )
571 enumrecords::Parse<Verbosity>( tok, verbosity );
572 continue;
573 }
574 if( tok.ConsumePartOf<lang::Case::Ignore, lang::Whitespaces::Trim>( A_CHAR("domain"), 1) )
575 {
576 if( tok.ConsumeChar<lang::Case::Sensitive, lang::Whitespaces::Trim>( '=' ) )
577 domain= tok.Trim();
578 continue;
579 }
580 error= true;
581 break;
582 }
583
584 // read and add state
585 StateInfo stateInfo;
586 if( !enumrecords::Parse<StateInfo>( tok, stateInfo ) )
587 {
588 error= true;
589 break;
590 }
591
592 // None clears all, others are added
593 if( stateInfo == StateInfo::NONE )
594 flags= StateInfo::NONE;
595 else
596 flags|= stateInfo;
597 }
598 if( error )
599 {
600 BoxesMA& logables= acquireInternalLogables(impl);
601 logables.Add( "Unknown argument {!Q} in variable {} = {!Q}.",
602 tok, variable, variable.GetString() );
603 logInternal( impl, Verbosity::Error, "VAR", logables);
604 }
605
606 if ( flags != StateInfo::NONE )
607 {
608 State( impl, domain, verbosity, A_CHAR("Auto dump state on exit requested: "), flags );
609 }
610}
611
612
613bool LI::RemoveLogger( LoxImpl* impl, Logger* logger )
614{
615 ASSERT_ACQUIRED
616
617 int noMainDom= impl->domains ->GetLoggerNo( logger );
618 int noIntDom= impl->internalDomains->GetLoggerNo( logger );
619
620 if( noMainDom >= 0 || noIntDom >= 0 )
621 {
622 dumpStateOnLoggerRemoval(impl);
623 writeVerbositiesOnLoggerRemoval( impl, logger );
624
625 if( noMainDom >= 0 )
626 impl->domains->RemoveLogger( noMainDom );
627
628 if( noIntDom >= 0 )
629 impl->internalDomains->RemoveLogger( noIntDom );
630
631 logger->AcknowledgeLox( impl, lang::ContainerOp::Remove );
632
633 return true;
634 }
635
636 // not found
637 BoxesMA& logables= acquireInternalLogables(impl);
638 logables.Add( "Logger {!Q} not found. Nothing removed.", logger );
639 logInternal( impl, Verbosity::Warning, "LGR", logables );
640 return false;
641}
642
643Logger* LI::RemoveLogger(LoxImpl* impl, const NString& loggerName )
644{
645 ASSERT_ACQUIRED
646
647 int noMainDom= impl->domains ->GetLoggerNo( loggerName );
648 int noIntDom= impl->internalDomains->GetLoggerNo( loggerName );
649
650 if( noMainDom >= 0 || noIntDom >= 0 )
651 {
652 Logger* logger= impl->domains->GetLogger( noMainDom );
653 if( logger == nullptr ) logger= impl->internalDomains->GetLogger( noIntDom );
654
655 dumpStateOnLoggerRemoval(impl);
656 writeVerbositiesOnLoggerRemoval( impl, logger );
657
658 if( noMainDom >= 0 )
659 impl->domains->RemoveLogger( noMainDom );
660
661 if( noIntDom >= 0 )
662 impl->internalDomains->RemoveLogger( noIntDom );
663
664 logger->AcknowledgeLox( impl, lang::ContainerOp::Remove );
665
666 BoxesMA& logables= acquireInternalLogables(impl);
667 logables.Add( "Logger {!Q} removed.", logger );
668 logInternal( impl, Verbosity::Info, "LGR", logables );
669 return logger;
670 }
671
672 // not found
673 BoxesMA& logables= acquireInternalLogables(impl);
674 logables.Add( "Logger {!Q} not found. Nothing removed.", loggerName );
675 logInternal( impl, Verbosity::Warning, "LGR", logables );
676
677 return nullptr;
678}
679
680void LI::SetVerbosity(LoxImpl* impl, Logger* logger, Verbosity verbosity, const NString& domain, Priority priority )
681{ ASSERT_ACQUIRED
682
683 // check
684 if ( logger == nullptr )
685 {
686 BoxesMA& logables= acquireInternalLogables(impl);
687 logables.Add( "Given Logger is \"null\". Verbosity not set." );
688 logInternal( impl, Verbosity::Error, "LGR", logables );
689 return;
690 }
691
692 // this might create the (path of) domain(s) and set the \e Logger's verbosities like their
693 // first parent's or as given in configuration
694 Domain* dom= evaluateResultDomain( impl, domain );
695
696 // search logger, insert if not found
697 bool isNewLogger= false;
698 int no= dom->GetLoggerNo( logger );
699 if( no < 0 )
700 {
701 no= dom->AddLogger( logger );
702
703 // error, logger with same name already exists
704 if( no < 0 )
705 {
706 logInternal( impl, Verbosity::Error, "LGR", acquireInternalLogables(impl)
707 .Add( "Unable to add logger {!Q}. Logger with same name exists.", logger ) );
708
709
710 logInternal( impl, Verbosity::Verbose, "LGR",
711 acquireInternalLogables(impl).Add(
712 " Request was: SetVerbosity({!Q}, {!Q}, Verbosity::{}, {}). ",
713 logger, dom->FullPath, verbosity, priority ) );
714
715 Logger* existingLogger= dom->GetLogger( logger->GetName() );
716 logInternal( impl, Verbosity::Verbose, "LGR", acquireInternalLogables(impl)
717 .Add( " Existing Logger: {!Q}.", existingLogger ) );
718
719 return;
720 }
721
722 // We have to register with the SmartLock facility of the \e Logger.
723 // But only if we have not done this yet, via the 'other' root domain tree
724 if ( ( dom->GetRoot() == impl->domains ? impl->internalDomains->GetLoggerNo( logger )
725 : impl->domains->GetLoggerNo( logger )
726 ) < 0 )
727 {
728 logger->AcknowledgeLox( impl, lang::ContainerOp::Insert );
729 }
730
731 // store size of name to support tabular internal log output
732 if ( impl->maxLoggerNameLength < logger->GetName().Length() )
733 impl->maxLoggerNameLength= logger->GetName().Length();
734
735 // for internal log
736 isNewLogger= true;
737
738 // remember that a logger was set after the last removal
739 // (for variable LOXNAME_DUMP_STATE_ON_EXIT)
740 impl->loggerAddedSinceLastDebugState= true;
741 }
742
743 // get verbosities from configuration
744 if( isNewLogger )
745 {
746 BoxesMA& logables= acquireInternalLogables(impl);
747 logables.Add( "Logger {!Q}.", logger );
748 if( domain.StartsWith(Lox::InternalDomains) )
749 logables.Add(" added for internal log messages.");
750 else
751 logables.Add(" added.");
752 logInternal( impl, Verbosity::Info, "LGR", logables );
753
754 // we have to get all verbosities of already existing domains
755 Box replacements[2]= { GetName( impl ), logger->GetName() };
756 Variable varVerbosities= variables::CampVariable(ALOX, Variables::VERBOSITY, replacements );
757 if( varVerbosities.IsDefined() )
758 {
759 getAllVerbosities( impl, varVerbosities, logger, *impl->domains );
760 getAllVerbosities( impl, varVerbosities, logger, *impl->internalDomains );
761 }
762 }
763
764 // do
765 dom->SetVerbosity( no, verbosity, priority );
766
767 BoxesMA& logables= acquireInternalLogables(impl);
768
769 logables.Add( "Logger {!Q}: {!Fill}{!Q'}{!Fill}= Verbosity::{}.",
770 logger->GetName(),
771 impl->maxLoggerNameLength - logger->GetName().Length(),
772 dom->FullPath,
773 impl->maxDomainPathLength - dom->FullPath.Length() + 1,
774 boxing::MakePair(verbosity, priority) );
775
776 Verbosity actVerbosity= dom->GetVerbosity( no );
777 if( actVerbosity != verbosity )
778 logables.Add( " Lower priority ({} < {}). Remains {}.",
779 priority, dom->GetPriority(no), actVerbosity );
780
781 logInternal( impl, Verbosity::Info, "LGR", logables );
782}
783
784void LI::SetVerbosity(LoxImpl* impl, const NString& loggerName, Verbosity verbosity, const NString& domain, Priority priority )
785{
786 // get logger
787 Logger* logger;
788 {
789 ASSERT_ACQUIRED
790
791 Domain* dom= evaluateResultDomain( impl, domain );
792
793 int no= dom->GetLoggerNo( loggerName );
794 if( no >= 0 )
795 logger= dom->GetLogger( no );
796 else
797 {
798 // we have to check if the logger was added in the 'other' tree
799 Domain* otherTree= dom->GetRoot() == impl->domains ? impl->internalDomains
800 : impl->domains;
801 no= otherTree->GetLoggerNo( loggerName );
802 if ( no < 0 )
803 {
804 // error
805 BoxesMA& logables= acquireInternalLogables(impl);
806 logables.Add( "Logger not found. Request was: SetVerbosity({!Q}, {!Q}, Verbosity::{}, {}).",
807 loggerName, dom->FullPath, verbosity, priority );
808 logInternal( impl, Verbosity::Warning, "LGR", logables );
809 return;
810 }
811
812 logger= otherTree->GetLogger( no );
813 }
814 }
815 // use the overloaded method
816 SetVerbosity( impl, logger, verbosity, domain, priority );
817}
818
819void LI::setDomain( LoxImpl* impl,
820 const NString& scopeDomain, Scope scope,
821 bool removeNTRSD, threads::Thread* thread )
822{
823 //note: the public class interface ensures that \p{removeNTRSD} (named thread related scope domain)
824 // only evaluates true for thread related scopes
825
826 ASSERT_ACQUIRED
827
828 // check
829 int pathLevel= checkScopeInformation( impl, scope, "DMN" );
830 if( pathLevel < 0 )
831 return;
832
833 #if !ALIB_SINGLE_THREADED
834 ThreadID threadID= thread != nullptr ? thread->GetID() : UNDEFINED_THREAD;
835 #else
836 threads::ThreadID threadID= UNDEFINED_THREAD;
837 (void) thread;
838 #endif
839
840 NString previousScopeDomain;
841
842 impl->scopeDomains.InitAccess( scope, pathLevel, threadID );
843 if ( removeNTRSD )
844 {
845 previousScopeDomain= impl->scopeDomains.Remove( scopeDomain );
846 }
847 else
848 {
849 if ( scopeDomain.IsNotEmpty() )
850 {
851 NString128 trimmable( scopeDomain );
852 previousScopeDomain= impl->scopeDomains.Store( NString(impl->poolAllocator, trimmable.Trim() ) );
853 }
854 else
855 previousScopeDomain= impl->scopeDomains.Remove( nullptr );
856 }
857
858 // log info on this
859 BoxesMA& logables= acquireInternalLogables(impl);
860 if ( !removeNTRSD && scopeDomain.IsNotEmpty() )
861 {
862 logables.Add("{!Q'} set as default for {}.", scopeDomain, (scope + pathLevel) );
863
864 if ( previousScopeDomain.IsNull() )
865 logInternal( impl, Verbosity::Info, "DMN", logables );
866 else
867 {
868 if ( previousScopeDomain.Equals<NC>( scopeDomain ) )
869 {
870 logables.Add( " (Was already set.)" );
871 logInternal( impl, Verbosity::Verbose, "DMN", logables );
872 }
873 else
874 {
875 logables.Add( " Replacing previous default {!Q'}.", previousScopeDomain );
876 logInternal( impl, Verbosity::Warning, "DMN", logables );
877 }
878 }
879
880 }
881 else
882 {
883 if ( previousScopeDomain.IsNotNull() )
884 {
885 logables.Add("{!Q'} removed from {}.", previousScopeDomain, (scope + pathLevel) );
886 logInternal( impl, Verbosity::Info, "DMN", logables );
887 }
888 else
889 {
890 if ( removeNTRSD )
891 logables.Add("{!Q'} not found. Nothing removed for {}.", scopeDomain );
892 else
893 logables.Add("Empty Scope Domain given, nothing registered for {}.", scopeDomain);
894
895 logables.Add( scope + pathLevel);
896 logInternal( impl, Verbosity::Warning, "DMN", logables );
897 }
898 }
899
900 // it is on us to delete the previous one
901 if ( previousScopeDomain.IsNotNull() )
902 previousScopeDomain.Free(impl->poolAllocator);
903}
904
905void LI::RemoveThreadDomain(LoxImpl* impl, const NString& scopeDomain, Scope scope, threads::Thread* thread )
906{
907 if ( !isThreadRelatedScope( impl, scope ) )
908 return;
909
910 // check
911 if ( scopeDomain.IsEmpty() )
912 {
913 BoxesMA& logables= acquireInternalLogables(impl);
914 logables.Add( "Illegal parameter. No scope domain path given. Nothing removed for {}.",
915 scope );
916 logInternal( impl, Verbosity::Warning, "DMN", logables );
917
918 // do nothing
919 return;
920 }
921
922 // invoke internal master
923 setDomain( impl, scopeDomain, scope, true, thread);
924}
925
926void LI::SetDomainSubstitutionRule(LoxImpl* impl, const NString& domainPath, const NString& replacement )
927{
928 // check null param: clears all rules
929 if ( domainPath.IsEmpty() )
930 {
931 impl->oneTimeWarningCircularDS= false;
932 impl->domainSubstitutions.Clear();
933 logInternal( impl, Verbosity::Info, "DMN", "Domain substitution rules removed.");
934 return;
935 }
936
937
938 // create rule
939 DomainSubstitutionRule newRule( domainPath, replacement );
940 if ( newRule.Search.IsEmpty() )
941 {
942 logInternal( impl, Verbosity::Warning, "DMN", "Illegal domain substitution rule. Nothing stored." );
943 return;
944 }
945
946 // search existing rule
947 List<MonoAllocator, DomainSubstitutionRule>::iterator it;
948 for( it= impl->domainSubstitutions.begin(); it != impl->domainSubstitutions.end() ; ++it )
949 {
950 if ( (*it).type == newRule.type
951 && (*it).Search.Equals<NC>( newRule.Search ) )
952 break;
953 }
954
955 // no replacement given?
956 if ( replacement.IsEmpty() )
957 {
958 BoxesMA& logables= acquireInternalLogables(impl);
959 if ( it == impl->domainSubstitutions.end() )
960 {
961 logables.Add("Domain substitution rule {!Q} not found. Nothing to remove.", domainPath );
962 logInternal( impl, Verbosity::Warning, "DMN", logables );
963 return;
964 }
965
966 logables.Add("Domain substitution rule {!Q} -> {!Q} removed.", domainPath, (*it).Replacement );
967 logInternal( impl, Verbosity::Info, "DMN", logables );
968 (void) impl->domainSubstitutions.erase( it );
969 return;
970 }
971
972 BoxesMA& logables= acquireInternalLogables(impl);
973 logables.Add("Domain substitution rule {!Q} -> {!Q} set.", domainPath, newRule.Replacement );
974
975 // change of rule
976 NString256 msg;
977 if ( it != impl->domainSubstitutions.end() )
978 {
979 msg << " Replacing previous -> \"" << (*it).Replacement << "\".";
980 logables.Add( msg );
981 (*it).Replacement.Reset( newRule.Replacement );
982 }
983 else
984 impl->domainSubstitutions.emplace_back( newRule );
985
986 if( ALOX.IsBootstrapped() ) // this function might be called very early.
987 logInternal( impl, Verbosity::Info, "DMN", logables );
988}
989
990void LI::setPrefix(LoxImpl* impl, const Box& prefix, Scope scope, threads::Thread* thread )
991{
992 ASSERT_ACQUIRED
993
994 // check
995 int pathLevel= checkScopeInformation( impl, scope, "PFX" );
996 if( pathLevel < 0 )
997 return;
998
999 #if !ALIB_SINGLE_THREADED
1000 ThreadID threadID= thread != nullptr ? thread->GetID() : UNDEFINED_THREAD;
1001 #else
1002 threads::ThreadID threadID= UNDEFINED_THREAD;
1003 (void) thread;
1004 #endif
1005
1006 impl->scopePrefixes.InitAccess( scope, pathLevel, threadID );
1007 bool isVoidOrEmpty= prefix.IsType<void>()
1008 || prefix.IsNull()
1009 || ( prefix.IsArray() && !prefix.UnboxLength() );
1010
1011 Box* previousLogable= !isVoidOrEmpty ? impl->scopePrefixes.Store( impl->newPO<PrefixLogable>( impl->poolAllocator, prefix ) )
1012 : impl->scopePrefixes.Remove( nullptr );
1013
1014
1015 BoxesMA& logables= acquireInternalLogables(impl);
1016 logables.Add( "Object ");
1017 Verbosity intMsgVerbosity= Verbosity::Info;
1018 if ( !isVoidOrEmpty )
1019 {
1020 logables.Add( prefix, " added as prefix logable for {}.", (scope + pathLevel) );
1021
1022 if ( previousLogable != nullptr )
1023 {
1024 if ( previousLogable->Call<FEquals>( prefix ) )
1025 {
1026 logables.Add(" (Same as before.)");
1027 intMsgVerbosity= Verbosity::Verbose;
1028 }
1029 else
1030 logables.Add(" Replacing previous {}.", *previousLogable );
1031 }
1032 }
1033 else
1034 {
1035 if ( previousLogable != nullptr )
1036 logables.Add( "{!Q} removed from list of prefix logables for {}.", *previousLogable);
1037 else
1038 {
1039 logables.Add( "<nullptr> given but no prefix logable to remove for {}.");
1040 intMsgVerbosity= Verbosity::Warning;
1041 }
1042 logables.Add( scope + pathLevel );
1043 }
1044
1045 logInternal( impl, intMsgVerbosity, "PFX", logables );
1046
1047 // it is on us to delete the previous one
1048 if ( previousLogable != nullptr )
1049 impl->deletePO(static_cast<PrefixLogable*>( previousLogable ));
1050}
1051
1052
1053void LI::SetPrefix(LoxImpl* impl, const Box& prefix, const NString& domain, lang::Inclusion otherPLs )
1054{
1055
1056 ASSERT_ACQUIRED
1057
1058 Domain* dom= evaluateResultDomain( impl, domain );
1059
1060 bool isVoidOrEmpty= prefix.IsType<void>()
1061 || prefix.IsNull()
1062 || ( prefix.IsArray() && !prefix.UnboxLength() );
1063
1064 BoxesMA& logables= acquireInternalLogables(impl);
1065 Verbosity intLogVerbosity= Verbosity::Info;
1066 PrefixLogable* removedLogable= nullptr;
1067
1068 if ( !isVoidOrEmpty )
1069 {
1070 // create logable: if String* type, then copy the string. We are responsible, then.
1071 logables.Add( "Object {} added as prefix logable for ", prefix );
1072
1073 dom->PrefixLogables.emplace_back( impl->newPO<PrefixLogable>( impl->poolAllocator, prefix ), otherPLs );
1074 }
1075 else
1076 {
1077 auto cntPLs= dom->PrefixLogables.size();
1078 if ( cntPLs > 0 )
1079 {
1080 removedLogable= dom->PrefixLogables.back().first;
1081 dom->PrefixLogables.pop_back();
1082 logables.Add( "Object {} removed from list of prefix logables for",
1083 *static_cast<Box*>(removedLogable) );
1084 }
1085 else
1086 {
1087 logables.Add( "No prefix logables to remove for" );
1088 intLogVerbosity= Verbosity::Warning;
1089 }
1090 }
1091
1092 logables.Add(" domain {!Q'}.", dom->FullPath);
1093 logInternal( impl, intLogVerbosity, "PFX", logables );
1094
1095 if( removedLogable )
1096 impl->deletePO(removedLogable);
1097}
1098
1099
1100#if defined (__GLIBCXX__) || defined(_LIBCPP_VERSION) || defined(__APPLE__) || defined(__ANDROID_NDK__)
1101 void LI::SetStartTime(LoxImpl* impl, time_t startTime, const NString& loggerName )
1102 {
1103 TickConverter converter;
1104 SetStartTime( impl, converter.ToTicks( DateTime::FromEpochSeconds( startTime ) ), loggerName );
1105 }
1106
1107#elif defined( _WIN32 )
1108 void LI::SetStartTime(LoxImpl* impl, const FILETIME& startTime, const NString& loggerName )
1109 {
1110 TickConverter converter;
1111 SetStartTime( impl, converter.ToTicks( DateTime::FromFileTime( startTime ) ), loggerName );
1112 }
1113#else
1114 #pragma message "Unknown Platform in file: " __FILE__ )
1115#endif
1116
1117void LI::SetStartTime(LoxImpl* impl, Ticks startTime, const NString& loggerName )
1118{
1119 ASSERT_ACQUIRED
1120
1121 bool foundOne= false;
1122 for( int loggerNo= 0; loggerNo < impl->domains->CountLoggers(); ++loggerNo )
1123 {
1124 // request logger only from main domain tree
1125 Logger* logger= impl->domains->GetLogger( loggerNo );
1126 if( loggerName.IsNotEmpty() && !loggerName.Equals<NC, lang::Case::Ignore>( logger->GetName()) )
1127 continue;
1128 foundOne= true;
1129
1130 // log info on this
1131 BoxesMA& logables= acquireInternalLogables(impl);
1132 logables.Add( "Logger {!Q}: Start time set to ", logger->GetName() );
1133 if ( !startTime.IsSet() )
1134 {
1135 startTime= Ticks::Now();
1136 logables.Add( "'now'" );
1137 }
1138 else
1139 {
1140 DateTime asDateTime;
1141 TextLogger* asTextLogger= dynamic_cast<TextLogger*>(logger);
1142 if( asTextLogger != nullptr )
1143 asDateTime= asTextLogger->DateConverter.ToDateTime( startTime );
1144 else
1145 asDateTime= TickConverter().ToDateTime( startTime );
1146 logables.Add( "{:yyyy-MM-dd HH:mm:ss}", asDateTime );
1147 }
1148 // do
1149 logger->TimeOfCreation.SetAs( startTime );
1150 logger->TimeOfLastLog .SetAs( startTime );
1151
1152 logInternal( impl, Verbosity::Info, "LGR", logables );
1153 }
1154
1155 if ( loggerName.IsNotEmpty() && !foundOne )
1156 {
1157 BoxesMA& logables= acquireInternalLogables(impl);
1158 logables.Add( "Logger {!Q}: not found. Start time not set.", loggerName );
1159 logInternal( impl, Verbosity::Error, "LGR", logables );
1160 return;
1161 }
1162}
1163
1164
1165void LI::MapThreadName(LoxImpl* impl, const String& threadName, threads::ThreadID id )
1166{
1167 #if !ALIB_SINGLE_THREADED
1168
1169 ASSERT_ACQUIRED
1170
1171 // get current thread id
1172 String origThreadName;
1173 if ( id == 0 )
1174 {
1175 Thread* t= Thread::GetCurrent();
1176 id= t->GetID();
1177 origThreadName= t->GetName();
1178 }
1179 else
1180 origThreadName= nullptr;
1181
1182 // add entry
1183 impl->scopeInfo.threadDictionary.EmplaceOrAssign(id, threadName);
1184
1185 // log info on this
1186 BoxesMA& logables= acquireInternalLogables(impl);
1187 logables.Add( "Mapped thread ID {} to {!Q}.", id, threadName);
1188 if ( origThreadName.IsNotEmpty() )
1189 logables.Add(" Original thread name: {!Q}.", origThreadName );
1190 logInternal( impl, Verbosity::Info, "THR", logables );
1191 #else
1192 (void) impl;
1193 (void) threadName;
1194 (void) id;
1195 #endif
1196}
1197
1198void LI::once( LoxImpl* impl,
1199 const NString& domain, Verbosity verbosity,
1200 const Box& logable,
1201 const String& pGroup,
1202 Scope scope,
1203 int quantity )
1204{
1205 int pathLevel= checkScopeInformation( impl, scope, "DMN" );
1206 if( pathLevel < 0 )
1207 return;
1208
1209 // We need a group. If none is given, there are two options:
1210 NString512 group(pGroup);
1211 bool groupWasEmtpy= group.IsEmpty();
1212 if ( groupWasEmtpy )
1213 {
1214 // GLOBAL scope: exact code line match
1215 if ( scope == Scope::Global )
1216 {
1217 scope= Scope::Filename;
1218 group._('#')._( impl->scopeInfo.GetLineNumber() );
1219 }
1220
1221 // not GLOBAL scope: Unique group per Scope
1222 else
1223 {
1224 group._( impl->noKeyHashKey );
1225 }
1226 }
1227
1228 // get the store
1229 impl->scopeLogOnce.InitAccess( scope, pathLevel, UNDEFINED_THREAD );
1230
1231 SSMap<int>* map= impl->scopeLogOnce.Get();
1232 if( map == nullptr )
1233 {
1234 map= impl->newPO<SSMap<int>>(impl->poolAllocator);
1235 impl->scopeLogOnce.Store( map );
1236 }
1237
1238 // create map entry (if not created yet)
1239 auto it= map->Find( group );
1240 if (it == map->end() )
1241 it= map->InsertUnique( std::make_pair( NString(impl->poolAllocator, group), 0) );
1242
1243 // log Once
1244 if ( quantity >= 0 )
1245 {
1246 if ( it->second < quantity )
1247 {
1248 ++it->second;
1249
1250 // do the log
1251 GetLogableContainer(impl) .Add( std::forward<const Box&>( logable ) );
1252 Entry( impl, domain, verbosity );
1253
1254 // log info if this was the last time
1255 if( it->second == quantity )
1256 {
1257 BoxesMA& logables= acquireInternalLogables(impl);
1258 logables.Add( "Once() reached limit of {} logs. No further logs for ", quantity );
1259
1260 if ( groupWasEmtpy )
1261 logables.Add( scope == Scope::Global ? Box( "this line" )
1262 : Box(scope + pathLevel) );
1263 else
1264 {
1265 logables.Add( "group {!Q}", group );
1266 if ( scope != Scope::Global )
1267 logables.Add(" in ", (scope + pathLevel) );
1268 }
1269 logables.Add('.');
1270
1271 logInternal( impl, Verbosity::Info, "", logables );
1272 }
1273 }
1274 }
1275
1276 // log Nth
1277 else
1278 {
1279 if ( it->second++ % -quantity == 0 )
1280 {
1281 GetLogableContainer(impl) .Add( std::forward<const Box&>( logable ) );
1282 Entry( impl, domain, verbosity );
1283 }
1284 }
1285}
1286
1287void LI::store( LoxImpl* impl, const Box& data, const NString& pKey, Scope scope )
1288{
1289 // We need a key. If none is given, we use a constant one indicating that storage is
1290 // associated exclusively with scope
1291 NString256 key(pKey);
1292 bool keyWasEmtpy= key.IsEmpty();
1293 if ( keyWasEmtpy )
1294 key= impl->noKeyHashKey;
1295
1296 // get path level
1297 int pathLevel= 0;
1298 if ( scope > Scope::Path )
1299 {
1300 pathLevel= int( scope - Scope::Path );
1301 scope= Scope::Path;
1302 }
1303
1304 // get the store
1305 impl->scopeLogData.InitAccess( scope, pathLevel, UNDEFINED_THREAD );
1306 SSMap<Box>* map= impl->scopeLogData.Get();
1307 if( map == nullptr )
1308 {
1309 map= impl->newPO<SSMap<Box>>(impl->poolAllocator);
1310 impl->scopeLogData.Store( map );
1311 }
1312
1313 BoxesMA& logables= acquireInternalLogables(impl);
1314
1315 // create map entry (if not created yet)
1316 auto it= map->Find( key );
1317 if ( !data.IsType<void>() )
1318 {
1319 bool replacedPrevious= false;
1320 if ( it == map->end() )
1321 map->InsertUnique( std::make_pair( NString(impl->poolAllocator, key), data ) );
1322 else
1323 {
1324 replacedPrevious= true;
1325 it->second= data;
1326 }
1327
1328 // log info if this was the last time
1329 logables.Add( "Stored data " );
1330
1331 if ( !keyWasEmtpy )
1332 logables.Add( " with key {!Q} ", key );
1333 logables.Add( "in {}.", (scope + pathLevel) );
1334 if ( replacedPrevious )
1335 logables.Add( " (Replaced and deleted previous.)" );
1336 }
1337
1338 // delete
1339 else
1340 {
1341 if ( it != map->end() )
1342 {
1343 auto keyString= it->first;
1344 map->erase( it );
1345 if ( map->Size() == 0 )
1346 {
1347 impl->deletePO(map);
1348 impl->scopeLogData.Remove( nullptr );
1349 }
1350 keyString.Free(impl->poolAllocator);
1351 logables.Add( "Deleted map data " );
1352 }
1353 else
1354 logables.Add( "No map data found to delete " );
1355
1356 if ( !keyWasEmtpy )
1357 logables.Add( " with key {!Q} ", key );
1358 logables.Add( "in {}.", (scope + pathLevel) );
1359 }
1360
1361 LI::logInternal( impl, Verbosity::Info, "LGD", logables );
1362}
1363
1364
1365Box LI::retrieve( LoxImpl* impl, const NString& pKey, Scope scope )
1366{
1367 // We need a key. If none is given, we use a constant one indicating that storage is
1368 // associated exclusively with scope
1369 NString256 key= pKey;
1370 bool keyWasEmtpy= key.IsEmpty();
1371 if ( keyWasEmtpy )
1372 key= impl->noKeyHashKey;
1373
1374 int pathLevel= 0;
1375 if ( scope > Scope::Path )
1376 {
1377 pathLevel= int( scope - Scope::Path );
1378 scope= Scope::Path;
1379 }
1380
1381 // get the data (create if not found)
1382 impl->scopeLogData.InitAccess( scope, pathLevel, UNDEFINED_THREAD );
1383 Box returnValue;
1384 SSMap<Box>* map= impl->scopeLogData.Get();
1385 if( map != nullptr )
1386 {
1387 auto it= map->Find( key );
1388 if ( it != map->end() )
1389 returnValue= it->second;
1390 }
1391
1392 if ( returnValue.IsType<void>() )
1393 store( impl, Box(), pKey, scope + pathLevel );
1394
1395 // log info if this was the last time
1396 BoxesMA& logables= acquireInternalLogables(impl);
1397 logables.Add( "Data " );
1398
1399 if ( !keyWasEmtpy )
1400 logables.Add( " with key {!Q} ", key );
1401 logables.Add( "in ", (scope + pathLevel), ( !returnValue.IsType<void>() ? " received."
1402 : " not found." ) );
1403
1404 logInternal( impl, Verbosity::Info, "LGD", logables );
1405 return returnValue;
1406}
1407
1408
1409void LI::State( LoxImpl* impl,
1410 const NString& domain,
1411 Verbosity verbosity,
1412 const String& headLine,
1413 StateInfo flags )
1414{
1415 ASSERT_ACQUIRED
1416
1417 NAString buf;
1418 buf.SetBuffer( 2048 );
1419 if ( headLine.IsNotEmpty() )
1420 buf._<NC>( headLine ).NewLine();
1421
1422 GetState( impl, buf, flags );
1423
1424 GetLogableContainer(impl) .Add( buf );
1425 Entry( impl, domain, verbosity );
1426}
1427
1428BoxesMA& LI::GetLogableContainer(LoxImpl* impl)
1429{
1430 auto cntAcquirements= impl->CountAcquirements();
1431 ALIB_ASSERT_ERROR( cntAcquirements >= 1, "ALOX", "Lox not acquired." )
1432 ALIB_ASSERT_WARNING( cntAcquirements < 5, "ALOX", "Logging recursion depth >= 5" )
1433 while( int(impl->logableContainers.size()) < cntAcquirements )
1434 impl->logableContainers.emplace_back( impl->monoAllocator().New<BoxesMA>(impl->monoAllocator) );
1435 BoxesMA& logables= *impl->logableContainers[size_t(cntAcquirements - 1)];
1436 logables.clear();
1437 return logables;
1438}
1439
1440void LI::Entry(LoxImpl* impl, const NString& domain, Verbosity verbosity )
1441{
1442 ASSERT_ACQUIRED
1443
1444 // auto-initialization of debug loggers
1445 #if ALOX_DBG_LOG
1446 if( impl == Log::Get()->impl
1447 && impl->domains->CountLoggers() == 0
1448 && Log::DebugLogger == nullptr )
1449 Log::AddDebugLogger( Log::Get() );
1450 #endif
1451
1452 ALIB_ASSERT_ERROR(ALOX.IsBootstrapped(), "ALOX", "ALox (ALib) was not properly bootstrapped." )
1453
1454 ++impl->CntLogCalls;
1455
1456 if ( impl->domains->CountLoggers() == 0 )
1457 return;
1458
1459 log( impl,
1460 evaluateResultDomain( impl, domain ),
1461 verbosity,
1462 *impl->logableContainers[size_t(impl->CountAcquirements() - 1)],
1463 lang::Inclusion::Include );
1464}
1465
1466int LI::IsActive( LoxImpl* impl, Verbosity verbosity, const NString& domain )
1467{
1468 ASSERT_ACQUIRED
1469
1470 // auto-initialization of debug loggers
1471 #if ALOX_DBG_LOG
1472 if( impl == Log::Get()->impl
1473 && impl->domains->CountLoggers() == 0
1474 && Log::DebugLogger == nullptr )
1475 Log::AddDebugLogger( Log::Get() );
1476 #endif
1477
1478 ALIB_ASSERT_ERROR(ALOX.IsBootstrapped(), "ALOX", "ALox (ALib) was not properly bootstrapped." )
1479
1480 if ( impl->domains->CountLoggers() == 0 )
1481 return 0;
1482
1483 Domain* dom= evaluateResultDomain( impl, domain );
1484
1485 int result= 0;
1486 for ( int i= 0; i < dom->CountLoggers() ; ++i )
1487 if( dom->IsActive( i, verbosity ) )
1488 ++result;
1489 return result;
1490}
1491
1492void LI::IncreaseLogCounter( LoxImpl* impl)
1493{
1494 ++impl->CntLogCalls;
1495}
1496
1497void LI::entryDetectDomainImpl(LoxImpl* impl, Verbosity verbosity )
1498{
1499 BoxesMA& logables= *impl->logableContainers[size_t(impl->CountAcquirements() - 1)];
1500 if ( logables.Size() > 1 && logables[0].IsArrayOf<nchar>() )
1501 {
1502 NString firstArg= logables[0].Unbox<NString>();
1503
1504 // accept internal domain at the start
1505 integer idx= 0;
1506 if( firstArg.StartsWith( Lox::InternalDomains ) )
1507 idx+= Lox::InternalDomains.Length();
1508
1509 // loop over domain and check for illegal characters
1510 bool illegalCharacterFound= false;
1511 for( ; idx< firstArg.Length() ; ++idx )
1512 {
1513 char c= firstArg[idx];
1514 if (! ( isdigit( c )
1515 || ( c >= 'A' && c <= 'Z' )
1516 || c == '-'
1517 || c == '_'
1518 || c == '/'
1519 || c == '.'
1520 ) )
1521 {
1522 illegalCharacterFound= true;
1523 break;
1524 }
1525 }
1526
1527 if ( illegalCharacterFound )
1528 {
1529 Entry( impl, nullptr, verbosity );
1530 return;
1531 }
1532
1533 logables.erase( logables.begin() );
1534 Entry( impl, firstArg, verbosity );
1535 return;
1536 }
1537
1538 Entry( impl, nullptr, verbosity );
1539}
1540
1541
1542// #################################################################################################
1543// internals
1544// #################################################################################################
1545Domain* LI::evaluateResultDomain(LoxImpl* impl, const NString& domainPath )
1546{
1547 NString128 resDomain;
1548
1549 // 0. internal domain tree?
1550 if ( domainPath.StartsWith( Lox::InternalDomains ) )
1551 {
1552 // cut "$/" from the path
1553 resDomain._( domainPath, Lox::InternalDomains.Length() );
1554 return findDomain( impl, *impl->internalDomains, resDomain );
1555 }
1556
1557 // loop over scopes
1558 NString64 localPath; localPath.DbgDisableBufferReplacementWarning();
1559 impl->scopeDomains.InitWalk( Scope::ThreadInner,
1560 // we have to provide NULL_STRING if parameter is empty
1561 domainPath.IsNotEmpty() ? localPath._(domainPath)
1562 : NULL_NSTRING
1563 );
1564 NString nextDefault;
1565 while( (nextDefault= impl->scopeDomains.Walk() ).IsNotNull() )
1566 {
1567 ALIB_ASSERT( nextDefault.IsNotEmpty(), "ALOX" )
1568
1569 if ( resDomain.IsNotEmpty() )
1570 resDomain.InsertAt( "/", 0);
1571 resDomain.InsertAt( nextDefault, 0 );
1572
1573 // absolute path? That's it
1574 if ( resDomain.CharAtStart() == Domain::Separator() )
1575 break;
1576 }
1577 return findDomain( impl, *impl->domains, resDomain );
1578}
1579
1580void LI::getVerbosityFromConfig(LoxImpl* impl, Variable& v, Logger* logger, Domain& dom )
1581{
1582 // get logger number. It may happen that the logger is not existent in this domain tree.
1583 int loggerNo= dom.GetLoggerNo( logger ) ;
1584 if ( loggerNo < 0 )
1585 return;
1586
1587 auto& cvVerb = v.Get<alib::lox::CVVerbosities>();
1588
1589 for (auto it : cvVerb)
1590 {
1591 Tokenizer verbosityTknzr( it, '=' );
1592
1593 NString256 domainStrBuf;
1594 Substring domainStrParser= verbosityTknzr.Next();
1595 if ( domainStrParser.ConsumeString<lang::Case::Ignore>( A_CHAR("INTERNAL_DOMAINS")) )
1596 {
1597 while ( domainStrParser.ConsumeChar('/') )
1598 ;
1599 domainStrBuf << Lox::InternalDomains << domainStrParser;
1600 }
1601 else
1602 domainStrBuf._( domainStrParser );
1603
1604 NSubstring domainStr= domainStrBuf ;
1605
1606 Substring verbosityStr= verbosityTknzr.Next();
1607 if ( verbosityStr.IsEmpty() )
1608 continue;
1609
1610 int searchMode= 0;
1611 if ( domainStr.ConsumeChar ( '*' ) ) searchMode+= 2;
1612 if ( domainStr.ConsumeCharFromEnd( '*' ) ) searchMode+= 1;
1613 if( ( searchMode == 0 && dom.FullPath.Equals <NC ,lang::Case::Ignore>( domainStr ) )
1614 || ( searchMode == 1 && dom.FullPath.StartsWith<CHK,lang::Case::Ignore>( domainStr ) )
1615 || ( searchMode == 2 && dom.FullPath.EndsWith <CHK,lang::Case::Ignore>( domainStr ) )
1616 || ( searchMode == 3 && dom.FullPath.IndexOf <CHK,lang::Case::Ignore>( domainStr ) >=0 )
1617 )
1618 {
1619 Verbosity verbosity(Verbosity::Info);
1620 enumrecords::Parse<Verbosity>(verbosityStr, verbosity );
1621 dom.SetVerbosity( loggerNo, verbosity, v.GetPriority() );
1622
1623 // log info on this
1624 NString512 msg;
1625 msg._<NC>( "Logger \"" )._<NC>( logger->GetName() ) ._<NC>( "\":" )._(NTab(11 + impl->maxLoggerNameLength))
1626 ._<NC>( '\'' )._<NC>( dom.FullPath )
1627 ._( '\'' ).InsertChars(' ', impl->maxDomainPathLength - dom.FullPath.Length() + 1 )
1628 ._( "= Verbosity::" )
1629 ._( boxing::MakePair(verbosity, dom.GetPriority( loggerNo )) ).TrimEnd()
1630 ._<NC>( '.' );
1631
1632 logInternal( impl, Verbosity::Info, "LGR", msg );
1633 }
1634 }
1635}
1636
1637void LI::getDomainPrefixFromConfig(LoxImpl* impl, Domain& dom )
1638{
1639 Variable variable= variables::CampVariable(ALOX);
1641 const Declaration* decl= Declaration::Get( Variables::PREFIXES);
1642 decl= ALOX.GetConfig()->StoreDeclaration( decl,
1643 #if !ALIB_CHARACTERS_WIDE
1644 GetName( impl )
1645 #else
1646 String128( GetName( impl ) )
1647 #endif
1648 );
1649 if( !variable.Try(decl) )
1650 return;
1651 }
1652
1653 Tokenizer prefixTokOuter;
1654 prefixTokOuter.Set(variable, ';', true);
1655 while(prefixTokOuter.HasNext())
1656 {
1657 Tokenizer prefixTok( prefixTokOuter.Next(), '=' );
1658
1659 NString128 domainStrBuf;
1660 Substring domainStrParser= prefixTok.Next();
1661 if ( domainStrParser.ConsumeString<lang::Case::Ignore>( A_CHAR("INTERNAL_DOMAINS")) )
1662 {
1663 while ( domainStrParser.ConsumeChar('/') )
1664 ;
1665 domainStrBuf << Lox::InternalDomains << domainStrParser;
1666 }
1667 else
1668 domainStrBuf._( domainStrParser );
1669
1670 NSubstring domainStr= domainStrBuf ;
1671
1672 Tokenizer prefixTokInner( prefixTok.Next(), ',' );
1673 Substring prefixStr= prefixTokInner.Next();
1674 if ( prefixStr.IsEmpty() )
1675 continue;
1676 if ( prefixStr.ConsumeChar( '\"' ) )
1677 prefixStr.ConsumeCharFromEnd( '\"' );
1678
1679 lang::Inclusion otherPLs= lang::Inclusion::Include;
1680 prefixTokInner.Next();
1681 if ( prefixTokInner.Actual.IsNotEmpty() )
1682 enumrecords::ParseEnumOrTypeBool( prefixTokInner.Actual, otherPLs, lang::Inclusion::Exclude, lang::Inclusion::Include );
1683
1684 int searchMode= 0;
1685 if ( domainStr.ConsumeChar ( '*' ) ) searchMode+= 2;
1686 if ( domainStr.ConsumeCharFromEnd( '*' ) ) searchMode+= 1;
1687 if( ( searchMode == 0 && dom.FullPath.Equals <NC ,lang::Case::Ignore>( domainStr ) )
1688 || ( searchMode == 1 && dom.FullPath.StartsWith<CHK,lang::Case::Ignore>( domainStr ) )
1689 || ( searchMode == 2 && dom.FullPath.EndsWith <CHK,lang::Case::Ignore>( domainStr ) )
1690 || ( searchMode == 3 && dom.FullPath.IndexOf <CHK,lang::Case::Ignore>( domainStr ) >=0 )
1691 )
1692 {
1693 dom.PrefixLogables.emplace_back( impl->newPO<PrefixLogable>( impl->poolAllocator, prefixStr ), otherPLs );
1694
1695 // log info on this
1696 NString128 msg; msg._<NC>( "String \"" )._<NC>( prefixStr )._<NC>( "\" added as prefix logable for domain \'" )
1697 ._<NC>( dom.FullPath )
1698 ._<NC>( "\'. (Retrieved from configuration variable \'" )._<NC>(variable)._( "\'.)" );
1699
1700 logInternal( impl, Verbosity::Info, "PFX", msg );
1701 }
1702 }
1703}
1704
1705void LI::getAllVerbosities(LoxImpl* impl, Variable& varVerbosities, Logger* logger, Domain& dom )
1706{
1707 // get verbosity for us
1708 getVerbosityFromConfig( impl, varVerbosities, logger, dom );
1709
1710 // loop over all subdomains (recursion)
1711 for ( Domain& subDomain : dom.SubDomains )
1712 getAllVerbosities( impl, varVerbosities, logger, subDomain );
1713}
1714
1715
1716Domain* LI::findDomain(LoxImpl* impl, Domain& rootDomain, NString domainPath )
1717{
1718 int maxSubstitutions= 10;
1719 NString128 substPath;
1720 for(;;)
1721 {
1722 // loop for creating domains, one by one
1723 Domain* dom= nullptr;
1724 for(;;)
1725 {
1726 bool wasCreated;
1727 dom= rootDomain.Find( domainPath, 1, &wasCreated );
1728 if ( wasCreated )
1729 {
1730 // get maximum domain path length (for nicer State output only...)
1731 if ( impl->maxDomainPathLength < dom->FullPath.Length() )
1732 impl->maxDomainPathLength= dom->FullPath.Length();
1733
1734 // log info on new domain
1735 BoxesMA& logables= acquireInternalLogables(impl);
1736 logables.Add( "{!Q} registered.", dom->FullPath );
1737 logInternal( impl, Verbosity::Info, "DMN", logables );
1738 }
1739
1740 // read domain from config
1741 if ( !dom->ConfigurationAlreadyRead )
1742 { dom->ConfigurationAlreadyRead= true;
1743
1744 Box replacements[2];
1745 for ( int i= 0; i < dom->CountLoggers(); ++i )
1746 {
1747 Logger* logger= dom->GetLogger(i);
1748 replacements[0]= GetName( impl );
1749 replacements[1]= logger->GetName();
1750 Variable varVerbosities= variables::CampVariable(ALOX, Variables::VERBOSITY, replacements );
1751 if ( varVerbosities.IsDefined() )
1752 getVerbosityFromConfig( impl, varVerbosities, logger, *dom );
1753 }
1754
1755 getDomainPrefixFromConfig( impl, *dom );
1756 }
1757
1758 if ( wasCreated )
1759 {
1760 if ( dom->CountLoggers() == 0 )
1761 logInternal( impl, Verbosity::Verbose, "DMN", " No loggers set, yet." );
1762 else
1763 {
1764 for ( int i= 0; i < dom->CountLoggers(); ++i )
1765 {
1766 NString256 msg; msg._(" \"")._( dom->GetLogger(i)->GetName() )._("\": ");
1767 msg.InsertChars( ' ', impl->maxLoggerNameLength + 6 - msg.Length() );
1768 msg._( dom->FullPath )._(" = " )
1769 ._(boxing::MakePair(dom->GetVerbosity(i), dom->GetPriority(i)));
1770 logInternal( impl, Verbosity::Verbose, "DMN", msg );
1771 }
1772 }
1773 }
1774 else
1775 break;
1776 }
1777
1778 // apply domain substitutions
1779 if( !impl->domainSubstitutions.empty() )
1780 {
1781 substPath.Reset();
1782 NSubstring domFullPath= dom->FullPath;
1783 if ( domFullPath.CharAtStart<NC>() == '$' )
1784 domFullPath.ConsumeChar();
1785
1786 while( maxSubstitutions-- > 0 )
1787 {
1788 // loop over rules
1789 bool substituted= false;
1790 for( auto& rule : impl->domainSubstitutions )
1791 {
1792 switch( rule.type )
1793 {
1794 case DomainSubstitutionRule::Type::StartsWith:
1795 if( substPath.IsEmpty() )
1796 {
1797 if ( domFullPath.StartsWith( rule.Search ) )
1798 {
1799 substPath._( rule.Replacement )._( domFullPath, rule.Search.Length() );
1800 substituted= true;
1801 continue;
1802 }
1803 }
1804 else
1805 {
1806 if ( substPath.StartsWith( rule.Search ) )
1807 {
1808 substPath.ReplaceSubstring<NC>( rule.Replacement, 0, rule.Search.Length() );
1809 substituted= true;
1810 continue;
1811 }
1812 }
1813 break;
1814
1815 case DomainSubstitutionRule::Type::EndsWith:
1816 if( substPath.IsEmpty() )
1817 {
1818 if ( domFullPath.EndsWith( rule.Search ) )
1819 {
1820 substPath._( domFullPath, 0, domFullPath.Length() - rule.Search.Length() )._( rule.Replacement );
1821 substituted= true;
1822 continue;
1823 }
1824 }
1825 else
1826 {
1827 if ( substPath.EndsWith( rule.Search ) )
1828 {
1829 substPath.DeleteEnd( rule.Search.Length() )._( rule.Replacement );
1830 substituted= true;
1831 continue;
1832 }
1833 }
1834 break;
1835
1836
1837 case DomainSubstitutionRule::Type::Substring:
1838 {
1839 if( substPath.IsEmpty() )
1840 {
1841 integer idx= domFullPath.IndexOf( rule.Search );
1842 if ( idx >= 0 )
1843 {
1844 substPath._( domFullPath, 0, idx )._( rule.Replacement)._( domFullPath, idx + rule.Search.Length() );
1845 substituted= true;
1846 continue; //next rule
1847 }
1848 }
1849 else
1850 {
1851 integer idx= substPath.IndexOf( rule.Search, 0 );
1852 if ( idx >= 0 )
1853 {
1854 substPath.ReplaceSubstring<NC>( rule.Replacement, idx, rule.Search.Length() );
1855 substituted= true;
1856 continue; //next rule
1857 }
1858 }
1859 }
1860 break;
1861
1862
1863 case DomainSubstitutionRule::Type::Exact:
1864 {
1865 if( substPath.IsEmpty() )
1866 {
1867 if ( domFullPath.Equals<NC>( rule.Search ) )
1868 {
1869 substPath._( rule.Replacement);
1870 substituted= true;
1871 continue; //next rule
1872 }
1873 if ( domFullPath.CharAtStart<NC>() == '$' )
1874 {
1875 substPath._( rule.Replacement);
1876 substituted= true;
1877 continue; //next rule
1878 }
1879 }
1880 else
1881 {
1882 if ( substPath.Equals<NC>( rule.Search) )
1883 {
1884 substPath.Reset( rule.Replacement );
1885 substituted= true;
1886 continue; //next rule
1887 }
1888 }
1889 }
1890 break;
1891
1892 default: ALIB_ERROR("ALOX", "Illegal switch state." ) break;
1893 } // switch rule type
1894
1895 }//rules loop
1896
1897 // stop if non was found
1898 if( !substituted )
1899 break;
1900 }
1901
1902 // too many substitutions?
1903 if ( maxSubstitutions <= 0 && !impl->oneTimeWarningCircularDS )
1904 {
1905 impl->oneTimeWarningCircularDS= true;
1906 logInternal( impl, Verbosity::Error, "DMN",
1907 "The Limit of 10 domain substitutions was reached. Circular substitution assumed!"
1908 " (This error is only reported once!)" );
1909 }
1910
1911 // anything substituted?
1912 if( substPath.Length() > 0 )
1913 {
1914 domainPath= substPath;
1915 continue;
1916 }
1917 }
1918
1919 return dom;
1920 }
1921}
1922
1923int LI::checkScopeInformation(LoxImpl* impl, Scope& scope, const NString& internalDomain )
1924{
1925 int pathLevel= 0;
1926 if ( scope > Scope::Path )
1927 {
1928 pathLevel= int( scope - Scope::Path );
1929 scope= Scope::Path;
1930 }
1931
1932 if ( ( scope == Scope::Path && impl->scopeInfo.GetFullPath().IsEmpty() )
1933 || ( scope == Scope::Filename && impl->scopeInfo.GetFileName().IsEmpty() )
1934 || ( scope == Scope::Method && impl->scopeInfo.GetMethod() .IsEmpty() ) )
1935 {
1936 BoxesMA& logables= acquireInternalLogables(impl);
1937 logables.Add( "Missing scope information. Cant use {}.", (scope + pathLevel) );
1938 logInternal( impl, Verbosity::Error, internalDomain, logables );
1939 return -1;
1940 }
1941 return pathLevel;
1942}
1943
1944bool LI::isThreadRelatedScope(LoxImpl* impl, Scope scope )
1945{
1946 // check
1947 if ( scope == Scope::ThreadOuter
1948 || scope == Scope::ThreadInner )
1949 return true;
1950
1951 BoxesMA& logables= acquireInternalLogables(impl);
1952 logables.Add( "Illegal parameter, only Scope::ThreadOuter and Scope::ThreadInner allowed."
1953 " Given: {}.", scope );
1954 logInternal( impl, Verbosity::Error, "DMN", logables );
1955
1956 #if ALIB_DEBUG
1957 alib::assert::Raise( { impl->scopeInfo.GetOrigFile(),
1958 impl->scopeInfo.GetLineNumber(),
1959 impl->scopeInfo.GetMethod(),
1960 #if !ALIB_SINGLE_THREADED
1961 impl->scopeInfo.GetThreadNativeID(),
1962 #elif ALIB_EXT_LIB_THREADS_AVAILABLE
1963 std::thread::id(),
1964 #endif
1965 impl->scopeInfo.GetTypeInfo()
1966 },
1967 0, "Illegal scope type \"{}\" given. Only Scope::ThreadOuter and "
1968 "Scope::ThreadInner allowed.", scope );
1969 #endif
1970
1971 return false;
1972}
1973
1974void LI::log(LoxImpl* impl, Domain* dom, Verbosity verbosity, BoxesMA& logables, lang::Inclusion includePrefixes )
1975{
1976 ++dom->CntLogCalls;
1977 bool logablesCollected= false;
1978 PrefixLogable marker(impl->poolAllocator, nullptr);
1979 for ( int i= 0; i < dom->CountLoggers() ; ++i )
1980 if( dom->IsActive( i, verbosity ) )
1981 {
1982 // lazily collect objects once an active logger is found
1983 if ( !logablesCollected )
1984 {
1985 logablesCollected= true;
1986 impl->scopePrefixes.InitWalk( Scope::ThreadInner, &marker );
1987 const Box* next;
1988 int userLogablesSize= int( logables.Size() );
1989 int threadInnersSize= -1;
1990
1991 while( (next= impl->scopePrefixes.Walk() ) != nullptr )
1992 {
1993 if( next != &marker )
1994 {
1995 // this is false for internal domains (only domain specific logables are added there)
1996 if ( includePrefixes == lang::Inclusion::Include )
1997 {
1998 // after marker is read, logables need to be prepended. This is checked below
1999 // using "qtyThreadInners < 0"
2000 if ( next->IsType<BoxesMA*>() )
2001 {
2002 auto* boxes= next->Unbox<BoxesMA*>();
2003 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2004 logables.emplace( logables.begin() + ( threadInnersSize < 0 ? userLogablesSize : 0 ),
2005 (*boxes)[size_t(pfxI)] );
2006 }
2007 else if ( next->IsType<BoxesHA*>() )
2008 {
2009 auto* boxes= next->Unbox<BoxesHA*>();
2010 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2011 logables.emplace( logables.begin() + ( threadInnersSize < 0 ? userLogablesSize : 0 ),
2012 (*boxes)[size_t(pfxI)] );
2013 }
2014 else if ( next->IsType<BoxesPA*>() )
2015 {
2016 auto* boxes= next->Unbox<BoxesPA*>();
2017 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2018 logables.emplace( logables.begin() + ( threadInnersSize < 0 ? userLogablesSize : 0 ),
2019 (*boxes)[size_t(pfxI)] );
2020 }
2021 else
2022 logables.emplace( logables.begin() + ( threadInnersSize < 0 ? userLogablesSize : 0 ), *next );
2023 }
2024 }
2025
2026 // was this the actual? then insert domain-associated logables now
2027 else
2028 {
2029 bool excludeOthers= false;
2030 threadInnersSize= int( logables.Size() ) - userLogablesSize;
2031 Domain* pflDom= dom;
2032 while ( pflDom != nullptr )
2033 {
2034 for( auto it= pflDom->PrefixLogables.rbegin() ; it != pflDom->PrefixLogables.rend() ; ++it )
2035 {
2036 // a list of logables? Copy them
2037 PrefixLogable& prefix= *it->first;
2038 if ( prefix.IsType<BoxesHA*>() )
2039 {
2040 auto* boxes= prefix.Unbox<BoxesHA*>();
2041 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2042 logables.emplace( logables.begin(),
2043 (*boxes)[size_t(pfxI)] );
2044 }
2045 else if ( prefix.IsType<BoxesMA*>() )
2046 {
2047 auto* boxes= prefix.Unbox<BoxesMA*>();
2048 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2049 logables.emplace( logables.begin(),
2050 (*boxes)[size_t(pfxI)] );
2051 }
2052 else if ( prefix.IsType<BoxesPA*>() )
2053 {
2054 auto* boxes= prefix.Unbox<BoxesPA*>();
2055 for (auto pfxI= boxes->Size() - 1 ; pfxI >= 0 ; --pfxI )
2056 logables.emplace( logables.begin(),
2057 (*boxes)[size_t(pfxI)] );
2058 }
2059 else
2060 logables.emplace( logables.begin(), prefix );
2061
2062
2063 if ( it->second == lang::Inclusion::Exclude )
2064 {
2065 excludeOthers= true;
2066 break;
2067 }
2068 }
2069
2070 pflDom= excludeOthers ? nullptr : pflDom->Parent;
2071 }
2072
2073 // found a stoppable one? remove those from thread inner and break
2074 if (excludeOthers)
2075 {
2076 for ( int ii= 0; ii < threadInnersSize ; ++ii )
2077 logables.pop_back();
2078 break;
2079 }
2080 }
2081 }
2082 } // end of collection
2083
2084 Logger* logger= dom->GetLogger(i);
2085 { ALIB_LOCK_RECURSIVE_WITH(*logger)
2086 ++logger->CntLogs;
2087 logger->Log( *dom, verbosity, logables, impl->scopeInfo );
2088 logger->TimeOfLastLog= Ticks::Now();
2089 }
2090 }
2091}
2092
2093BoxesMA& LI::acquireInternalLogables(LoxImpl* impl)
2094{
2095 if( integer(impl->internalLogables.size()) == impl->internalLogRecursionCounter )
2096 {
2097 BoxesMA* newLogables= impl->monoAllocator().New<BoxesMA>(impl->monoAllocator);
2098 impl->internalLogables.emplace_back( newLogables );
2099 }
2100
2101 return *impl->internalLogables[size_t(impl->internalLogRecursionCounter++)];
2102}
2103
2104void LI::logInternal(LoxImpl* impl, Verbosity verbosity, const NString& subDomain, BoxesMA& msg )
2105{
2106 ALIB_ASSERT_ERROR(ALOX.IsBootstrapped(), "ALOX", "ALox (ALib) was not properly bootstrapped." )
2107 log( impl, findDomain( impl, *impl->internalDomains, subDomain ), verbosity, msg, lang::Inclusion::Exclude );
2108
2109 impl->internalLogables[size_t(--impl->internalLogRecursionCounter)]->clear();
2110}
2111
2112void LI::logInternal(LoxImpl* impl, Verbosity verbosity, const NString& subDomain, const NString& msg )
2113{
2114 BoxesMA& logables= acquireInternalLogables(impl);
2115 logables.Add( msg );
2116 logInternal( impl, verbosity, subDomain, logables );
2117}
2118
2119#if !DOXYGEN
2120
2121namespace {
2122void getStateDomainRecursive( Domain& domain, integer maxDomainPathLength, NAString& buf );
2123void getStateDomainRecursive( Domain& domain, integer maxDomainPathLength, NAString& buf )
2124{
2125 integer reference= buf.Length();
2126 buf._(" "); domain.ToString( buf );
2127 integer idx= buf.IndexOf( '[', reference );
2128 buf.InsertChars( ' ', maxDomainPathLength + 5 - idx + reference, idx);
2129 buf.NewLine();
2130
2131 // loop over all subdomains (recursion)
2132 for ( Domain& subDomain : domain.SubDomains )
2133 getStateDomainRecursive( subDomain, maxDomainPathLength, buf );
2134}
2135
2136void getStateDomainsWithDiffVerb( Domain& dom, int loggerNo, std::vector<Domain*>& results );
2137void getStateDomainsWithDiffVerb( Domain& dom, int loggerNo, std::vector<Domain*>& results )
2138{
2139 if ( dom.Parent == nullptr
2140 || dom.Parent->GetVerbosity(loggerNo) != dom.GetVerbosity(loggerNo) )
2141 results.emplace_back( &dom );
2142
2143 for( auto& it : dom.SubDomains )
2144 getStateDomainsWithDiffVerb( it, loggerNo, results );
2145}
2146
2147void getStateCollectPrefixes( Domain& dom, integer indentSpaces, NAString& target );
2148void getStateCollectPrefixes( Domain& dom, integer indentSpaces, NAString& target )
2149{
2150 AString buffer;
2151 for ( auto& pfl : dom.PrefixLogables )
2152 {
2153 buffer.InsertChars( ' ', indentSpaces );
2154 buffer << '"';
2155 integer actLen= buffer.Length();
2156 buffer._( *static_cast<Box*>(pfl.first) );
2157 ESC::ReplaceToReadable( buffer, actLen );
2158 buffer << Escape( lang::Switch::On, actLen );
2159 buffer << '"';
2160 if ( pfl.second == lang::Inclusion::Exclude )
2161 buffer._<NC>( " (Excl.)" );
2162 buffer._<NC>( Tab( 25, -1 ) );
2163 buffer._<NC>( "<domain> [" )._<NC>( dom.FullPath )._<NC>(']').NewLine();
2164 }
2165 target << buffer;
2166
2167 for( auto& subDom : dom.SubDomains )
2168 getStateCollectPrefixes( subDom, indentSpaces, target );
2169}
2170
2171} // anonymous namespace
2172
2173#endif // !DOXYGEN
2174
2175
2176void LI::GetState( LoxImpl* impl, NAString& buf, StateInfo flags )
2177{
2178 ASSERT_ACQUIRED
2179
2180 ScopeDump scopeDump( IF_ALIB_THREADS( impl->scopeInfo.threadDictionary, )
2181 impl->noKeyHashKey, buf );
2182
2183 if ( HasBits( flags, StateInfo::CompilationFlags ) )
2184 {
2185 buf._<NC>( "ALib Version: " )._<NC>( alib::VERSION)
2186 ._<NC>(" (Rev. ") ._ ( alib::REVISION)._(')').NewLine();
2187 buf._<NC>( "ALib Compiler Symbols:" ).NewLine();
2188 {
2189 for( auto& p : alib::COMPILATION_FLAG_MEANINGS )
2190 {
2191 buf << " " << NField( p.Name, 41, lang::Alignment::Left ) << ':'
2192 << (alib::COMPILATION_FLAGS.bits[p.Flag/8] & (1 << p.Flag % 8) ? " On" : " Off")
2193 << NEW_LINE;
2194 }
2195
2196 }
2197
2198 buf.NewLine();
2199 }
2200
2201 // basic lox info
2202 if( alib::HasBits( flags, StateInfo::Basic ) )
2203 buf._<NC>( "Name: \"" )._( impl->scopeInfo.GetLoxName() )._('\"').NewLine();
2204
2205 if( HasBits( flags, StateInfo::Version ) )
2206 {
2207 buf._<NC>( "Version: " )._<NC>( alib::VERSION)
2208 ._<NC>(" (Rev. " )._( alib::REVISION)._(')').NewLine();
2209 }
2210
2211 if( HasBits( flags, StateInfo::Basic ) )
2212 buf._<NC>( "#Log Calls: " )._<NC>( impl->CntLogCalls ).NewLine();
2213
2214 if( HasBits( flags, StateInfo::Basic )
2215 || HasBits( flags, StateInfo::Version ) )
2216 buf.NewLine();
2217
2218 // source path trim info
2219 if( HasBits( flags, StateInfo::SPTR ) )
2220 {
2221 buf._<NC>( "Source Path Trimming Rules: " ).NewLine();
2222
2223 int cnt= 0;
2224 // do 2 times, 0== global list, 1 == local list
2225 for( int trimInfoNo= 0; trimInfoNo < 2 ; ++trimInfoNo )
2226 {
2227 // choose local or global list
2228 std::vector<ScopeInfo::SourcePathTrimRule>* trimInfoList=
2229 trimInfoNo == 0 ? &ScopeInfo::GlobalSPTRs
2230 : &impl->scopeInfo.LocalSPTRs;
2231
2232
2233 // loop over trimInfo
2234 for ( auto& ti : *trimInfoList )
2235 {
2236 ++cnt;
2237 buf._<NC>( trimInfoNo == 0 ? " Global: "
2238 : " Local: " );
2239 buf._<NC>( ti.IsPrefix ? "\"" : "\"*");
2240 buf._<NC>( ti.Path )._<NC>( "\", " );
2241 buf._<NC>( ti.IncludeString );
2242 if ( ti.TrimOffset != 0 )
2243 buf._<NC>( ti.Path )._<NC>( "\", Offset: " )._<NC>( ti.TrimOffset );
2244 buf._<NC>( ", Priority: " )._( ti.Priority );
2245 buf.NewLine();
2246 }
2247 }
2248
2249
2250 if ( cnt == 0 )
2251 buf._<NC>(" <no rules set>" ).NewLine();
2252 buf.NewLine();
2253 }
2254
2255 // domain substitutions
2256 if( HasBits( flags, StateInfo::DSR ) )
2257 {
2258 buf._<NC>( "Domain Substitution Rules: " ).NewLine();
2259 if( !impl->domainSubstitutions.empty() )
2260 {
2261 // get size
2262 integer maxWidth= 0;
2263 for ( auto& it : impl->domainSubstitutions )
2264 if ( maxWidth < it.Search.Length() )
2265 maxWidth = it.Search.Length();
2266 maxWidth+= 2;
2267
2268 // write
2269 for ( auto& it : impl->domainSubstitutions )
2270 {
2271 buf._<NC>( " " );
2272 if ( it.type == DomainSubstitutionRule::Type::EndsWith
2273 || it.type == DomainSubstitutionRule::Type::Substring )
2274 buf._<NC>( '*' );
2275
2276 buf._<NC>( it.Search );
2277 if ( it.type == DomainSubstitutionRule::Type::StartsWith
2278 || it.type == DomainSubstitutionRule::Type::Substring )
2279 buf._<NC>( '*' );
2280
2281 buf._<NC>( NTab( maxWidth, -1, 0 ) )
2282 ._<NC>( " -> " )
2283 ._<NC>( it.Replacement );
2284 buf.NewLine();
2285 }
2286 }
2287 else
2288 buf._<NC>(" <no rules set>" ).NewLine();
2289 buf.NewLine();
2290 }
2291
2292 // Log Once Counters
2293 if( HasBits( flags, StateInfo::Once ) )
2294 {
2295 buf._<NC>( "Once() Counters: " ).NewLine();
2296 if ( scopeDump.writeStoreMap( &impl->scopeLogOnce ) == 0 )
2297 buf._<NC>(" <no Once() counters set>" ).NewLine();
2298 buf.NewLine();
2299 }
2300
2301 // Log Data
2302 if( HasBits( flags, StateInfo::LogData ) )
2303 {
2304 buf._<NC>( "Log Data: " ).NewLine();
2305 if ( scopeDump.writeStoreMap( &impl->scopeLogData ) == 0 )
2306 buf._<NC>(" <no data objects stored>" ).NewLine();
2307 buf.NewLine();
2308 }
2309
2310 // Prefix Logables
2311 if( HasBits( flags, StateInfo::PrefixLogables ) )
2312 {
2313 buf._<NC>( "Prefix Logables: " ).NewLine();
2314 integer oldLength= buf.Length();
2315 scopeDump.writeStore( &impl->scopePrefixes, 2 );
2316 getStateCollectPrefixes( *impl->domains, 2, buf );
2317 if ( oldLength == buf.Length() )
2318 buf._<NC>(" <no prefix logables set>" ).NewLine();
2319 buf.NewLine();
2320 }
2321
2322 // thread mappings
2323 if( HasBits( flags, StateInfo::ThreadMappings ) )
2324 {
2325 #if !ALIB_SINGLE_THREADED
2326 buf._<NC>( "Named Threads: " ).NewLine();
2327 if ( impl->scopeInfo.threadDictionary.Size() == 0 )
2328 buf._<NC>(" <no thread name mappings set>" ).NewLine();
2329 else
2330 for ( auto& pair : impl->scopeInfo.threadDictionary )
2331 {
2332 buf._<NC>( " " ) << NField( String32() << '(' << pair.first << "):", 7, lang::Alignment::Left )
2333 << '\"' << pair.second << '\"';
2334 buf.NewLine();
2335 }
2336 buf.NewLine();
2337 #endif
2338 }
2339
2340 // Scope Domains
2341 if( HasBits( flags, StateInfo::ScopeDomains ) )
2342 {
2343 buf._<NC>( "Scope Domains: " ).NewLine();
2344 if ( scopeDump.writeStore( &impl->scopeDomains, 2 ) == 0 )
2345 buf._<NC>(" <no scope domains set>" ).NewLine();
2346 buf.NewLine();
2347 }
2348
2349 // Loggers
2350 if( HasBits( flags, StateInfo::Loggers ) )
2351 {
2352 TickConverter dateTimeConverter;
2353 std::vector<Domain*> domainsWithDiffVerb;
2354 for (int treeNo= 0; treeNo < 2; ++treeNo )
2355 {
2356 int cnt= 0;
2357 Domain* domTree;
2358 if( treeNo==0 )
2359 {
2360 domTree= impl->domains;
2361 buf._<NC>( "Loggers:" ).NewLine();
2362 }
2363 else
2364 {
2365 domTree= impl->internalDomains;
2366 buf._<NC>( "Loggers on Internal Domains:" ).NewLine();
2367 }
2368
2369 for ( int loggerNo= 0; loggerNo< domTree->CountLoggers(); ++loggerNo )
2370 {
2371 ++cnt;
2372 String64 as64;
2373 CalendarDateTime ct(lang::Initialization::Suppress);
2374
2375 Logger* logger= domTree->GetLogger(loggerNo);
2376 buf._<NC>( " " )._<NC>( *logger ).NewLine();
2377 buf._<NC>( " Lines logged: " )._<NC>( logger->CntLogs ).NewLine();
2378
2379 ct.Set( dateTimeConverter.ToDateTime(logger->TimeOfCreation) );
2380 buf._<NC>( " Creation time: " )._<NC>( ct.Format( A_CHAR("yyyy-MM-dd HH:mm:ss"), as64.Reset()) ).NewLine();
2381
2382 ct.Set( dateTimeConverter.ToDateTime(logger->TimeOfLastLog) );
2383 buf._<NC>( " Last log time: " )._<NC>( ct.Format( A_CHAR("yyyy-MM-dd HH:mm:ss"), as64.Reset()) ).NewLine();
2384
2385 domainsWithDiffVerb.clear();
2386 getStateDomainsWithDiffVerb( *domTree, loggerNo, domainsWithDiffVerb);
2387 for ( Domain* dom : domainsWithDiffVerb )
2388 {
2389 buf._<NC>(" ")
2390 ._( dom == *domainsWithDiffVerb.begin() ? "Verbosities: "
2391 : " " );
2392
2393 integer tabRef= buf.Length();
2394 buf << dom->FullPath << NTab( impl->maxDomainPathLength +1, tabRef);
2395
2396 buf << "= " << boxing::MakePair(dom->GetVerbosity( loggerNo ), dom->GetPriority(loggerNo) )
2397 << NEW_LINE;
2398 }
2399 }
2400 if ( cnt == 0 )
2401 buf._<NC>(" <no loggers attached>" ).NewLine();
2402 buf.NewLine();
2403 }
2404 }
2405
2406 // Internal Domains
2407 if( HasBits( flags, StateInfo::InternalDomains ) )
2408 {
2409 buf._<NC>( "Internal Domains:" ).NewLine();
2410 getStateDomainRecursive( *impl->internalDomains, impl->maxDomainPathLength, buf );
2411 buf.NewLine();
2412 }
2413
2414 // Domains
2415 if( HasBits( flags, StateInfo::Domains ) )
2416 {
2417 buf._<NC>( "Domains:" ).NewLine();
2418 getStateDomainRecursive( *impl->domains , impl->maxDomainPathLength, buf );
2419 buf.NewLine();
2420 }
2421}
2422
2423} // namespace alib::lox[::detail]
2424
2425
2426
2427}} // namespace [alib::lox]
2428# include "ALib.Lang.CIMethods.H"
2429
2430#undef UNDEFINED_THREAD
2431#undef ASSERT_ACQUIRED
2432
SharedConfiguration & GetConfig()
Definition camp.inl:215
bool IsBootstrapped()
Definition camp.inl:186
ALIB_DLL void SetBuffer(integer newCapacity)
bool ConsumeCharFromEnd(TChar consumable)
void Set(const TString< TChar > &src, TChar delimiter, bool skipEmptyTokens=false)
DbgLockAsserter Dbg
The debug tool instance.
Definition lock.inl:65
DateTime ToDateTime(Ticks ticks)
#define LOG_RELEASE
#define LOG_ACQUIRE
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
#define ALIB_LOCK_SHARED_WITH(lock)
Definition alib.inl:1324
#define IF_ALIB_THREADS(...)
Definition alib.inl:401
#define A_CHAR(STR)
#define ALIB_ASSERT(cond, domain)
Definition alib.inl:1048
#define ALIB_WARNING(domain,...)
Definition alib.inl:1046
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1050
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#define ALIB_LOCK_RECURSIVE_WITH(lock)
Definition alib.inl:1323
#define ALIB_DBG(...)
Definition alib.inl:836
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
#define ALIB_CALLER_PRUNED
Definition alib.inl:1007
#define ALIB_COMMA
Definition alib.inl:946
#define ALIB_LOCK_WITH(lock)
Definition alib.inl:1322
#define ALIB_REL_DBG(releaseCode,...)
Definition alib.inl:838
#define ALIB_CHARACTERS_WIDE
Definition alib.inl:871
void Raise(const lang::CallerInfo &ci, int type, std::string_view domain, TArgs &&... args)
Definition assert.inl:181
const TChar * Search(const TChar *haystack, integer haystackLength, TChar needle)
integer Length(const TChar *cstring)
Definition functions.inl:91
std::conditional_t< ArrayTraits< T, nchar >::Access !=Policy::NONE, nchar, std::conditional_t< ArrayTraits< T, wchar >::Access !=Policy::NONE, wchar, std::conditional_t< ArrayTraits< T, xchar >::Access !=Policy::NONE, xchar, void > > > Type
constexpr bool HasBits(TEnum element, TEnum selection) noexcept
Definition bitwise.inl:336
@ Add
Arithmetic addition ('+'). Precedence 800.
platform_specific integer
Definition integers.inl:32
integer ThreadID
The ALib thread identifier type.
Definition thread.inl:23
strings::TEscape< character > Escape
Type alias in namespace alib.
Definition format.inl:536
threads::Lock Lock
Type alias in namespace alib.
Definition lock.inl:132
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128>.
CompilationFlagMeaningsEntry COMPILATION_FLAG_MEANINGS[40]
NLocalString< 64 > NString64
Type alias name for TLocalString<nchar,64>.
strings::TTab< character > Tab
Type alias in namespace alib.
Definition format.inl:516
LocalString< 256 > String256
Type alias name for TLocalString<character,256>.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
unsigned char REVISION
strings::util::TTokenizer< character > Tokenizer
Type alias in namespace alib.
variables::Variable Variable
Type alias in namespace alib.
LocalString< 128 > String128
Type alias name for TLocalString<character,128>.
time::DateTime DateTime
Type alias in namespace alib.
Definition datetime.inl:224
LocalString< 64 > String64
Type alias name for TLocalString<character,64>.
constexpr CString NEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.inl:644
strings::TSubstring< nchar > NSubstring
Type alias in namespace alib.
monomem::TPoolAllocator< MonoAllocator > PoolAllocator
strings::TString< nchar > NString
Type alias in namespace alib.
Definition string.inl:2390
strings::TAString< nchar, lang::HeapAllocator > NAString
Type alias in namespace alib.
boxing::TBoxes< PoolAllocator > BoxesPA
Type alias in namespace alib.
Definition boxes.inl:248
monomem::TMonoAllocator< lang::HeapAllocator > MonoAllocator
threads::Thread Thread
Type alias in namespace alib.
Definition thread.inl:389
strings::TTab< nchar > NTab
Type alias in namespace alib.
Definition format.inl:519
int VERSION
strings::TCString< nchar > NCString
Type alias in namespace alib.
Definition cstring.inl:512
strings::util::CalendarDateTime CalendarDateTime
Type alias in namespace alib.
Definition calendar.inl:647
LocalString< 32 > String32
Type alias name for TLocalString<character,32>.
lox::ALoxCamp ALOX
The singleton instance of ALib Camp class ALoxCamp.
Definition aloxcamp.cpp:53
boxing::Box Box
Type alias in namespace alib.
Definition box.inl:1216
threads::RecursiveLock RecursiveLock
Type alias in namespace alib.
boxing::TBoxes< MonoAllocator > BoxesMA
Type alias in namespace alib.
Definition boxes.inl:245
strings::TField< nchar > NField
Type alias in namespace alib.
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2381
NLocalString< 512 > NString512
Type alias name for TLocalString<nchar,512>.
boxing::TBoxes< lang::HeapAllocator > BoxesHA
Type alias in namespace alib.
Definition boxes.inl:241
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256>.
TCompilationFlags COMPILATION_FLAGS
NLocalString< 32 > NString32
Type alias name for TLocalString<nchar,32>.
time::TickConverter TickConverter
Type alias in namespace alib.
strings::TSubstring< character > Substring
Type alias in namespace alib.
lox::detail::Logger Logger
Type alias in namespace alib.
Definition logger.inl:157
containers::List< TAllocator, T, TRecycling > List
Type alias in namespace alib.
Definition list.inl:746
lox::textlogger::TextLogger TextLogger
Type alias in namespace alib.
unsigned char bits[5]
Definition lang.mpp:62