Finish adding the high log type (something more important than info, but not a warning either).

Add file/line/member to log messages.
Add Auto Category to logs using the last dir in their path.  This will entail moving some files around to get better categories.
This commit is contained in:
Marc Hernandez 2019-12-07 18:32:44 -08:00
parent 992c442393
commit 24cbb17615

852
Log.cs
View File

@ -1,399 +1,453 @@
using System; using System;
using System.IO; using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.Collections; using System.Collections;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Collections.Immutable;
//using System.Threading.Tasks; using System.Reflection;
//using System.Threading.Tasks;
namespace lib
{ namespace lib
{
[Flags]
public enum LogTypeNew [Flags]
{ public enum LogTypeNew
Invalid = 0, {
Invalid = 0,
// Frequency
FrequencyBase = 1, // Frequency
FrequencyBits = 2, FrequencyBase = 1,
FrequencyMask = ( ( 1 << FrequencyBits ) - 1 ) << FrequencyBase, FrequencyBits = 2,
FrequencyMask = ( ( 1 << FrequencyBits ) - 1 ) << FrequencyBase,
Detail = 0b01 << FrequencyBase,
Normal = 0b10 << FrequencyBase, Detail = 0b01 << FrequencyBase,
Overview = 0b11 << FrequencyBase, Normal = 0b10 << FrequencyBase,
Overview = 0b11 << FrequencyBase,
// Type
TypeBase = FrequencyBase + FrequencyBits, // Type
TypeBits = 3, TypeBase = FrequencyBase + FrequencyBits,
TypeMask = ( ( 1 << TypeBits ) - 1 ) << TypeBase, TypeBits = 3,
TypeMask = ( ( 1 << TypeBits ) - 1 ) << TypeBase,
Startup = 0b001 << TypeBase,
Running = 0b010 << TypeBase, Startup = 0b001 << TypeBase,
Shutdown = 0b011 << TypeBase, Running = 0b010 << TypeBase,
Error = 0b101 << TypeBase, Shutdown = 0b011 << TypeBase,
Error = 0b101 << TypeBase,
}
}
[Flags]
public enum LogType [Flags]
{ public enum LogType
Invalid = 0, {
Trace = 1, Invalid = 0,
Debug = 2, Trace = 1,
Info = 3, Debug = 2,
Warn = 4, Info = 3,
Error = 5, High = 4,
Fatal = 6, Warn = 5,
} Error = 6,
Fatal = 7,
public struct LogEvent }
{
public DateTime Time; public struct LogEvent
public LogType LogType; {
public string Cat; public DateTime Time;
public string Msg; public LogType LogType;
public object Obj; public string Msg;
public string Path;
public LogEvent( LogType logType, string cat, string msg, object obj ) public int Line;
{ public string Member;
Time = DateTime.Now;
LogType = logType; public string Cat;
Cat = cat; public object Obj;
Msg = msg;
Obj = obj;
}
} static ImmutableDictionary<string, string> m_shortname = ImmutableDictionary<string, string>.Empty;
public delegate void Log_delegate( LogEvent evt );
public LogEvent( LogType logType, string msg, string path, int line, string member, string cat, object obj )
{
public class Log : TraceListener
{ //Cache the automatic category names
static public void create( string filename ) if( string.IsNullOrEmpty( cat ) )
{ {
s_log = new Log( filename ); if( m_shortname.TryGetValue( path, out var autoCat ) )
} {
cat = autoCat;
static public void destroy() }
{ else
string msg = "==============================================================================\nLogfile shutdown at " + DateTime.Now.ToString(); {
var pathPieces = path.Split('\\');
var evt = new LogEvent( LogType.Info, "System", msg, null );
var lastDir = pathPieces[pathPieces.Length - 1];
s_log.writeToAll( evt );
ImmutableInterlocked.AddOrUpdate( ref m_shortname, path, lastDir, ( key, value ) => { return lastDir; } );
s_log.stop();
cat = lastDir;
s_log = null; }
} }
static public Log s_log; Time = DateTime.Now;
LogType = logType;
/* Msg = msg;
static public Log log Path = path;
{ Line = line;
get Member = member;
{ Cat = cat;
return s_log; Obj = obj;
} }
} }
*/
public delegate void Log_delegate( LogEvent evt );
// Forwards.
static public void fatal( string msg, string cat = "", object obj = null )
{
log( msg, LogType.Fatal, cat, obj ); public class Log : TraceListener
} {
static public void create( string filename )
static public void error( string msg, string cat = "", object obj = null ) {
{ s_log = new Log( filename );
log( msg, LogType.Error, cat, obj ); }
}
static public void warn( string msg, string cat = "", object obj = null ) static public void destroy()
{ {
log( msg, LogType.Warn, cat, obj ); string msg = "==============================================================================\nLogfile shutdown at " + DateTime.Now.ToString();
}
var evt = CreateLogEvent( LogType.Info, msg, "System", null );
static public void info( string msg, string cat = "", object obj = null )
{ s_log.writeToAll( evt );
log( msg, LogType.Info, cat, obj );
} s_log.stop();
static public void debug( string msg, string cat = "", object obj = null ) s_log = null;
{ }
log( msg, LogType.Debug, cat, obj );
} static public Log s_log;
static public void trace( string msg, string cat = "", object obj = null ) /*
{ static public Log log
log( msg, LogType.Trace, cat, obj ); {
} get
{
static public void log( string msg, LogType type = LogType.Debug, string cat = "unk", object obj = null ) return s_log;
{ }
lock( s_log ) }
{ */
var evt = new LogEvent( type, cat, msg, obj );
s_log.writeToAll( evt ); static LogEvent CreateLogEvent( LogType logType, string msg, string cat, object obj, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
} {
} var logEvent = new LogEvent( logType, msg, path, line, member, cat, obj );
return logEvent;
static public void logProps( object obj, string header, LogType type = LogType.Debug, string cat = "", string prefix = "" ) }
{
var list = scr.GetAllProperties( obj.GetType() );
lock( s_log )
{ // Forwards.
var evt = new LogEvent( type, cat, header, obj ); static public void fatal( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
{
s_log.writeToAll( evt ); log( msg, LogType.Fatal, path, line, member, cat, obj );
}
foreach( var pi in list )
{ static public void error( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
try {
{ log( msg, LogType.Error, path, line, member, cat, obj );
var v = pi.GetValue( obj ); }
log( $"{prefix}{pi.Name} = {v}", type, cat ); static public void warn( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
} {
catch( Exception ex ) log( msg, LogType.Warn, path, line, member, cat, obj );
{ }
log( $"Exception processing {pi.Name} {ex.Message}", type, cat );
} static public void info( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
} {
log( msg, LogType.Info, path, line, member, cat, obj );
} }
}
static public void high( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
//This might seem a little odd, but the intent is that usually you wont need to set notExpectedValue. {
static public void expected<T>( T value, string falseString, string trueString = "", T notExpectedValue = default( T ) ) log( msg, LogType.High, path, line, member, cat, obj );
{ }
if( !value.Equals( notExpectedValue ) ) static public void debug( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
{ {
lib.Log.info( $"Properly got {value}{trueString}" ); log( msg, LogType.Debug, path, line, member, cat, obj );
} }
else
{ static public void trace( string msg, string cat = "", object obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
lib.Log.warn( $"Got {notExpectedValue} instead of {value}{falseString}" ); {
} log( msg, LogType.Trace, path, line, member, cat, obj );
} }
static public void log( string msg, LogType type = LogType.Debug, string path = "", int line = -1, string member = "", string cat = "unk", object obj = null )
private Log( string filename ) {
{ // @@@@@ TODO Get rid of this lock.
//TODO: Fix this so itll work without a directory. var evt = new LogEvent( type, msg, path, line, member, cat, obj );
Directory.CreateDirectory( Path.GetDirectoryName( filename ) );
lock( s_log )
m_stream = new FileStream( filename, FileMode.Append, FileAccess.Write ); {
m_writer = new StreamWriter( m_stream ); s_log.writeToAll( evt );
}
m_errorStream = new FileStream( filename + ".error", FileMode.Append, FileAccess.Write ); }
m_errorWriter = new StreamWriter( m_errorStream );
static public void logProps( object obj, string header, LogType type = LogType.Debug, string cat = "", string prefix = "" )
//Debug.Listeners.Add( this ); {
var list = scr.GetAllProperties( obj.GetType() );
string msg = "\n==============================================================================\nLogfile " + filename + " startup at " + DateTime.Now.ToString();
lock( s_log )
var evt = new LogEvent( LogType.Info, "System", msg, null ); {
var evt = CreateLogEvent( type, header, cat, obj );
writeToAll( evt );
} s_log.writeToAll( evt );
public override void Write( string msg ) foreach( var pi in list )
{ {
WriteLine( msg ); try
} {
var v = pi.GetValue( obj );
public override void WriteLine( string msg )
{ log( $"{prefix}{pi.Name} = {v}", type, cat );
error( msg ); }
//base.WriteLine( msg ); catch( Exception ex )
} {
log( $"Exception processing {pi.Name} {ex.Message}", LogType.Error, "log" );
void stop() }
{ }
m_writer.Close();
m_stream.Close(); }
}
m_errorWriter.Close();
m_errorStream.Close(); //This might seem a little odd, but the intent is that usually you wont need to set notExpectedValue.
} static public void expected<T>( T value, string falseString, string trueString = "", T notExpectedValue = default( T ) )
{
public void addDelegate( Log_delegate cb )
{ if( !value.Equals( notExpectedValue ) )
m_delegates.Add( cb ); {
} lib.Log.info( $"Properly got {value}{trueString}" );
}
/* else
private void writeFileAndLine( StackTrace st ) {
{ lib.Log.warn( $"Got {notExpectedValue} instead of {value}{falseString}" );
StackFrame frame = st.GetFrame( 1 ); }
}
string srcFile = frame.GetFileName();
string srcLine = frame.GetFileLineNumber().Tostring();
private Log( string filename )
Console.WriteLine( $"{srcFile} ({srcLine}):" ); {
} //TODO: Fix this so itll work without a directory.
Directory.CreateDirectory( Path.GetDirectoryName( filename ) );
private void writeStack( StackTrace st )
{ m_stream = new FileStream( filename, FileMode.Append, FileAccess.Write );
for( int i=0; i<st.FrameCount; ++i ) m_writer = new StreamWriter( m_stream );
{
StackFrame frame = st.GetFrame( i ); m_errorStream = new FileStream( filename + ".error", FileMode.Append, FileAccess.Write );
m_errorWriter = new StreamWriter( m_errorStream );
string srcFile = frame.GetFileName();
string srcLine = frame.GetFileLineNumber().Tostring(); //Debug.Listeners.Add( this );
if( srcFile != null ) string msg = "\n==============================================================================\nLogfile " + filename + " startup at " + DateTime.Now.ToString();
{
Console.WriteLine( $"{srcFile} ({srcLine})" ); var evt = CreateLogEvent( LogType.Info, msg, "System", null );
}
} writeToAll( evt );
} }
*/
public override void Write( string msg )
private char getSymbol( LogType type ) {
{ WriteLine( msg );
switch( type ) }
{
case LogType.Trace: public override void WriteLine( string msg )
return '.'; {
case LogType.Debug: error( msg );
return '-'; //base.WriteLine( msg );
case LogType.Info: }
return ' ';
case LogType.Warn: void stop()
return '+'; {
case LogType.Error: m_writer.Close();
return '*'; m_stream.Close();
case LogType.Fatal:
return '*'; m_errorWriter.Close();
default: m_errorStream.Close();
return '?'; }
}
} public void addDelegate( Log_delegate cb )
{
private void writeToAll( LogEvent evt ) m_delegates.Add( cb );
{ }
try
{ /*
// _SHOULDNT_ need this since we lock at the top. private void writeFileAndLine( StackTrace st )
//lock( this ) {
{ StackFrame frame = st.GetFrame( 1 );
char sym = getSymbol( evt.LogType );
string srcFile = frame.GetFileName();
string finalMsg = string.Format( "{0,-6}{1}| {2}", evt.Cat, sym, evt.Msg ); string srcLine = frame.GetFileLineNumber().Tostring();
//Console.WriteLine( finalMsg ); Console.WriteLine( $"{srcFile} ({srcLine}):" );
//Console.Out.Write( finalMsg ); }
/* private void writeStack( StackTrace st )
foreach( var l_obj in Debug.Listeners ) {
{ for( int i=0; i<st.FrameCount; ++i )
var l = l_obj as TraceListener; {
if( l != null && l != this ) StackFrame frame = st.GetFrame( i );
{
l.WriteLine( finalMsg ); string srcFile = frame.GetFileName();
} string srcLine = frame.GetFileLineNumber().Tostring();
}
*/ if( srcFile != null )
{
m_writer.WriteLine( finalMsg ); Console.WriteLine( $"{srcFile} ({srcLine})" );
}
m_writer.Flush(); }
}
foreach( Log_delegate cb in m_delegates ) */
{
//lock( cb ) private char getSymbol( LogType type )
{ {
cb( evt ); switch( type )
} {
} case LogType.Trace:
} return ' ';
} case LogType.Debug:
catch( Exception ) return ' ';
{ case LogType.Info:
//oops. return ':';
//int dummy = 0; case LogType.High:
} return '+';
} case LogType.Warn:
return '+';
/* case LogType.Error:
private void error_i( string msg, object obj ) return '*';
{ case LogType.Fatal:
//var t = Task.Run( () => { return '*';
StackTrace st = new StackTrace( true ); default:
return '?';
writeStack( st ); }
}
string msgPrint = msg;
private void writeToAll( LogEvent evt )
if( args.Length > 0 ) {
{ try
msgPrint = string.Format( msg, args ); {
} // _SHOULDNT_ need this since we lock at the top.
//lock( this )
writeToAll( "error", "log", msgPrint ); {
//} ); char sym = getSymbol( evt.LogType );
}
private void warn_i( string msg, object obj ) string finalMsg = string.Format( "{0,-6}{1}| {2}", evt.Cat, sym, evt.Msg );
{
//var t = Task.Run( () => { //Console.WriteLine( finalMsg );
StackTrace st = new StackTrace( true ); //Console.Out.Write( finalMsg );
writeStack( st ); /*
foreach( var l_obj in Debug.Listeners )
string msgPrint = msg; {
var l = l_obj as TraceListener;
if( args.Length > 0 ) if( l != null && l != this )
{ {
msgPrint = string.Format( msg, args ); l.WriteLine( finalMsg );
} }
}
writeToAll( "warn", "log", msgPrint ); */
//});
} m_writer.WriteLine( finalMsg );
private void info_i( string msg, object obj )
{ m_writer.Flush();
//var t = Task.Run( () => {
StackTrace st = new StackTrace( true ); foreach( Log_delegate cb in m_delegates )
{
string msgPrint = msg; //lock( cb )
{
if( args.Length > 0 ) cb( evt );
{ }
msgPrint = string.Format( msg, args ); }
} }
}
writeToAll( "info", "log", msgPrint ); catch( Exception )
//} ); {
} //oops.
*/ //int dummy = 0;
}
private Stream m_stream; }
private StreamWriter m_writer;
/*
private Stream m_errorStream; private void error_i( string msg, object obj )
private StreamWriter m_errorWriter; {
//var t = Task.Run( () => {
private ArrayList m_delegates = new ArrayList(); StackTrace st = new StackTrace( true );
}
writeStack( st );
string msgPrint = msg;
if( args.Length > 0 )
{
msgPrint = string.Format( msg, args );
}
} writeToAll( "error", "log", msgPrint );
//} );
}
private void warn_i( string msg, object obj )
{
//var t = Task.Run( () => {
StackTrace st = new StackTrace( true );
writeStack( st );
string msgPrint = msg;
if( args.Length > 0 )
{
msgPrint = string.Format( msg, args );
}
writeToAll( "warn", "log", msgPrint );
//});
}
private void info_i( string msg, object obj )
{
//var t = Task.Run( () => {
StackTrace st = new StackTrace( true );
string msgPrint = msg;
if( args.Length > 0 )
{
msgPrint = string.Format( msg, args );
}
writeToAll( "info", "log", msgPrint );
//} );
}
*/
private Stream m_stream;
private StreamWriter m_writer;
private Stream m_errorStream;
private StreamWriter m_errorWriter;
private ArrayList m_delegates = new ArrayList();
}
}