rd-net/Lifetimes/Diagnostics/LogEx.cs (400 lines of code) (raw):
using System;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using JetBrains.Diagnostics.StringInterpolation;
namespace JetBrains.Diagnostics
{
public static class LogEx
{
public static ILog GetSublogger(this ILog log, string subcategory)
{
return Log.GetLog(log.Category + "." + subcategory);
}
#region IsEnabled
public static bool IsTraceEnabled(this ILog @this)
{
return @this.IsEnabled(LoggingLevel.TRACE);
}
public static bool IsVersboseEnabled(this ILog @this)
{
return @this.IsEnabled(LoggingLevel.VERBOSE);
}
#endregion
#region LogFormat
[StringFormatMethod("message")]
public static void LogFormat<T1>(this ILog @this, LoggingLevel level, string message, T1 t1)
{
if (@this.IsEnabled(level))
{
@this.Log(level, message.FormatEx(t1));
}
}
[StringFormatMethod("message")]
public static void LogFormat<T1, T2>(this ILog @this, LoggingLevel level, string message, T1 t1, T2 t2)
{
if (@this.IsEnabled(level))
{
@this.Log(level, message.FormatEx(t1, t2));
}
}
[StringFormatMethod("message")]
public static void LogFormat<T1, T2, T3>(this ILog @this, LoggingLevel level, string message, T1 t1, T2 t2, T3 t3)
{
if (@this.IsEnabled(level))
{
@this.Log(level, message.FormatEx(t1, t2, t3));
}
}
[StringFormatMethod("message")]
public static void LogFormat<T1, T2, T3, T4>(this ILog @this, LoggingLevel level, string message, T1 t1, T2 t2, T3 t3, T4 t4)
{
if (@this.IsEnabled(level))
{
@this.Log(level, message.FormatEx(t1, t2, t3, t4));
}
}
//Universal method for many parameter
[StringFormatMethod("message")]
public static void LogFormat(this ILog @this, LoggingLevel level, string message, params object?[] args)
{
if (@this.IsEnabled(level))
{
@this.Log(level, message.FormatEx(args));
}
}
#endregion
#region Trace
public static void Trace(this ILog @this, string message)
{
@this.Log(LoggingLevel.TRACE, message);
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.TRACE"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Trace($"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogTraceInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Trace(ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="messageHandler"></param>
public static void Trace(this ILog logger, [InterpolatedStringHandlerArgument("logger")] ref JetLogTraceInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Log(LoggingLevel.TRACE, messageHandler.ToStringAndClear());
}
}
[StringFormatMethod("message")]
public static void Trace<T1>(this ILog @this, string message, T1 t1)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1);
}
[StringFormatMethod("message")]
public static void Trace<T1, T2>(this ILog @this, string message, T1 t1, T2 t2)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1, t2);
}
[StringFormatMethod("message")]
public static void Trace<T1, T2, T3>(this ILog @this, string message, T1 t1, T2 t2, T3 t3)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1, t2, t3);
}
[StringFormatMethod("message")]
public static void Trace<T1, T2, T3, T4>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1, t2, t3, t4);
}
[StringFormatMethod("message")]
public static void Trace<T1, T2, T3, T4, T5>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1, t2, t3, t4, t5);
}
[StringFormatMethod("message")]
public static void Trace<T1, T2, T3, T4, T5, T6>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
@this.LogFormat(LoggingLevel.TRACE, message, t1, t2, t3, t4, t5, t6);
}
#endregion
#region Verbose
public static void Verbose(this ILog @this, string message)
{
@this.Log(LoggingLevel.VERBOSE, message);
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.VERBOSE"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Verbose($"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogVerboseInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Verbose(ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="messageHandler"></param>
public static void Verbose(this ILog logger, [InterpolatedStringHandlerArgument("logger")] ref JetLogVerboseInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Log(LoggingLevel.VERBOSE, messageHandler.ToStringAndClear());
}
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.VERBOSE"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Verbose(ex, $"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogVerboseInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Verbose(ex, ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="ex"></param>
/// <param name="messageHandler"></param>
public static void Verbose(this ILog logger, Exception ex, [InterpolatedStringHandlerArgument("logger")] ref JetLogVerboseInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Verbose(ex, messageHandler.ToStringAndClear());
}
}
[StringFormatMethod("message")]
public static void Verbose<T1>(this ILog @this, string message, T1 t1)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1);
}
[StringFormatMethod("message")]
public static void Verbose<T1, T2>(this ILog @this, string message, T1 t1, T2 t2)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1, t2);
}
[StringFormatMethod("message")]
public static void Verbose<T1, T2, T3>(this ILog @this, string message, T1 t1, T2 t2, T3 t3)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1, t2, t3);
}
[StringFormatMethod("message")]
public static void Verbose<T1, T2, T3, T4>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1, t2, t3, t4);
}
[StringFormatMethod("message")]
public static void Verbose<T1, T2, T3, T4, T5>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1, t2, t3, t4, t5);
}
[StringFormatMethod("message")]
public static void Verbose<T1, T2, T3, T4, T5, T6>(this ILog @this, string message, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, t1, t2, t3, t4, t5, t6);
}
[StringFormatMethod("message")]
public static void Verbose(this ILog @this, string message, object[] args)
{
@this.LogFormat(LoggingLevel.VERBOSE, message, args);
}
public static void Verbose(this ILog @this, Exception ex, string? message = null)
{
@this.Log(LoggingLevel.VERBOSE, message, ex);
}
#endregion
#region Info
public static void Info(this ILog @this, string message)
{
@this.Log(LoggingLevel.INFO, message);
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.INFO"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Info($"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogInfoInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Info(ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="messageHandler"></param>
public static void Info(this ILog logger, [InterpolatedStringHandlerArgument("logger")] ref JetLogInfoInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Log(LoggingLevel.INFO, messageHandler.ToStringAndClear());
}
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.INFO"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Info(ex, $"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogInfoInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Info(ex, ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="ex"></param>
/// <param name="messageHandler"></param>
public static void Info(this ILog logger, Exception ex, [InterpolatedStringHandlerArgument("logger")] ref JetLogInfoInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Info(ex, messageHandler.ToStringAndClear());
}
}
[StringFormatMethod("message")]
public static void Info(this ILog @this, string message, params object[] args)
{
@this.LogFormat(LoggingLevel.INFO, message, args);
}
public static void Info(this ILog @this, Exception ex, string? message = null)
{
@this.Log(LoggingLevel.INFO, message, ex);
}
#endregion
#region Warn
public static void Warn(this ILog @this, string message)
{
@this.Log(LoggingLevel.WARN, message);
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.WARN"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Warn($"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogWarnInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Warn(ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="messageHandler"></param>
public static void Warn(this ILog logger, [InterpolatedStringHandlerArgument("logger")] ref JetLogWarnInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Log(LoggingLevel.WARN, messageHandler.ToStringAndClear());
}
}
/// Log the message if <see cref="LoggingLevel.WARN"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Warn(ex, $"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogWarnInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Warn(ex, ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="ex"></param>
/// <param name="messageHandler"></param>
public static void Warn(this ILog logger, Exception ex, [InterpolatedStringHandlerArgument("logger")] ref JetLogWarnInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Warn(ex, messageHandler.ToStringAndClear());
}
}
[StringFormatMethod("message")]
public static void Warn(this ILog @this, string message, params object[] args)
{
@this.LogFormat(LoggingLevel.WARN, message, args);
}
public static void Warn(this ILog @this, Exception ex, string? message = null)
{
@this.Log(LoggingLevel.WARN, message, ex);
}
#endregion
#region Error
[StringFormatMethod("message")]
public static void Error(this ILog @this, string message)
{
@this.Log(LoggingLevel.ERROR, message);
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.ERROR"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Error($"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogErrorInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Error(ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="messageHandler"></param>
public static void Error(this ILog logger, [InterpolatedStringHandlerArgument("logger")] ref JetLogErrorInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Log(LoggingLevel.ERROR, messageHandler.ToStringAndClear());
}
}
/// <summary>
/// Log the message if <see cref="LoggingLevel.ERROR"/> is enabled, otherwise the message will not be logged, moreover, no calculations (including method calls) will be performed.
/// <br />
/// <br />
/// For example, the code below
/// <code>
/// logger.Error(ex, $"{DoSmthSlow()}");
/// </code>
///
/// will be compiled into
///
/// <code>
/// var handler = new JetLogErrorInterpolatedStringHandler(logger, out var isEnabled);
/// if (isEnabled)
/// handler.Append(DoSmthSlow());
/// logger.Error(ex, ref handler);
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <param name="ex"></param>
/// <param name="messageHandler"></param>
[StringFormatMethod("message")]
public static void Error(this ILog logger, Exception ex, [InterpolatedStringHandlerArgument("logger")] ref JetLogErrorInterpolatedStringHandler messageHandler)
{
if (messageHandler.IsEnabled)
{
logger.Error(ex, messageHandler.ToStringAndClear());
}
}
[StringFormatMethod("message")]
public static void Error(this ILog @this, string message, params object?[] args)
{
@this.LogFormat(LoggingLevel.ERROR, message, args);
}
[StringFormatMethod("message")]
public static void Error(this ILog @this, string message, Exception e)
{
@this.Log(LoggingLevel.ERROR, message, e);
}
[StringFormatMethod("message")]
public static void Error(this ILog @this, Exception ex, string? message = null)
{
@this.Log(LoggingLevel.ERROR, message, ex);
}
#endregion
#region Assert
public static void Assert(this ILog @this, bool condition, [CallerArgumentExpression("condition")] string? message = null)
{
if (!condition)
{
@this.Error(message ?? "");
}
}
public static void Assert<T>(this ILog @this, bool condition, string message, T t1)
{
if (!condition)
{
@this.Error(message, t1);
}
}
public static void Assert(this ILog @this, bool condition, string message, params object[] args)
{
if (!condition)
{
@this.Error(message, args);
}
}
#endregion
#region Catch
/// <summary>
/// Run <paramref name="action"/> and in case of exception log it with <see cref="LoggingLevel"/> == ERROR. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
public static void Catch(this ILog log, [InstantHandle] Action action)
{
try
{
action();
}
catch(Exception e)
{
log.Error(e);
}
}
/// <summary>
/// Run <paramref name="action"/> and in case of exception log it with <see cref="LoggingLevel"/> == ERROR. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
/// <returns>result of action() or <c>default(T)></c> if exception arises</returns>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
[PublicAPI] public static T? Catch<T>(this ILog log, [InstantHandle] Func<T> action)
{
try
{
return action();
}
catch(Exception e)
{
log.Error(e);
return default;
}
}
/// <summary>
/// Run <paramref name="action"/> and in case of exception discard it. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
[PublicAPI] public static void CatchAndDrop(this ILog log, [InstantHandle] Action action)
{
try
{
action();
}
catch(Exception e)
{
DropException(e);
}
}
/// <summary>
/// Run <paramref name="action"/> and in case of exception discard it. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
/// <returns>result of action() or <c>default(T)></c> if exception arises</returns>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
[PublicAPI] public static T? CatchAndDrop<T>(this ILog log, [InstantHandle] Func<T> action)
{
try
{
return action();
}
catch(Exception e)
{
DropException(e);
return default;
}
}
/// <summary>
/// Run <paramref name="action"/> and in case of exception log it with <see cref="LoggingLevel"/> == WARN. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
[PublicAPI] public static void CatchWarn(this ILog log, [InstantHandle] Action action)
{
try
{
action();
}
catch(Exception e)
{
log.Warn(e);
}
}
/// <summary>
/// Run <paramref name="action"/> and in case of exception log it with <see cref="LoggingLevel"/> == WARN. Do not throw exception (if any).
/// </summary>
/// <param name="log"></param>
/// <param name="action"></param>
/// <returns>result of action() or <c>default(T)></c> if exception arises</returns>
#if !NETCOREAPP
[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
#endif
[PublicAPI] public static T? CatchWarn<T>(this ILog log, [InstantHandle] Func<T> action)
{
try
{
return action();
}
catch(Exception e)
{
log.Warn(e);
return default;
}
}
private static void DropException(Exception e)
{
//do nothing
}
#endregion
#region Helpers
[MethodImpl(MethodImplOptions.NoInlining)]
[StringFormatMethod("s")]
private static string FormatEx(this string s, params object?[] p)
{
return string.Format(s, p);
}
#endregion
#region LogWithLevel
/// <summary>
/// One-line shortcut builder to replace common pattern:
/// <code>
/// if (logger.IsTraceEnabled())
/// logger.Trace($"some messages with {HeavyComputation()}"")
/// </code>
/// Usage:
/// <code>
/// logger.WhenTrace()?.Log($"some messages with {HeavyComputation()}")
/// </code>
/// </summary>
/// <param name="logger"/>
/// <returns>struct <see cref="LogWithLevel"/>(<paramref name="logger"/>, <see cref="LoggingLevel.TRACE"/>) if <see cref="IsTraceEnabled"/>;
/// null otherwise</returns>
[PublicAPI] public static LogWithLevel? WhenTrace(this ILog logger)
{
return LogWithLevel.CreateIfEnabled(logger, LoggingLevel.TRACE);
}
/// <summary>
/// One-line shortcut builder to replace common pattern:
/// <code>
/// if (logger.IsVerboseEnabled())
/// logger.Verbose($"some messages with {HeavyComputation()}"")
/// </code>
/// Usage:
/// <code>
/// logger.WhenVerbose()?.Log($"some messages with {HeavyComputation()}")
/// </code>
/// </summary>
/// <param name="logger"></param>
/// <returns>struct <see cref="LogWithLevel"/>(<paramref name="logger"/>, <see cref="LoggingLevel.VERBOSE"/>) if <see cref="IsVersboseEnabled"/>;
/// null otherwise</returns>
[PublicAPI] public static LogWithLevel? WhenVerbose(this ILog logger)
{
return LogWithLevel.CreateIfEnabled(logger, LoggingLevel.VERBOSE);
}
[Obsolete("Renamed to WhenTrace")]
public static LogWithLevel? Trace(this ILog logger) => logger.WhenTrace();
[Obsolete("Renamed to WhenVerbose")]
public static LogWithLevel? Verbose(this ILog logger) => logger.WhenVerbose();
#endregion
}
}