Files
sbox-public/engine/Sandbox.System/Logging/Logger.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

232 lines
6.4 KiB
C#

using NLog;
using System.Web;
namespace Sandbox.Diagnostics;
[StackTraceHidden]
public class Logger
{
private NLog.Logger _log;
/// <summary>
/// Name of this logger.
/// </summary>
public string Name { get; protected set; }
public Logger( string name )
{
Logging.InitializeConfig();
Name = name;
_log = NLog.LogManager.GetLogger( name );
Logging.RegisterLogger( Name );
}
internal void DoInfo( FormattableString message ) => WriteToTargets( NLog.LogLevel.Info, null, message );
internal void DoTrace( FormattableString message ) => WriteToTargets( NLog.LogLevel.Trace, null, message );
internal void DoWarning( FormattableString message ) => WriteToTargets( NLog.LogLevel.Warn, null, message );
internal void DoError( FormattableString message ) => WriteToTargets( NLog.LogLevel.Error, null, message );
/// <inheritdoc cref="Info(object)"/>
public void Info( FormattableString message ) => DoInfo( message );
/// <inheritdoc cref="Trace(object)"/>
public void Trace( FormattableString message ) => DoTrace( message );
/// <inheritdoc cref="Warning(object)"/>
public void Warning( FormattableString message ) => DoWarning( message );
/// <inheritdoc cref="Error(object)"/>
public void Error( FormattableString message ) => DoError( message );
/// <inheritdoc cref="Error(Exception, object)"/>
public void Error( Exception exception, FormattableString message ) => WriteToTargets( NLog.LogLevel.Error, exception, message );
/// <summary>
/// Log an exception as an error, with given message override.
/// </summary>
/// <param name="exception">The exception to log.</param>
/// <param name="message">The text to override exceptions' message with in the log.</param>
public void Error( Exception exception, object message ) => Error( exception, $"{message}" );
/// <summary>
/// Log an exception as an error.
/// </summary>
/// <param name="exception">The exception to log.</param>
public void Error( Exception exception ) => WriteToTargets( NLog.LogLevel.Error, exception, $"{exception.Message}" );
/// <inheritdoc cref="Warning(Exception, object)"/>
public void Warning( Exception exception, FormattableString message ) => WriteToTargets( NLog.LogLevel.Warn, exception, message );
/// <summary>
/// Log an exception as a warning, with given message override.
/// </summary>
/// <param name="exception">The exception to log.</param>
/// <param name="message">The text to override exceptions' message with in the log.</param>
public void Warning( Exception exception, object message ) => Warning( exception, $"{message}" );
/// <summary>
/// Log some information. This is the default log severity level.
/// </summary>
/// <param name="message">The information to log.</param>
public void Info( object message )
{
if ( message is Exception ex )
{
Warning( ex, ex.Message );
return;
}
Info( $"{message}" );
}
/// <summary>
/// Log some information. This is least severe log level.
/// </summary>
/// <param name="message">The information to log.</param>
public void Trace( object message )
{
if ( message is Exception ex )
{
Warning( ex, ex.Message );
return;
}
Trace( $"{message}" );
}
/// <summary>
/// Log a warning. This is the second most severe log level.
/// </summary>
/// <param name="message">The warning to log.</param>
public void Warning( object message )
{
if ( message is Exception ex )
{
Warning( ex, ex.Message );
return;
}
Warning( $"{message}" );
}
/// <summary>
/// Log an error. This is the most severe log level.
/// </summary>
/// <param name="message">The error to log.</param>
public void Error( object message )
{
if ( message is Exception ex )
{
Error( ex, ex.Message );
return;
}
Error( $"{message}" );
}
internal void WriteToTargets( NLog.LogLevel nlogLevel, Exception ex, FormattableString message, string name = null )
{
name ??= Name;
LogLevel level = nlogLevel.Ordinal switch
{
0 => LogLevel.Trace,
1 => LogLevel.Trace,
2 => LogLevel.Info,
3 => LogLevel.Warn,
4 => LogLevel.Error,
5 => LogLevel.Error,
_ => LogLevel.Info,
};
if ( !Logging.ShouldLog( name, level ) )
return;
var defaultMessage = message.ToString();
var arguments = new List<object>();
var htmlMessage = (string)WrapObject( message, arguments );
// Only show the first line in the console, but inspecting that line will show the rest
var firstLineBreakIndex = htmlMessage.IndexOf( "\n", StringComparison.Ordinal );
if ( firstLineBreakIndex > -1 )
{
htmlMessage = htmlMessage.Substring( 0, firstLineBreakIndex ).TrimEnd();
}
var logEvent = LogEventInfo.Create( nlogLevel, name, defaultMessage );
if ( ex != null )
{
logEvent.Exception = ex;
}
else
{
var stackTrace = new StackTrace( 0, true );
logEvent.SetStackTrace( stackTrace, 0 );
}
_log.Log( logEvent );
string stacktrace = null;
if ( logEvent.Exception != null )
{
stacktrace = GameLog.WriteExceptionDetails( logEvent.Exception );
}
if ( stacktrace == null && logEvent.StackTrace != null )
{
stacktrace = logEvent.StackTrace.ToString();
}
var e = new LogEvent
{
Level = level,
Logger = logEvent.LoggerName,
Message = defaultMessage,
Exception = ex,
HtmlMessage = htmlMessage,
Stack = stacktrace,
Time = DateTime.Now,
Arguments = arguments.ToArray()
};
Logging.Write( e );
}
/// <summary>
/// Wrap / escape an object for html log messages. Inspectable objects
/// will be wrapped in a link, and added to <paramref name="outArgs"/>.
/// The link will index into <paramref name="outArgs"/>. <see cref="FormattableString"/>s
/// will recurse into <see cref="WrapObject"/>, so their arguments can also be inspected.
/// </summary>
/// <param name="o">Object to wrap</param>
/// <param name="outArgs">Inspectable objects will be added here</param>
/// <returns>Html-wrapped object. Either a string or a primitive.</returns>
static object WrapObject( object o, List<object> outArgs )
{
if ( o == null ) return "null";
if ( o is FormattableString formattable )
{
var wrappedArgs = formattable.GetArguments()
.Select( x => WrapObject( x, outArgs ) )
.ToArray();
return string.Format( formattable.Format, wrappedArgs );
}
if ( o is string ) return HttpUtility.HtmlEncode( o );
var t = o.GetType();
if ( t.IsPrimitive ) return o;
var index = outArgs.Count;
outArgs.Add( o );
return $"<a href=\"arg:{index}\" style=\"\">{HttpUtility.HtmlEncode( o.ToString() )}</a>";
}
}