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