/** ******************************************************************************************** * Processes the next command found in the format string, by writing formatted information * into the given buffer. * The given Substring holds the next command. When method returns, the command is cut * from the front. * * @param logger The logger that we are embedded in. * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param dest The buffer to write meta information into. * @param variable The variable to read (may have more characters appended) * * @return The number of tab sequences that were written (by adding ESC::TAB to the buffer). **********************************************************************************************/ protected virtual int processVariable( TextLogger logger, Domain domain, Verbosity verbosity, ScopeInfo scope, AString dest, Substring variable ) { // process commands char c2; switch ( variable.Consume() ) { // scope info case 'S': { // read sub command AString val; switch( c2= variable.Consume() ) { case 'P': // SP: full path { int length; String path= scope.GetFullPath( out length ); if ( length > 0 ) { dest._( path, 0, length ); return 0; } val= NoSourceFileInfo; } break; case 'p': // Sp: trimmed path { val= scope.GetTrimmedPath(); if ( val.IsEmpty() ) val= NoSourceFileInfo; } break; case 'F': // file name { val= scope.GetFileName(); if ( val.IsEmpty() ) val= NoSourceFileInfo; } break; case 'f': // file name without extension { val= scope.GetFileNameWithoutExtension(); if ( val.IsEmpty() ) val= NoSourceFileInfo; } break; case 'M': // method name { String method= scope.GetMethod(); if ( method.Length == 0 ) dest._( NoMethodInfo ); else dest._( method ); return 0; } case 'L': // line number { dest._( scope.GetLineNumber() ); return 0; } default: { if( !warnedOnce ) { warnedOnce= true; ALIB.WARNING( "Unknown format variable '%S" + c2 + "\' (only one warning)" ); } dest._( "%ERROR" ); return 0; } } dest._( val ); return 0; } // %Tx: Time case 'T': { // read sub command c2= variable.Consume(); // %TD: Date if ( c2 == 'D' ) { // get time stamp as DateTime once if ( callerDateTime == null ) callerDateTime= scope.GetTimeStamp().InDotNetDateTime(); // avoid the allocation of a) a StringBuilder (yes, a string builder is allocated inside StringBuilder.AppendFormat!) // and b) a DateTime object, if the format is the unchanged standard. And it is faster anyhow. if ( DateFormat.Equals( "yyyy-MM-dd" ) ) { dest ._( callerDateTime.Value.Year, 4 )._( '-' ) ._( callerDateTime.Value.Month, 2 )._( '-' ) ._( callerDateTime.Value.Day, 2 ); } // support user defined standards else { // detect changes of format string since last log if ( detectDateFormatChanges != DateFormat ) { detectDateFormatChanges= DateFormat; dateFormatString= "{0:" + DateFormat + "}"; } // get date string from system and append to log buffer formatSB.Clear(); formatSB.AppendFormat( CultureInfo.InvariantCulture, dateFormatString, callerDateTime ); dest._( formatSB ); } } // %TT: Time of Day else if ( c2 == 'T' ) { // get time stamp as DateTime once if ( callerDateTime == null ) callerDateTime= scope.GetTimeStamp().InDotNetDateTime(); // avoid the allocation of a) a StringBuilder (yes, a string builder is allocated inside StringBuilder.AppendFormat!) // and b) a DateTime object, if the format is the unchanged standard. And it is faster anyhow. if ( TimeOfDayFormat.Equals( "HH:mm:ss" ) ) { dest ._( callerDateTime.Value.Hour, 2 )._( ':' ) ._( callerDateTime.Value.Minute, 2 )._( ':' ) ._( callerDateTime.Value.Second, 2 ); } // support user defined standards else { // detect changes of format string since last log if ( detectTimeOfDayFormatChanges != TimeOfDayFormat ) { detectTimeOfDayFormatChanges= TimeOfDayFormat; timeOfDayFormatString= "{0:" + TimeOfDayFormat + "}"; } // get time string from system and append to log buffer formatSB.Clear(); formatSB.AppendFormat( CultureInfo.InvariantCulture, timeOfDayFormatString, callerDateTime); dest ._( formatSB ); } } // %TC: Time elapsed since created else if ( c2 == 'C' ) { elapsedTime.Set( scope.GetTimeStamp() ); elapsedTime.Sub( logger.TimeOfCreation ); if( MaxElapsedTime.Raw() < elapsedTime.Raw() ) MaxElapsedTime.Set( elapsedTime ); long maxElapsedSecs= MaxElapsedTime.InSeconds(); TimeSpan elapsed= new TimeSpan( elapsedTime.Raw() ); if ( maxElapsedSecs >= 60*60*24 ) dest._( elapsed.Days )._NC( TimeElapsedDays ); if ( maxElapsedSecs >= 60*60 ) dest._( elapsed.Hours , maxElapsedSecs >= 60*60*10 ? 2 : 1 )._( ':' ); if ( maxElapsedSecs >= 60 ) dest._( elapsed.Minutes, maxElapsedSecs >= 10*60 ? 2 : 1 )._( ':' ); dest._( elapsed.Seconds, maxElapsedSecs > 9 ? 2 : 1 )._NC( '.' ); dest._( elapsed.Milliseconds, 3 ); } // %TL: Time elapsed since last log call else if ( c2 == 'L' ) writeTimeDiff( dest, scope.GetTimeStamp().Since(logger.TimeOfLastLog).InNanos() ); else { if( !warnedOnce ) { warnedOnce= true; ALIB.WARNING( "Unknown format variable '%T" + c2 + "\' (only one warning)" ); } dest._( "%ERROR" ); } return 0; } // thread name / ID case 't': { c2= variable.Consume(); if ( c2 == 'N' ) { dest.Field() ._( scope.GetThreadName() ) .Field( logger.AutoSizes.Next( scope.GetThreadName().Length(), 0 ), Alignment.Center ); } else if ( c2 == 'I' ) { tmpAString._()._( scope.GetThreadID() ); dest.Field() ._( tmpAString ) .Field( logger.AutoSizes.Next( tmpAString .Length(), 0 ), Alignment.Center ); } else { if( !warnedOnce ) { warnedOnce= true; ALIB.WARNING( "Unknown format variable '%t" + c2 + "\' (only one warning)" ); } dest._( "%ERROR" ); } return 0; } case 'L': { c2= variable.Consume(); if ( c2 == 'G' ) dest._NC( logger.GetName() ); else if ( c2 == 'X' ) dest._NC( scope.GetLoxName() ); else { if( !warnedOnce ) { warnedOnce= true; ALIB.WARNING( "Unknown format variable '%L" + c2 + "\' (only one warning)" ); } dest._( "%ERROR" ); return 0; } return 0; } case 'P': { dest._NC( Util.GetProcessName() ); return 0; } case 'V': dest._ ( verbosity == Verbosity.Error ? VerbosityError : verbosity == Verbosity.Warning ? VerbosityWarning : verbosity == Verbosity.Info ? VerbosityInfo : VerbosityVerbose ); return 0; case 'D': { dest.Field()._( domain.FullPath ).Field( logger.AutoSizes.Next( domain.FullPath.Length(), 0 ), Alignment.Left ); return 0; } case '#': dest._( logger.CntLogs, LogNumberMinDigits ); return 0; // A: Auto tab case 'A': { // read extra space from format string int oldStart= variable.Start; int extraSpace; variable.ConsumeInteger( out extraSpace ); if ( oldStart == variable.Start ) extraSpace= 1; // insert ESC code to jump to next tab level extraSpace= Math.Min( extraSpace, 10 + ('Z'-'A') ); char escNo= extraSpace < 10 ? (char) ( '0' + extraSpace ) : (char) ( 'A' + extraSpace ); dest._( "\x1Bt" )._( escNo ); return 1; } default: { if( !warnedOnce ) { warnedOnce= true; ALIB.WARNING( "Unknown format variable \'" + variable.Buf[variable.Start - 1] + "\'" ); } dest._( "%ERROR" ); } return 0; } }
// ############################################################################################# // logText // ############################################################################################# /** ******************************************************************************************** * * The implementation of the abstract method of parent class TextLogger. Logs messages to the * application console and/or the VStudio output window. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param msg The log message * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For * single line messages this is -1. **********************************************************************************************/ override protected void logText( Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { // loop over message, print the parts between the escape sequences Tokenizer msgParts= new Tokenizer( msg, '\x001B' ); Substring actual= msgParts.Actual; Substring rest= msgParts.Rest; int column= 0; for(;;) { msgParts.Next( Whitespaces.Keep ); // check if this is an ANSI sequence already if ( rest.CharAtStart() == '[' ) { // read the 'm' int idx= rest.IndexOf( 'm' ); if ( idx < 0 ) // unknown ANSI Code { ALIB.WARNING( "Unknown ANSI ESC Code " ); textWriter.Write( actual.Buf, actual.Start, actual.Length() ); continue; } column+= actual.Length(); actual.End= rest.Start + idx ; rest.Start+= idx + 1; textWriter.Write( actual.Buf, actual.Start, actual.Length() ); continue; } if ( actual.IsNotEmpty() ) { textWriter.Write( actual.Buf, actual.Start, actual.Length() ); column+= actual.Length(); } // end of loop? if ( !msgParts.HasNext() ) break; // found an ESC sequence char c= rest.Consume(); // Colors bool isForeGround= true; if( c == 'C' || c == 'c' ) { isForeGround= c== 'c'; c= rest.Consume(); int colNo= c - '0'; ALIB.ASSERT_WARNING( colNo >=0 && colNo <=9, "Unknown ESC-c code" ); // add bg colNo+= isForeGround ? 0 : 10; // add light colNo+= (isForeGround ? !IsBackgroundLight : IsBackgroundLight ) ? 20 : 0; textWriter.Write( ansiCols[ colNo ] ); } // Styles else if ( c == 's' ) { // bold/italics style not supported in Windows console // reset all if ( rest.Consume() == 'a' ) { textWriter.Write( ANSI_RESET ); } } // auto tab / end of meta else if ( c == 't' || c == 'A') { bool endOfMeta= c == 'A'; c= rest.Consume(); int extraSpace= c >= '0' && c <= '9' ? (int) ( c - '0' ) : (int) ( c - 'A' ) + 10; int tabStop= AutoSizes.Next( column, extraSpace ); Util.WriteSpaces( textWriter, tabStop - column ); column= tabStop; if ( endOfMeta ) { String msgPrefix; switch ( verbosity ) { case lox.Verbosity.Verbose: msgPrefix= MsgPrefixVerbose; break; case lox.Verbosity.Info: msgPrefix= MsgPrefixInfo; break; case lox.Verbosity.Warning: msgPrefix= MsgPrefixWarning; break; case lox.Verbosity.Error: msgPrefix= MsgPrefixError; break; default: msgPrefix= ""; break; } textWriter.Write( msgPrefix ); } } // Link (we just colorize links here) else if ( c == 'l' ) { textWriter.Write( rest.Consume() == 'S' ? ( IsBackgroundLight ? ANSI_LIGHT_BLUE : ANSI_LIGHT_BLUE ) : ANSI_STD_COL ); } else { ALIB.WARNING( "Unknown ESC code" ); } } // write loop textWriter.WriteLine( MsgSuffix ); }
/** ******************************************************************************************** * * The implementation of the abstract method of parent class TextLogger. Logs messages to the * application console and/or the VStudio output window. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param msg The log message. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For * single line messages this is -1. **********************************************************************************************/ override protected void logText( Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { // get actual console attributes ConsoleColor actualFGCol= Console.ForegroundColor; ConsoleColor actualBGCol= Console.BackgroundColor; // loop over message, print the parts between the escape sequences Tokenizer msgParts= new Tokenizer( msg, '\x1B' ); Substring actual= msgParts.Actual; Substring rest= msgParts.Rest; int column= 0; for(;;) { if ( msgParts.Next(Whitespaces.Keep).IsNotEmpty() ) { #if !(ALOX_WP71 || ALOX_WP8) Console.Write( msg.Buffer(), actual.Start, actual.Length() ); #else Console.Write( msg.ToString( 0, actual.Start, actual.Length() ); #endif column+= actual.Length(); } // end of loop? if ( !msgParts.HasNext() ) break; // found an ESC sequence char c= rest.Consume(); // Colors bool isForeGround= true; if( c == 'C' || c == 'c' ) { isForeGround= c== 'c'; c= rest.Consume(); int colNo= c - '0'; ALIB.ASSERT_WARNING( colNo >=0 && colNo <=9, "Unknown ESC-c code" ); // set color if( colNo >= 0 && colNo <= 8 || colNo == 8) { ConsoleColor[] cols= (isForeGround ? !IsBackgroundLight : IsBackgroundLight ) ? lightColors : darkColors; if ( isForeGround ) Console.ForegroundColor= cols[ colNo ]; else Console.BackgroundColor= cols[ colNo ]; } else if ( colNo == 9 ) { if ( isForeGround ) Console.ForegroundColor= actualFGCol; else Console.BackgroundColor= actualBGCol; } else { ALIB.WARNING( "Unknown ESC- code" ); } } // Styles else if ( c == 's' ) { // bold/italics style not supported in Windows console // reset all if ( rest.Consume() == 'a' ) { Console.ForegroundColor= actualFGCol; Console.BackgroundColor= actualBGCol; } } // auto tab / end of meta else if ( c == 't' || c == 'A') { bool endOfMeta= c == 'A'; c= rest.Consume(); int extraSpace= c >= '0' && c <= '9' ? (int) ( c - '0' ) : (int) ( c - 'A' ) + 10; int tabStop= AutoSizes.Next( column, extraSpace ); Util.WriteSpaces( Console.Out, tabStop - column ); column= tabStop; if ( endOfMeta ) { switch ( verbosity ) { case Verbosity.Verbose: Console.ForegroundColor= MsgColorVerbose; break; case Verbosity.Info: Console.ForegroundColor= MsgColorInfo; break; case Verbosity.Warning: Console.ForegroundColor= MsgColorWarning; break; case Verbosity.Error: Console.ForegroundColor= MsgColorError; break; default: break; } } } // Link (we just colorize links here) else if ( c == 'l' ) { if ( rest.Consume() == 'S' ) Console.ForegroundColor= IsBackgroundLight ? ConsoleColor.DarkBlue : ConsoleColor.Blue; else Console.ForegroundColor= actualFGCol; } else { ALIB.WARNING( "Unknown ESC code" ); } } // write loop // reset colors Console.ForegroundColor= actualFGCol; Console.BackgroundColor= actualBGCol; // write NL #if !(ALOX_WP71 || ALOX_WP8) Console.WriteLine(); #else Console.WriteLine(); #endif }
/** ******************************************************************************************** * Parses the #Format string and logs meta information into the log buffer. For each * variable found, method #processVariable is invoked. Hence, to add new variables, * the latter method can be overwritten by descendants. Overwriting this method is * recommended for formatter classes that do not rely on format strings. * @param logger The logger that we are embedded in. * @param buffer The buffer to write meta information into. * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param scope Information about the scope of the <em>Log Statement</em>.. * * @return The number of tab sequences that were written (by adding ESC::TAB to the buffer). **********************************************************************************************/ public virtual int Write( TextLogger logger, AString buffer, Domain domain, Verbosity verbosity, ScopeInfo scope ) { int qtyTabStops= 0; // check if ( Format == null || Format.IsEmpty() ) return 0; // clear DateTime singleton callerDateTime= null ; // loop/switch over content specified in configuration array tTok.Set( Format, '%' ); while ( true ) { // get next and log substring between commands if ( tTok.Next(Whitespaces.Keep).IsNotEmpty() ) buffer._( tTok.Actual ); // previous next did not find a delimiter if ( !tTok.HasNext() ) break; // process the found variable qtyTabStops+= processVariable( logger, domain, verbosity, scope, buffer, tTok.Rest ); } return qtyTabStops; }
// ############################################################################################# // Abstract method implementations // ############################################################################################# /** **************************************************************************************** * This is the implementation of the abstract method inherited from class Logger * that executes a log.<br> * This class implements this method and but exposes the new abstract method #logText. * This mechanism allows this class to do various things that are standard to Loggers * of type TextLogger. For example, meta information of the log invocation is formatted and * string replacements are performed. This way, descendants of this class will consume * a ready to use log buffer with all meta information and contents of all objects to be * included and their primary obligation is to copy the content into a corresponding * output stream. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param logables The list of objects to log. * @param scope Information about the scope of the <em>Log Statement</em>.. ******************************************************************************************/ override public void Log( Domain domain, Verbosity verbosity, List<Object> logables, ScopeInfo scope ) { // clear Buffer and reset utility members logBuf.Clear(); AutoSizes.Start(); // << meta info << ESC::EOMETA int qtyESCTabsWritten= MetaInfo.Write( this, logBuf, domain, verbosity, scope ); logBuf._( ESC.EOMETA ); // convert msg object into an AString representation msgBuf._(); foreach( Object logable in logables ) { int i; for( i= ObjectConverters.Count - 1; i >= 0 ; i-- ) if ( ObjectConverters[i].ConvertObject( logable, msgBuf ) ) break; if ( i == -1 ) { AString msg= new AString( FmtUnknownObject ); msg.SearchAndReplace( "%", "" + logable.GetType().ToString() ); msgBuf._NC( msg ); } } // replace strings in message for ( int i= 0; i < replacements.Count ; i+=2 ) msgBuf.SearchAndReplace( replacements[i], replacements[i + 1] ); // check for empty messages if ( msgBuf.IsEmpty() ) { // log empty msg and quit if (usesStdStreams) ALIB.StdOutputStreamsLock.Acquire(); logText( domain, verbosity, logBuf, scope, -1 ); if (usesStdStreams) ALIB.StdOutputStreamsLock.Release(); return; } // single line output if ( MultiLineMsgMode == 0 ) { // replace line separators int cntReplacements=0; if ( MultiLineDelimiter != null ) cntReplacements+=msgBuf.SearchAndReplace( MultiLineDelimiter, MultiLineDelimiterRepl ); else { cntReplacements+=msgBuf.SearchAndReplace( "\r\n", MultiLineDelimiterRepl ); cntReplacements+=msgBuf.SearchAndReplace( "\r", MultiLineDelimiterRepl ); cntReplacements+=msgBuf.SearchAndReplace( "\n", MultiLineDelimiterRepl ); } // append msg to logBuf if ( cntReplacements == 0 ) { logBuf._( msgBuf ); } else { logBuf._( FmtMultiLinePrefix ); logBuf._( msgBuf ); logBuf._( FmtMultiLineSuffix ); } // now do the logging by calling our derived classes' logText logText( domain, verbosity, logBuf, scope, -1 ); // stop here return; } // multiple line output int qtyTabStops= AutoSizes.ActualIndex; int actStart=0; int lineNo=0; int lbLenBeforeMsgPart= logBuf.Length(); // loop over lines in msg while ( actStart < msgBuf.Length() ) { // find next end int delimLen; int actEnd; // no delimiter given: search "\r\n", then '\r', then '\n' if ( String.IsNullOrEmpty( MultiLineDelimiter ) ) { delimLen= 1; actEnd= msgBuf.IndexOf( '\n', actStart ); if( actEnd > actStart ) { if( msgBuf.CharAt(actEnd - 1) == '\r' ) { actEnd--; delimLen= 2; } } } else { delimLen=MultiLineDelimiter.Length; actEnd=msgBuf.IndexOf( MultiLineDelimiter, actStart ); } // not found a delimiter? - log the rest if ( actEnd < 0 ) { // single line? if ( lineNo == 0 ) { // append msg to logBuf logBuf._( msgBuf ); // now do the logging by calling our derived classes' logText if (usesStdStreams) ALIB.StdOutputStreamsLock.Acquire(); logText( domain, verbosity, logBuf, scope, -1 ); if (usesStdStreams) ALIB.StdOutputStreamsLock.Release(); // stop here return; } // store actual end actEnd= msgBuf.Length(); } // found a delimiter // signal start of multi line log if ( lineNo == 0 ) { if (usesStdStreams) ALIB.StdOutputStreamsLock.Acquire(); notifyMultiLineOp( Phase.Begin ); } // in mode 3, 4, meta info is deleted if ( lineNo == 0 && ( MultiLineMsgMode == 3 || MultiLineMsgMode == 4 ) ) { // log headline in mode 3 if ( MultiLineMsgMode == 3 ) { logBuf._( FmtMultiLineMsgHeadline ); AutoSizes.ActualIndex= qtyTabStops; logText( domain, verbosity, logBuf, scope, 0 ); } // remember zero as offset lbLenBeforeMsgPart=0; } // blank out meta information? (do this exactly in 2nd line once) if ( MultiLineMsgMode == 2 ) { if (lineNo != 0 ) { logBuf.Clear()._( ESC.EOMETA ); AutoSizes.ActualIndex= qtyTabStops + qtyESCTabsWritten; } } // reset logBuf length to marked position else { logBuf.SetLength_NC( lbLenBeforeMsgPart ); AutoSizes.ActualIndex= qtyTabStops; } // append message logBuf._( FmtMultiLinePrefix ); logBuf._NC( msgBuf, actStart, actEnd - actStart ); logBuf._( FmtMultiLineSuffix ); logText( domain, verbosity, logBuf, scope, lineNo ); // next actStart= actEnd + delimLen; lineNo++; } // signal end of multi line log if ( lineNo > 0 ) { notifyMultiLineOp( Phase.End ); if (usesStdStreams) ALIB.StdOutputStreamsLock.Release(); } }
// ############################################################################################# // Abstract methods (introduced) // ############################################################################################# /** **************************************************************************************** * This is the central method that derived logger classes have to implement to log a * message. When it is invoked the <em>Log Domains' Verbosity</em> was already checked against * parameter \p verbosity. The only action to take is to perform the log itself.<br> * Parameter \p logables contains at least one object, the one provided with the log * statement. Other objects that might be included in the list, are <em>Prefix Objects</em> * corresponding to the \p scope. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. * @param logables The list of objects to log. * @param scope Information about the scope of the <em>Log Statement</em>. ******************************************************************************************/ abstract public void Log( Domain domain, Verbosity verbosity, List<Object> logables, ScopeInfo scope );
// ############################################################################################# // Abstract methods (introduced) // ############################################################################################# /** **************************************************************************************** * This abstract method introduced by this class "replaces" the the abstract method #Log * of parent class Logger which this class implements. In other words, descendants of this * class need to overwrite this method instead of \b %Do. This class %TextLogger is * responsible for generating meta information, doing text replacements, handle multi-line * messages, etc. and provides the textual representation of the whole log contents * to descendants using this method. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this * stage and is provided to be able to be logged out only. * @param msg The log message. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. * For single line messages this is -1. ******************************************************************************************/ abstract protected void logText( Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber);
// ############################################################################################# // Abstract methods (introduced) // ############################################################################################# /** **************************************************************************************** * This is the central method that derived logger classes have to implement to log a * message. When it is invoked the <em>Log Domains' Verbosity</em> was already checked against * parameter \p verbosity. The only action to take is to perform the log itself.<br> * Parameter \p logables contains at least one object, the one provided with the log * statement. Other objects that might be included in the list, are <em>Prefix Objects</em> * corresponding to the \p scope. * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. * @param logables The list of objects to log. * @param scope Information about the scope of the <em>Log Statement</em>. ******************************************************************************************/ abstract public void Log(Domain domain, Verbosity verbosity, List <Object> logables, ScopeInfo scope);
/** ******************************************************************************************** * The implementation of the abstract method of parent class TextLogger. * Loops over the log text, removes or ignores ESC sequences (all but ESC.TAB) and invokes * abstract methods of descendants as follows: * - \ref notifyLogOp "notifyLogOp(true)" * - #logSubstring() * - ... * - \ref notifyLogOp "notifyLogOp(Phase.End)" * * @param domain The <em>Log Domain</em>. * @param verbosity The verbosity. This has been checked to be active already on this stage * and is provided to be able to be logged out only. * @param msg The log message. * @param scope Information about the scope of the <em>Log Statement</em>.. * @param lineNumber The line number of a multi-line message, starting with 0. For single * line messages this is -1. **********************************************************************************************/ override protected void logText( Domain domain, Verbosity verbosity, AString msg, ScopeInfo scope, int lineNumber) { if ( !notifyLogOp( Phase.Begin ) ) return; // loop over message, print the parts between the escape sequences int msgLength= msg.Length(); int start= 0; int end; int column= 0; while( start < msgLength ) { bool foundESC= true; end= msg.IndexOf( '\x1B', start ); if( end < 0 ) { foundESC= false; end= msgLength ; } if ( end > start ) { if (!logSubstring( msg, start, end - start ) ) return; column+= end - start; } // interpret escape sequence if ( foundESC ) { char c= msg[++end]; // auto tab or end of meta info part if ( c == 't' || c == 'A') { end++; c= msg[end++]; int extraSpace= c >= '0' && c <= '9' ? (int) ( c - '0' ) : (int) ( c - 'A' ) + 10; int tabStop= AutoSizes.Next( column, extraSpace ); if ( tabStop > column ) { AString spaces= Util.GetSpaces(); int spacesLength= spaces.Length(); int qty= tabStop - column; while ( qty > 0 ) { int size= qty < spacesLength ? qty : spacesLength; if(!logSubstring( spaces, 0, size ) ) return; qty-= size; } column= tabStop; } } // prune or ignore all others else { if ( !PruneESCSequences ) if(!logSubstring( msg, end - 1, 3 )) return; end+= 2; } } // next start= end; } // write loop ALIB.ASSERT_WARNING( start == msgLength, "Loop error when pruning ESC codes" ); notifyLogOp( Phase.End); }
// ############################################################################################# // Public interface // ############################################################################################# /** **************************************************************************************** * Constructor * @param scopeInfo The ScopeInfo singleton of 'our' Lox. * @param cfgSingleThreadValue Determines if for thread-related scopes, a list of values is * stored, or just one. ******************************************************************************************/ public ScopeStore(ScopeInfo scopeInfo, bool cfgSingleThreadValue) { this.scopeInfo = scopeInfo; this.cfgSingleThreadValue = cfgSingleThreadValue; this.globalStore = default(T); }
// ############################################################################################# // Constructors // ############################################################################################# /** **************************************************************************************** * Constructs a new, empty Lox with the given \p name. * The name is immutable and all \b %Lox objects registered with ALox must be unique. * The name \c "Log" is reserved for the internal default singleton used for debug-logging. * In addition, name \c "GLOBAL" is not allowed. * * If parameter \p register is \c true (the default), static method * \ref cs::aworx::lox::ALox::Register "ALox.Register" is invoked and the object will be * retrievable with static method * \ref cs::aworx::lox::ALox::Get "ALox.Get". In some situations, such 'registration' * may not be wanted. * @param name The name of the Lox. Will be converted to upper case. * @param doRegister If \c true, this object is registered with static class * \ref cs::aworx::lox::ALox "ALox". * Optional and defaults to \c true. ******************************************************************************************/ public Lox( String name, bool doRegister = true ) : base() { // set recursion warning of log buffer lock to 1. Warnings are logged if recursively // acquired more than once #if ALOX_DBG_LOG || ALOX_REL_LOG logBufLock.RecursionWarningThreshold= 1; scopeInfo= new ScopeInfo( name, threadDictionary ); scopeDomains= new ScopeStore<AString >( scopeInfo, false ); scopePrefixes= new ScopeStore<Object >( scopeInfo, false ); scopeLogData= new ScopeStore<Dictionary<AString, LogData>>( scopeInfo, true ); scopeLogOnce= new ScopeStore<Dictionary<AString, int[]> >( scopeInfo, true ); // create domain trees domains = new Domain( null, new AString( "") ); internalDomains = new Domain( null, new AString( ALox.InternalDomains, 0, ALox.InternalDomains.Length - 1) ); // create internal sub-domains bool wasCreated= false; String[] internalDomainList= {"LGR", "DMN", "PFX", "THR", "LGD", "VAR" }; foreach ( String it in internalDomainList ) { resDomainInternal._()._NC( it ); internalDomains.Find( resDomainInternal, Case.Sensitive, 1, ref wasCreated ); } maxDomainPathLength= ALox.InternalDomains.Length + 3; // register with ALox if ( doRegister ) ALox.Register( this, ContainerOp.Insert ); // read domain substitution rules from configuration Variable variable= new Variable( ALox.DOMAIN_SUBSTITUTION, GetName() ); if ( variable.Load() != 0 ) { for( int ruleNo= 0; ruleNo< variable.Size(); ruleNo++ ) { AString rule= variable.GetString( ruleNo ); int idx= rule.IndexOf( "->" ); if ( idx > 0 ) { String domainPath= rule.ToString( 0, idx ).Trim(); String replacement= rule.ToString( idx + 2 ).Trim(); SetDomainSubstitutionRule( domainPath, replacement ); } else { // using alib warning here as we can't do internal logging in the constructor ALIB.WARNING( "Syntax error in variable \"" + variable.Fullname + "\"." ); } } } #endif }