/** ******************************************************************************************** * 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); }
/** ******************************************************************************************** * 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') { // create TimeSpan object (on the stack by using new! :) TimeSpan elapsed = new TimeSpan(scope.GetTimeStamp().Raw() - logger.TimeOfCreation.Raw()); if (elapsed.Days > 0) { dest._(elapsed.Days)._(TimeElapsedDays); } if (elapsed.Hours > 0) { dest._(elapsed.Hours)._(':'); } dest._(elapsed.Minutes, 2)._(':') ._(elapsed.Seconds, 2)._('.') ._(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); } }
public static void Reset() { #if ALOX_DBG_LOG if ( Log.DebugLogger != null ) Log.RemoveDebugLogger(); if ( Log.LOX != null ) ALox.Register( Log.LOX, ContainerOp.Remove ); Log.LOX= new Lox("Log", true ); Log.ClearSourcePathTrimRules( Reach.Global, true ); Log.DebugLogger= null; Log.IDELogger = null; ALox.ConfigCategoryName._()._( "ALOX" ); #endif }
public static void RemoveDebugLogger( Lox lox= null, [CallerLineNumber] int cln= 0,[CallerFilePath] String csf="",[CallerMemberName] String cmn="" ) { #if ALOX_DBG_LOG // replace the report writer (if we replaced it before) Log.RemoveALibReportWriter(); // remove debug logger(s) ALIB.ASSERT_WARNING( DebugLogger != null, "No debug logger to remove." ); if ( lox == null ) lox= LOX; if ( DebugLogger != null ) { lox.RemoveLogger( DebugLogger, cln,csf,cmn ); DebugLogger= null; } if ( IDELogger != null ) { lox.RemoveLogger( IDELogger, cln,csf,cmn ); IDELogger= null; } #endif }
public static void AddDebugLogger( Lox lox= null, [CallerLineNumber] int cln= 0,[CallerFilePath] String csf="",[CallerMemberName] String cmn="" ) { #if ALOX_DBG_LOG if ( lox == null ) lox= LOX; ALIB.ASSERT_ERROR( DebugLogger == null, "Illeagal repeated invocation." ); // add a CLR logger if this a debug session if( System.Diagnostics.Debugger.IsAttached ) { Variable variable= new Variable(ALox.NO_IDE_LOGGER); variable.Load(); if( !variable.IsTrue() ) { IDELogger= new CLRDebuggerLogger( "IDE_LOGGER" ); // add logger lox.SetVerbosity( IDELogger , Verbosity.Verbose, "/" , Configuration.PrioDefault ,cln,csf,cmn ); lox.SetVerbosity( IDELogger , Verbosity.Warning, ALox.InternalDomains, Configuration.PrioDefault ,cln,csf,cmn ); } } // add a default console logger DebugLogger= Lox.CreateConsoleLogger("DEBUG_LOGGER"); lox.SetVerbosity( DebugLogger, Verbosity.Verbose, "/" , Configuration.PrioDefault ,cln,csf,cmn ); lox.SetVerbosity( DebugLogger, Verbosity.Warning, ALox.InternalDomains, Configuration.PrioDefault ,cln,csf,cmn ); // replace the ReportWriter Log.AddALibReportWriter( lox ); #endif }
/** ******************************************************************************************** * 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; } }
/** ******************************************************************************************** * 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; }
public UTWriter() { lox= new Lox( "UTLox" ); #if ALIB_MONO_DEVELOP logger= new ConsoleLogger( "UT ALib ReportWriter" ); #else logger= Lox.CreateConsoleLogger( "UT ALib ReportWriter" ); #endif lox.SetVerbosity( logger, Verbosity.Verbose, "UT" ); lox.SetVerbosity( logger, Verbosity.Verbose, ALox.InternalDomains); lox.SetPrefix( ESC.BG_GRAY, "/" ); cs.aworx.lib.Report.GetDefault().PushWriter( this ); }