ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
textlogger.cpp
1// #################################################################################################
2// alib::lox::detail - ALox Logging Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !DOXYGEN
11# define HPP_ALIB_LOX_PROPPERINCLUDE
14# undef HPP_ALIB_LOX_PROPPERINCLUDE
15
17# include "alib/alox/aloxcamp.hpp"
22# if ALIB_THREADS
23# include "alib/threads/lock.hpp"
24# endif
25#endif // !DOXYGEN
26
27namespace alib::lox::textlogger {
28
29// #################################################################################################
30// StandardConverter
31// #################################################################################################
33{
34 auto* firstLevelFormatter= new FormatterPythonStyle();
35 firstLevelFormatter->Next.InsertDerived<FormatterJavaStyle>();
36
37 Formatters.emplace_back( firstLevelFormatter );
38 cntRecursion= -1;
39}
40
42{
43 ALIB_ASSERT_ERROR( cntRecursion == -1, "ALOX",
44 "ALox object converter recursion counter > 0.\n"
45 "Note: This error indicates, that a previous format operation (log statement) contained\n"
46 " corrupt format values, which caused the formatter to behave undefined, including\n"
47 " the corruption of the execution stack of ALox logging." )
48 for( auto* elem : Formatters )
49 delete elem;
50}
51
53{
55
57
58 // get a formatter. We use a clone per recursion depth!
59 // So, did we have this depth already before? If not, create a new set of formatters formatter
60 if( size_t( cntRecursion ) >= Formatters.size() )
61 {
62 // create a pair of recursion formatters
63 Formatter* recursionFormatter= new FormatterPythonStyle();
64 recursionFormatter->Next.InsertDerived<FormatterJavaStyle>();
65 recursionFormatter->CloneSettings( *Formatters[0] );
66 Formatters.emplace_back( recursionFormatter );
67 }
68
69 Formatter* formatter= Formatters[size_t( cntRecursion )];
70
71 try
72 {
73 formatter->FormatArgs( target, logables );
74 }
75 catch(Exception& e )
76 {
77 target << ALOX.GetResource("TLFmtExc");
78 ALIB_LOCK_RECURSIVE_WITH( lang::format::Formatter::DefaultLock )
79 e.Format( target );
80 }
81
82 --cntRecursion;
83}
84
85void StandardConverter::SetAutoSizes( AutoSizes* autoSizes )
86{
87 FormatterPythonStyle* fmtPS= dynamic_cast<FormatterPythonStyle*>( Formatters[0] );
88 if (fmtPS != nullptr )
89 fmtPS->Sizes= autoSizes;
90}
91
92AutoSizes* StandardConverter::GetAutoSizes()
93{
94 FormatterPythonStyle* fmtPS= dynamic_cast<FormatterPythonStyle*>( Formatters[0] );
95 if (fmtPS != nullptr )
96 return fmtPS->Sizes;
97 return nullptr;
98}
99
100void StandardConverter::ResetAutoSizes()
101{
102 FormatterPythonStyle* fmtPS;
103 for( auto* elem : Formatters )
104 if ( (fmtPS= dynamic_cast<FormatterPythonStyle*>( elem )) != nullptr )
105 fmtPS->Sizes->Reset();
106}
107
108// #################################################################################################
109// MetaInfo
110// #################################################################################################
111void TextLogger::writeMetaInfo( AString& buf, detail::Domain& domain, Verbosity verbosity,
112 detail::ScopeInfo& scope )
113{
114 // check
115 auto& fmt= varFormatMetaInfo.Get<FormatMetaInfo>();
116 if ( fmt.Format.IsEmpty() )
117 return;
118
119 // clear DateTime singleton
120 callerDateTime.Year= (std::numeric_limits<int>::min)();
121
122 Substring format( fmt.Format );
123 for(;;)
124 {
125 // get next and log substring between commands
126 integer idx= format.IndexOf( '%' );
127 if ( idx >= 0 )
128 {
129 format.ConsumeChars<NC, lang::CurrentData::Keep>( idx, buf, 1 );
130 processVariable( domain.FullPath, verbosity, scope, buf, format );
131 }
132 else
133 {
134 buf._<NC>( format );
135 break;
136 }
137 }
138}
139
140void TextLogger::processVariable( const NString& domainPath,
141 Verbosity verbosity,
142 detail::ScopeInfo& scope,
143 AString& dest,
144 Substring& variable )
145
146{
147 // process commands
148 auto& fmt= varFormatMetaInfo .Get<FormatMetaInfo>();
149 auto& autoSizes= varFormatAutoSizes.Get<FormatAutoSizes>();
150 character c2;
151 switch ( variable.ConsumeChar() )
152 {
153 // scope info
154 case 'S':
155 {
156 // read sub command
157 NString val;
158 switch( c2= variable.ConsumeChar() )
159 {
160 case 'P': // SP: full path
161 {
162 val= scope.GetFullPath();
163 if ( val.IsEmpty() )
164 val= GetFormatOther().NoSourceFileInfo;
165 } break;
166
167 case 'p': // Sp: trimmed path
168 {
169 integer previousLength= dest.Length();
170 scope.GetTrimmedPath( dest );
171 if( dest.Length() != previousLength )
172 return;
173 val= GetFormatOther().NoSourceFileInfo;
174 } break;
175
176 case 'F': // file name
177 {
178 val= scope.GetFileName();
179 if ( val.IsEmpty() )
180 val= GetFormatOther().NoSourceFileInfo;
181 } break;
182
183 case 'f': // file name without extension
184 {
185 val= scope.GetFileNameWithoutExtension();
186 if ( val.IsEmpty() )
187 val= GetFormatOther().NoSourceFileInfo;
188 } break;
189
190
191 case 'M': // method name
192 {
193 val= scope.GetMethod();
194 if ( val.IsEmpty() )
195 val= GetFormatOther().NoMethodInfo;
196 } break;
197
198 case 'L': // line number
199 {
200 dest._<NC>( scope.GetLineNumber() );
201 return;
202 }
203
204 default:
205 {
206 ALIB_ASSERT_WARNING( FormatWarningOnce, "ALOX",
207 "Unknown format variable '%S{}' (only one warning)", c2 )
208 ALIB_DBG( FormatWarningOnce= true; )
209 val= "%ERROR";
210 } break;
211 }
212 dest._( val );
213 return;
214 }
215
216 // %Tx: Time
217 case 'T':
218 {
219 c2= variable.ConsumeChar();
220
221 // %TD: Date
222 if ( c2 == 'D' )
223 {
224 // get time stamp as CalendarDateTime once
225 if ( callerDateTime.Year == (std::numeric_limits<int>::min)() )
226 callerDateTime.Set( DateConverter.ToDateTime( scope.GetTimeStamp() ) );
227
228 // if standard format, just write it out
229 if ( GetFormatDate().Date.Equals<NC>( A_CHAR("yyyy-MM-dd") ) )
230 {
231 dest._<NC>( alib::Format( callerDateTime.Year, 4 ) )._<NC>( '-' )
232 ._<NC>( alib::Format( callerDateTime.Month, 2 ) )._<NC>( '-' )
233 ._<NC>( alib::Format( callerDateTime.Day, 2 ) );
234 }
235 // user-defined format
236 else
237 callerDateTime.Format( GetFormatDate().Date, dest );
238
239 return;
240 }
241
242
243 // %TT: Time of Day
244 if ( c2 == 'T' )
245 {
246 // get time stamp as CalendarDateTime once
247 if ( callerDateTime.Year == (std::numeric_limits<int>::min)() )
248 callerDateTime.Set( DateConverter.ToDateTime( scope.GetTimeStamp() ) );
249
250 // avoid the allocation of a) a StringBuilder (yes, a string builder is allocated inside StringBuilder.AppendFormat!)
251 // and b) a DateTime object, if the format is the unchanged standard. And it is faster anyhow.
252 if ( GetFormatDate().TimeOfDay.Equals<NC>( A_CHAR("HH:mm:ss") ) )
253 {
254 dest._<NC>( alib::Format(callerDateTime.Hour, 2) )._<NC>( ':' )
255 ._<NC>( alib::Format(callerDateTime.Minute, 2) )._<NC>( ':' )
256 ._<NC>( alib::Format(callerDateTime.Second, 2) );
257 }
258
259 // user-defined format
260 else
261 callerDateTime.Format( GetFormatDate().TimeOfDay, dest );
262 }
263
264 // %TC: Time elapsed since created
265 else if ( c2 == 'C' )
266 {
267 auto elapsedTime= scope.GetTimeStamp() - TimeOfCreation;
268 auto elapsedSecs= elapsedTime.InAbsoluteSeconds();
269 CalendarDuration elapsed( elapsedTime );
270
271 // determine number of segments to write and match this to recent (autosizes) value
272 int timeSize= elapsedSecs >= 24*3600 ? 6
273 : elapsedSecs >= 10*3600 ? 5
274 : elapsedSecs >= 3600 ? 4
275 : elapsedSecs >= 10*60 ? 3
276 : elapsedSecs >= 60 ? 2
277 : elapsedSecs >= 9 ? 1
278 : 0;
279 timeSize= int(autoSizes.Main.Next( AutoSizes::Types::Field, timeSize, 0 ));
280
281
282 if ( timeSize >= 4 ) dest._<NC>( elapsed.Days )._<NC>( GetFormatDate().ElapsedDays );
283 if ( timeSize >= 3 ) dest._<NC>( alib::Format(elapsed.Hours , timeSize >= 5 ? 2 : 1 ) )._<NC>( ':' );
284 if ( timeSize >= 2 ) dest._<NC>( alib::Format(elapsed.Minutes, timeSize >= 3 ? 2 : 1 ) )._<NC>( ':' );
285 dest._<NC>( alib::Format(elapsed.Seconds, timeSize >= 1 ? 2 : 1) )._<NC>( '.' );
286 dest._<NC>( alib::Format(elapsed.Milliseconds, 3) );
287 }
288
289 // %TL: Time elapsed since last log call
290 else if ( c2 == 'L' )
291 writeTimeDiff( dest, scope.GetTimeStamp().Since( TimeOfLastLog ).InNanoseconds() );
292
293 else
294 {
295 ALIB_ASSERT_WARNING( FormatWarningOnce, "ALOX",
296 "Unknown format variable '%T{}' (only one warning)", c2 )
297 ALIB_DBG( FormatWarningOnce= true; )
298 }
299 return;
300 }
301
302
303 // Thread
304 case 't':
305 {
306 c2= variable.ConsumeChar();
307
308 if ( c2 == 'N' ) // %tN: thread name
309 {
310 #if ALIB_THREADS
311 const String& threadName= scope.GetThreadNameAndID(nullptr);
312 #else
313 String msg( A_CHAR("SINGLE_THREADED") );
314 const String& threadName= msg;
315 #endif
316 dest._<NC>( Format::Field( threadName,
317 autoSizes.Main.Next(
318 AutoSizes::Types::Field, threadName.Length(), 0),
319 lang::Alignment::Center ) );
320 }
321 else if ( c2 == 'I' ) // %tI: thread ID
322 {
323 String32 threadID;
324 #if ALIB_THREADS
325 threadID._( scope.GetThreadID() );
326 #else
327 threadID << "-1";
328 #endif
329 dest._<NC>( Format::Field( threadID,
330 autoSizes.Main.Next(
331 AutoSizes::Types::Field, threadID .Length(), 0),
332 lang::Alignment::Center ) );
333 }
334 else
335 {
336 ALIB_ASSERT_WARNING( FormatWarningOnce, "ALOX",
337 "Unknown format variable '%t{}' (only one warning)", c2 )
338 ALIB_DBG( FormatWarningOnce= true; )
339 }
340 return;
341 }
342
343 case 'L':
344 {
345 c2= variable.ConsumeChar();
346 if ( c2 == 'G' ) dest._<NC>( GetName() );
347 else if ( c2 == 'X' ) dest._<NC>( scope.GetLoxName() );
348 else
349 {
350 ALIB_ASSERT_WARNING( FormatWarningOnce, "ALOX",
351 "Unknown format variable '%L{}' (only one warning)", c2 )
352 ALIB_DBG( FormatWarningOnce= true; )
353 }
354 return;
355 }
356
357 case 'P':
358 {
359 dest._<NC>( ProcessInfo::Current().Name );
360 return;
361 }
362
363 case 'V':
364 dest._<NC>( verbosity == Verbosity::Error ? fmt.VerbosityError
365 : verbosity == Verbosity::Warning ? fmt.VerbosityWarning
366 : verbosity == Verbosity::Info ? fmt.VerbosityInfo
367 : fmt.VerbosityVerbose );
368 return;
369
370 case 'D':
371 {
372 dest._( Format::Field( domainPath,
373 autoSizes.Main.Next(
374 AutoSizes::Types::Field, domainPath.Length(), 0 ),
375 lang::Alignment::Left ) );
376 return;
377 }
378
379 case '#':
380 dest._<NC>( alib::Format( CntLogs, GetFormatOther().LogNumberMinDigits ) );
381 return;
382
383 // A: Auto tab
384 case 'A':
385 {
386 // read extra space from format string
387 integer extraSpace;
388 if( !variable.ConsumeDecDigits( extraSpace ) )
389 extraSpace= 1;
390 integer currentLength= dest.WStringLength();
391 integer tabPos= autoSizes.Main.Next(
392 AutoSizes::Types::Tabstop, currentLength, extraSpace);
393 dest.InsertChars(' ', tabPos - currentLength );
394
395 return;
396 }
397
398 case 'N':
399 dest._<NC>( GetName() );
400 return;
401
402 default:
403 ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
404 ALIB_ASSERT_WARNING( FormatWarningOnce, "ALOX",
405 "Unknown format character {!Q'} (only one warning)",
406 *( variable.Buffer() -1 ) )
407 ALIB_DBG( FormatWarningOnce= true; )
408 ALIB_WARNINGS_RESTORE
409 return;
410 }// switch
411}
412
413void TextLogger::writeTimeDiff( AString& buf, int64_t diffNanos )
414{
415 auto& td= GetFormatTimeDiff();
416
417 // unmeasurable?
418 if ( diffNanos < td.Minimum )
419 {
420 buf._<NC>( td.None );
421 return;
422 }
423
424 if ( diffNanos < 1000 )
425 {
426 buf._<NC>( alib::Format( diffNanos, 3 ) )._<NC>( td.Nanos );
427 return;
428 }
429
430 // we continue with micros
431 int64_t diffMicros= diffNanos / 1000L;
432
433 // below 1000 microseconds?
434 if ( diffMicros < 1000 )
435 {
436 buf._<NC>( alib::Format( diffMicros, 3 ) );
437 buf._<NC>( td.Micros );
438 return;
439 }
440
441 // below 1000 ms?
442 if ( diffMicros < 1000000 )
443 {
444 buf._<NC>( alib::Format( (diffMicros / 1000), 3 ) )._<NC>( td.Millis );
445 return;
446 }
447
448
449 // below 10 secs (rounded) ?
450 if ( diffMicros < 9995000 )
451 {
452 // convert to hundredth of secs
453 int64_t hundredthSecs= ((diffMicros / 1000) + 5) / 10;
454
455 // print two digits after dot x.xx
456 buf._<NC>( alib::Format( (hundredthSecs / 100), 1 ) )
457 ._<NC>( '.' )
458 ._<NC>( alib::Format( (hundredthSecs % 100), 2 ) )
459 ._<NC>( td.Secs );
460 return;
461 }
462
463 // convert to tenth of secs
464 int64_t tenthSecs= ((diffMicros / 10000) + 5) / 10 ;
465
466 // below 100 secs ?
467 if ( tenthSecs < 1000 )
468 {
469 // print one digits after dot xx.x (round value by adding 5 hundredth)
470 buf._<NC>( alib::Format( ( tenthSecs / 10 ), 2 ) )
471 ._<NC>( '.' )
472 ._<NC>( alib::Format( ( tenthSecs % 10 ), 1 ) )
473 ._<NC>( td.Secs );
474 return;
475 }
476
477 // below 10 mins ?
478 if ( tenthSecs < 6000 )
479 {
480 // convert to hundredth of minutes
481 int64_t hundredthMins= tenthSecs / 6;
482
483 // print two digits after dot x.xx
484 buf._<NC>( alib::Format( (hundredthMins / 100), 1 ) )
485 ._<NC>( '.' )
486 ._<NC>( alib::Format( (hundredthMins % 100), 2 ) )
487 ._<NC>( td.Mins );
488 return;
489 }
490
491 // convert to tenth of minutes
492 int64_t tenthMins= tenthSecs / 60;
493
494 // below 100 mins ?
495 if ( tenthMins < 1000 )
496 {
497 // print one digits after dot xx.x (round value by adding 5 hundredth)
498 buf._<NC>( alib::Format( (tenthMins / 10), 2 ) )
499 ._<NC>( '.' )
500 ._<NC>( alib::Format( (tenthMins % 10), 1 ) )
501 ._<NC>( td.Mins );
502 return;
503 }
504
505 // below ten hours?
506 if ( tenthMins < 6000 )
507 {
508 // convert to hundredth of hours
509 int64_t hundredthHours= tenthMins / 6;
510
511 // print two digits after dot x.xx
512 buf._<NC>( alib::Format( (hundredthHours / 100), 1 ) )
513 ._<NC>( '.' )
514 ._<NC>( alib::Format( (hundredthHours % 100), 2 ))
515 ._<NC>( td.Hours );
516 return;
517 }
518
519 // convert to tenth of minutes
520 int64_t tenthHours= tenthMins / 60;
521
522 // below 10 hours ?
523 if ( tenthHours < 1000 )
524 {
525 // print two digits after dot x.xx
526 buf._<NC>( alib::Format( (tenthHours / 10), 2 ) )
527 ._<NC>( '.' )
528 ._<NC>( alib::Format( (tenthHours % 10), 1 ) )
529 ._<NC>( td.Hours );
530 return;
531 }
532
533 // below 100 hours ?
534 if ( tenthHours < 1000 )
535 {
536 // print one digits after dot xx.x (round value by adding 5 hundredth)
537 buf._<NC>( alib::Format( (tenthHours / 10), 2 ) )
538 ._<NC>( '.' )
539 ._<NC>( alib::Format( ((tenthHours / 10) % 10), 1 ) )
540 ._<NC>( td.Hours );
541 return;
542 }
543
544 // convert to hundredth of days
545 int64_t hundredthDays= tenthHours * 10 / 24;
546
547 // below 10 days ?
548 if ( hundredthDays < 1000 )
549 {
550 // print two digits after dot x.xx
551 buf._<NC>( alib::Format( (hundredthDays / 100), 1 ) )
552 ._<NC>( '.' )
553 ._<NC>( alib::Format( (hundredthDays % 100), 2 ) )
554 ._<NC>( td.Days );
555 return;
556 }
557
558 // 10 days or more (print days plus one digit after the comma)
559 // print one digits after dot xx.x (round value by adding 5 hundredth)
560 buf ._<NC>( alib::Format( (hundredthDays / 100), 2 ) )
561 ._<NC>( '.' )
562 ._<NC>( alib::Format( ((hundredthDays / 10) % 10), 1 ) )
563 ._<NC>( td.Days );
564}
565
566
567// #################################################################################################
568// TextLogger
569// #################################################################################################
570TextLogger::TextLogger( const NString& pName, const NString& typeName, bool pUsesStdStreams )
571: Logger( pName, typeName )
572, usesStdStreams( pUsesStdStreams )
573, varFormatMetaInfo (alib::ALOX)
574, varFormatDateTime (alib::ALOX)
575, varFormatTimeDiff (alib::ALOX)
576, varFormatMultiLine(alib::ALOX)
577, varFormatOther (alib::ALOX)
578, varFormatAutoSizes(alib::ALOX)
579, varReplacements (alib::ALOX)
580{
581 logBuf.SetBuffer( 256 );
582 msgBuf.SetBuffer( 256 );
583}
584
585TextLogger::~TextLogger()
586{
587 if (Converter)
588 delete Converter;
589 ALIB_ASSERT( msgBuf.IsEmpty() )
590}
591
592void TextLogger::AcknowledgeLox( detail::LoxImpl* , lang::ContainerOp op )
593{
594 // --------------- insert ------------------
595 if( op == lang::ContainerOp::Insert )
596 {
597 if ( Converter == nullptr )
598 Converter= new textlogger::StandardConverter();
599
600 // Variable AUTO_SIZES: use last session's values
602 varFormatAutoSizes.Declare(Variables::AUTO_SIZES, Name );
603 (void) varFormatAutoSizes.Define();
604 Converter->SetAutoSizes( &varFormatAutoSizes.Get<FormatAutoSizes>().LogMessage );
605 }
606
607 // Variable <name>_FORMAT / <typeName>_FORMAT:
609 const Declaration* variableDecl= Declaration::Get( Variables::FORMAT );
610 const Declaration* privateDecl= ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
611
612 if( !varFormatMetaInfo.Try( privateDecl )
613 && !varFormatMetaInfo.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName() ) ) )
614 {
615 varFormatMetaInfo.Declare( privateDecl );
616 ALIB_ASSERT_ERROR(varFormatMetaInfo.IsDefined(), "ALOX",
617 "Mandatory (usually resourced) default value is missing for variable {!Q}.",
618 varFormatMetaInfo)
619 }
620 }
621
622 // Variable <name>_FORMAT_DATE_TIME / <typeName>_FORMAT_DATE_TIME:
624 auto* variableDecl= Declaration::Get( Variables::FORMAT_DATE_TIME );
625 auto* privateDecl= ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
626 if( !varFormatDateTime.Try( privateDecl )
627 && !varFormatDateTime.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName() ) ) )
628 {
629 varFormatDateTime.Declare( privateDecl );
630
631 ALIB_ASSERT_ERROR(varFormatDateTime.IsDefined(), "ALOX",
632 "Mandatory (usually resourced) default value is missing for variable {!Q}.",
633 varFormatDateTime)
634 }
635 }
636
637 // Variable <name>FORMAT_TIME_DIFF / <typeName>FORMAT_TIME_DIFF:
639 auto* variableDecl= Declaration::Get( Variables::FORMAT_TIME_DIFF );
640 auto* privateDecl= ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
641 if( !varFormatTimeDiff.Try( privateDecl )
642 && !varFormatTimeDiff.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName() ) ) )
643 {
644 varFormatTimeDiff.Declare( privateDecl );
645 ALIB_ASSERT_ERROR(varFormatTimeDiff.IsDefined(), "ALOX",
646 "Mandatory (usually resourced) default value is missing for variable {!Q}.",
647 varFormatTimeDiff)
648 }
649 }
650
651 // Variable <name>FORMAT_MULTILINE / <typeName>FORMAT_MULTILINE:
653 auto* variableDecl= Declaration::Get( Variables::FORMAT_MULTILINE );
654 auto* privateDecl= ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
655 if( !varFormatMultiLine.Try( privateDecl )
656 && !varFormatMultiLine.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName() ) ) )
657 {
658 varFormatMultiLine.Declare( privateDecl );
659 ALIB_ASSERT_ERROR(varFormatMultiLine.IsDefined(), "ALOX",
660 "Mandatory (usually resourced) default value is missing for variable {!Q}.",
661 varFormatMultiLine)
662 }
663 }
664
665 // Variable <name>FORMAT_OTHER / <typeName>FORMAT_OTHER:
667 auto* variableDecl= Declaration::Get( Variables::FORMAT_OTHER );
668 auto* privateDecl= ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
669 if( !varFormatOther.Try( privateDecl )
670 && !varFormatOther.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName() ) ) )
671 {
672 varFormatOther.Declare( privateDecl );
673 ALIB_ASSERT_ERROR(varFormatOther.IsDefined(), "ALOX",
674 "Mandatory (usually resourced) default value is missing for variable {!Q}.",
675 varFormatOther)
676 }
677 }
678
679 // Variable <name>FORMAT_REPLACEMENTS / <typeName>FORMAT_REPLACEMENTS:
681 auto* variableDecl= Declaration::Get( Variables::REPLACEMENTS );
682 auto* privateDecl = ALOX.GetConfig().StoreDeclaration(variableDecl, GetName() );
683 if( !varReplacements.Try( privateDecl)
684 && !varReplacements.Try( ALOX.GetConfig().StoreDeclaration(variableDecl, GetTypeName())) )
685 {
686 varReplacements.Declare(privateDecl);
687 }
688
689 // if not defined, create the empty variable. For one, this way it is not needed to be
690 // checked before access, and furthermore this allows it to appear in config files.
691 if( !varReplacements.IsDefined() )
692 (void) varReplacements.Define(Priority::DefaultValues - 1);
693 }
694 }
695}
696
697
698void TextLogger::SetReplacement( const String& searched, const String& replacement )
699{
700 auto& replacements= GetReplacements().Pairs;
701 // if exists, replace replacement
702 for( auto it= replacements.begin(); it < replacements.end(); it+= 2)
703 if ( it->Equals<NC>( searched ) )
704 {
705 if ( replacement.IsNotNull() )
706 {
707 ++it;
708 (*it).Reset( replacement );
709 return;
710 }
711
712 replacements.erase( it );
713 replacements.erase( it );
714 return;
715 }
716
717 // append at the end
718 if ( replacement.IsNotNull() )
719 {
720 replacements.insert( replacements.end(), AStringPA(replacements.get_allocator().GetAllocator()) );
721 replacements.back() << searched;
722 replacements.insert( replacements.end(), AStringPA(replacements.get_allocator().GetAllocator()) );
723 replacements.back() << replacement;
724 }
725}
726
731
736
737
738void TextLogger::Log( detail::Domain& domain, Verbosity verbosity, BoxesMA& logables,
739 detail::ScopeInfo& scope )
740{
741 // we store the current msgBuf length and reset the buffer to this length when exiting.
742 // This allows recursive calls! Recursion might happen with the evaluation of the
743 // logables (in the next line).
744 StringLengthResetter msgBufResetter(msgBuf);
745 Converter->ConvertObjects( msgBuf, logables );
746
747 // replace strings in message
748 auto& replacements= GetReplacements().Pairs;
749 for ( size_t i= 0; i < replacements.size() ; i+= 2 )
750 msgBuf.SearchAndReplace( replacements[i],
751 replacements[i + 1], msgBufResetter.OriginalLength() );
752
753 // get auto-sizes and set write protected in case someone wrote the variable with higher
754 // priority than we do.
755 auto& autoSizes= varFormatAutoSizes.Get<FormatAutoSizes>();
756 if( varFormatAutoSizes.GetPriority() > Priority::Session )
757 autoSizes.Main .WriteProtected=
758 autoSizes.LogMessage.WriteProtected= true;
759
760 // clear log buffer and write meta info
761 logBuf.Reset();
762 autoSizes.Main.Restart();
763 writeMetaInfo( logBuf, domain, verbosity, scope );
765
766 // check for empty messages
768 if ( msgBuf.Length() == msgBufResetter.OriginalLength() )
769 {
770 // log empty msg and quit
771 logBuf._<NC>( fmt.MsgSuffix );
772 if ( usesStdStreams )
774 logText( domain, verbosity, logBuf, scope, -1 );
775 }
776 else
777 logText( domain, verbosity, logBuf, scope, -1 );
778 return;
779 }
780
781 // single line output
782 auto& multiLine= varFormatMultiLine.Get<FormatMultiLine>();
783 if ( multiLine.Mode == 0 )
784 {
785 // replace line separators
786 integer cntReplacements=0;
787 if ( multiLine.Delimiter.IsNotNull() )
788 cntReplacements+= msgBuf.SearchAndReplace( multiLine.Delimiter, multiLine.DelimiterReplacement, msgBufResetter.OriginalLength() );
789 else
790 {
791 String replacement= multiLine.DelimiterReplacement;
792 cntReplacements+= msgBuf.SearchAndReplace( A_CHAR("\r\n"), replacement, msgBufResetter.OriginalLength() );
793 cntReplacements+= msgBuf.SearchAndReplace( A_CHAR("\r"), replacement, msgBufResetter.OriginalLength() );
794 cntReplacements+= msgBuf.SearchAndReplace( A_CHAR("\n"), replacement, msgBufResetter.OriginalLength() );
795 }
796
797 // append msg to logBuf
798 if ( cntReplacements == 0 )
799 {
800 logBuf._<NC>( msgBuf, msgBufResetter.OriginalLength(), msgBuf.Length() - msgBufResetter.OriginalLength() );
801 }
802 else
803 {
804 logBuf._<NC>( multiLine.Prefix );
805 logBuf._<NC>( msgBuf, msgBufResetter.OriginalLength(), msgBuf.Length() - msgBufResetter.OriginalLength() );
806 logBuf._<NC>( multiLine.Suffix );
807 }
808 logBuf._<NC>( fmt.MsgSuffix );
809
810 // now do the logging by calling our derived class's logText
811 if ( usesStdStreams )
813 logText( domain, verbosity, logBuf, scope, -1 );
814 }
815 else
816 logText( domain, verbosity, logBuf, scope, -1 );
817 }
818
819 // multiple line output
820 auto prevIndex= autoSizes.Main.ActualIndex;
821 integer actStart= msgBufResetter.OriginalLength();
822 int lineNo= 0;
823 integer lbLenBeforeMsgPart= logBuf.Length();
824
825 // loop over lines in msg
826 while ( actStart < msgBuf.Length() )
827 {
828 // find next end
829 integer delimLen;
830 integer actEnd;
831
832 // no delimiter given: search '\n' and then see if it is "\r\n" in fact
833 if (multiLine.Delimiter.IsEmpty() )
834 {
835 delimLen= 1;
836
837 actEnd= msgBuf.IndexOf<NC>( '\n', actStart );
838 if( actEnd > actStart )
839 {
840 if( msgBuf.CharAt<NC>(actEnd - 1) == '\r' )
841 {
842 --actEnd;
843 delimLen= 2;
844 }
845 }
846 }
847 else
848 {
849 delimLen= multiLine.Delimiter.Length();
850 actEnd= msgBuf.IndexOf<NC>( multiLine.Delimiter, actStart );
851 }
852
853 // not found a delimiter? - log the rest
854 if ( actEnd < 0 )
855 {
856 // single line?
857 if ( lineNo == 0 )
858 {
859 logBuf._<NC>( msgBuf, msgBufResetter.OriginalLength(), msgBuf.Length() - msgBufResetter.OriginalLength() );
860 logBuf._<NC>( fmt.MsgSuffix );
861
862 if ( usesStdStreams )
864 logText( domain, verbosity, logBuf, scope, -1 );
865 }
866 else
867 logText( domain, verbosity, logBuf, scope, -1 );
868
869 // stop here
870 goto LOG_END;
871 }
872
873 // store actual end
874 actEnd= msgBuf.Length();
875 }
876
877 // found a delimiter
878
879 // signal start of multi line log
880 if ( lineNo == 0 )
881 {
882 if ( usesStdStreams)
885 }
886 else
888 }
889
890 // in mode 3, 4, meta info is deleted
891 if ( lineNo == 0 && ( multiLine.Mode == 3 || multiLine.Mode == 4 ) )
892 {
893 // log headline in mode 3
894 if ( multiLine.Mode == 3 )
895 {
896 logBuf._<NC>( multiLine.Headline );
897 autoSizes.Main.ActualIndex= prevIndex;
898 logText( domain, verbosity, logBuf, scope, 0 );
899 }
900
901 // remember zero as offset
902 lbLenBeforeMsgPart= 0;
903 }
904
905 // clear meta-information?
906 if ( multiLine.Mode == 2 )
907 {
908 if (lineNo != 0 )
909 {
911 autoSizes.Main.ActualIndex= prevIndex;
912 }
913 }
914 // reset logBuf length to marked position
915 else
916 {
917 logBuf.ShortenTo( lbLenBeforeMsgPart );
918 autoSizes.Main.ActualIndex= prevIndex;
919 }
920
921 // append message and do the log
922 logBuf._<NC>( multiLine.Prefix );
923 logBuf._<NC>( msgBuf, actStart, actEnd - actStart );
924 logBuf._<NC>( multiLine.Suffix );
925 actStart= actEnd + delimLen;
926 if ( actStart >= msgBuf.Length() )
927 logBuf._<NC>( fmt.MsgSuffix );
928 logText( domain, verbosity, logBuf, scope, lineNo );
929
930 // next
931 ++lineNo;
932 }
933
934 // signal end of multi line log
935 if ( lineNo > 0 )
936 {
937 if ( usesStdStreams)
940 }
941 else
943 }
944
945 LOG_END:
946 // In case of changes, re-define the auto-sizes variable.
947 // This might trigger a listener that monitors this session-type variable
948 if( autoSizes.Main .IsChanged()
949 || autoSizes.LogMessage.IsChanged() )
950 (void) varFormatAutoSizes.Define( Priority::Session );
951} // TextLogger::Log()
952
953} // namespace [alib::lox::textlogger]
954
955
ALIB_API bool Define(Priority requestedPriority=Priority::Standard)
Definition variable.cpp:272
Priority GetPriority() const
threads::SharedLock & GetConfigLock()
Definition camp.hpp:219
static constexpr character EOMETA[4]
End of meta-information in log string.
virtual void ConvertObjects(AString &target, BoxesMA &logables)=0
ALIB_API StandardConverter()
Constructor.
std::vector< Formatter * > Formatters
virtual ALIB_API void ConvertObjects(AString &target, BoxesMA &logables) override
virtual ALIB_API ~StandardConverter() override
Virtual destructor.
int cntRecursion
A counter to detect recursive calls.
virtual ALIB_API void ClearReplacements()
Removes all pairs of searched strings and their replacement value.
AString logBuf
The internal log Buffer.
virtual void notifyMultiLineOp(lang::Phase phase)=0
virtual ALIB_API void ResetAutoSizes()
virtual ALIB_API void writeMetaInfo(AString &buffer, detail::Domain &domain, Verbosity verbosity, detail::ScopeInfo &scope)
AString msgBuf
The buffers for converting the logables.
virtual ALIB_API void SetReplacement(const String &searched, const String &replacement)
virtual ALIB_API void Log(detail::Domain &domain, Verbosity verbosity, BoxesMA &logables, detail::ScopeInfo &scope) override
virtual void logText(detail::Domain &domain, Verbosity verbosity, AString &msg, detail::ScopeInfo &scope, int lineNumber)=0
TAString & ShortenTo(integer newLength)
Definition tastring.inl:971
ALIB_API integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:896
TChar CharAt(integer idx) const
Definition string.hpp:444
constexpr integer Length() const
Definition string.hpp:326
constexpr bool IsNotNull() const
Definition string.hpp:371
#define A_CHAR(STR)
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:1272
#define ALIB_LOCK_WITH(lock)
Definition owner.hpp:456
@ Begin
The start of a transaction.
@ End
The end of a transaction.
This namespaces defines class TextLogger and its helpers.
Definition loxpimpl.inl:41
@ FORMAT_TIME_DIFF
Denotes configuration variable ALOX/LOGGERNAME/FORMAT_TIME_DIFF used by class TextLogger.
@ FORMAT
Denotes configuration variable ALOX/LOGGERNAME/FORMAT used by class TextLogger.
@ REPLACEMENTS
Denotes configuration variable ALOX/LOGGERNAME/REPLACEMENTS used by class TextLogger.
@ FORMAT_DATE_TIME
Denotes configuration variable ALOX/LOGGERNAME/FORMAT_DATE_TIME used by class TextLogger.
@ FORMAT_MULTILINE
Denotes configuration variable ALOX/LOGGERNAME/FORMAT_MULTILINE used by class TextLogger.
@ FORMAT_OTHER
Denotes configuration variable ALOX/LOGGERNAME/FORMAT_OTHER used by class TextLogger.
@ AUTO_SIZES
Denotes configuration variable ALOX/LOGGERNAME/AUTO_SIZES used by class TextLogger.
ALIB_API Lock STD_IOSTREAMS_LOCK
Definition locks.cpp:301
lox::ALoxCamp ALOX
The singleton instance of ALib Camp class ALoxCamp.
Definition aloxcamp.cpp:33
lang::format::FormatterPythonStyle FormatterPythonStyle
Type alias in namespace alib.
config::Declaration Declaration
Type alias in namespace alib.
characters::character character
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
AutoSizes Main
The instance used with the meta info format string.
std::vector< AStringPA, lang::StdContainerAllocator< AStringPA, PoolAllocator > > Pairs
The list of pairs of replacement strings.