Last active
May 4, 2022 17:25
-
-
Save bronumski/8ffbd37b9446794be4acc5a9a810c776 to your computer and use it in GitHub Desktop.
Using `InterpolatedStringHandler` to support writing safe structured log messages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
logger.LogInformation($"We can now do this {someCode}"); | |
logger.LogInformation($"And this {1 + 1:Calculated}"); | |
logger.LogInformation($"And also this {() => ExpensiveMethodCall(TimeSpan.FromSeconds(1)):Expensive}"); | |
logger.LogInformation($"And not worry about {UnsafeMethodCall:Unsafe}"); | |
logger.LogTrace($"And not have to check if the log level is enabled {() => ExpensiveMethodCall(TimeSpan.FromSeconds(10)):Expensive}"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[InterpolatedStringHandler] | |
public readonly ref struct CriticalStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public CriticalStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Critical); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct DebugStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public DebugStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Debug); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct ErrorStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public ErrorStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Error); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct InformationStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public InformationStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Information); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct TraceStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public TraceStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Trace); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct WarningStructuredLoggingInterpolatedStringHandler | |
{ | |
internal StructuredLoggingInterpolatedStringHandler _innerHandler { get; } | |
public WarningStructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger) => _innerHandler = | |
new StructuredLoggingInterpolatedStringHandler(literalLength, formattedCount, logger, LogLevel.Warning); | |
public void AppendLiteral(string s) => _innerHandler.AppendLiteral(s); | |
public void AppendFormatted<T>( | |
T value, string? format = null, | |
[CallerArgumentExpression("value")] string name = "") => _innerHandler.AppendFormatted(value, format, name); | |
public void AppendFormatted<T>(Func<T> value, string format) => _innerHandler.AppendFormatted(value, format); | |
} | |
[InterpolatedStringHandler] | |
public readonly ref struct StructuredLoggingInterpolatedStringHandler | |
{ | |
private readonly ILogger _logger = null!; | |
private readonly StringBuilder _template = null!; | |
private readonly List<object?> _arguments = null!; | |
private readonly bool _isDisabled; | |
public StructuredLoggingInterpolatedStringHandler( | |
int literalLength, int formattedCount, ILogger logger, LogLevel logLevel) | |
{ | |
_isDisabled = !logger.IsEnabled(logLevel); | |
if (_isDisabled) return; | |
_logger = logger; | |
_template = new StringBuilder(literalLength); | |
_arguments = new List<object?>(formattedCount); | |
} | |
public void AppendLiteral(string s) | |
{ | |
if (_isDisabled) return; | |
_template.Append(s.Replace("{", "{{", StringComparison.Ordinal).Replace("}", "}}", StringComparison.Ordinal)); | |
} | |
public void AppendFormatted<T>(T value, string? format = null, [CallerArgumentExpression("value")] string name = "") | |
{ | |
if (_isDisabled) return; | |
_arguments.Add(value); | |
_template.Append($"{{{format ?? name}}}"); | |
} | |
public void AppendFormatted<T>(Func<T> value, string format) | |
{ | |
if (_isDisabled) return; | |
try | |
{ | |
_arguments.Add(value()); | |
} | |
catch (Exception e) | |
{ | |
_arguments.Add("<error resolving argument>"); | |
_logger.LogError(e, "Error logging parameter"); | |
} | |
_template.Append($"{{{format}}}"); | |
} | |
public (string template, object[] arguments)? GetTemplateAndArguments() => | |
_isDisabled ? null : (_template.ToString(), _arguments.ToArray())!; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class LoggingExtensions | |
{ | |
public static void LogTrace(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref TraceStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Trace, null); | |
public static void LogTrace(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref TraceStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Trace, ex); | |
public static void LogDebug(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref DebugStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Debug, null); | |
public static void LogDebug(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref DebugStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Debug, ex); | |
public static void LogInformation(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref InformationStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Information, null); | |
public static void LogInformation(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref InformationStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Information, ex); | |
public static void LogWarning(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref WarningStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Warning, null); | |
public static void LogWarning(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref WarningStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Warning, ex); | |
public static void LogError(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref ErrorStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Error, null); | |
public static void LogError(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref ErrorStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Error, ex); | |
public static void LogCritical(this ILogger logger, | |
[InterpolatedStringHandlerArgument("logger")] ref CriticalStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Critical, null); | |
public static void LogCritical(this ILogger logger, Exception ex, | |
[InterpolatedStringHandlerArgument("logger")] ref CriticalStructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler._innerHandler.GetTemplateAndArguments(), LogLevel.Critical, ex); | |
public static void Log(this ILogger logger, LogLevel logLevel, | |
[InterpolatedStringHandlerArgument("logger", "logLevel")] ref StructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(logLevel, null, ref handler); | |
public static void Log(this ILogger logger, LogLevel logLevel, Exception? ex, | |
[InterpolatedStringHandlerArgument("logger", "logLevel")] | |
ref StructuredLoggingInterpolatedStringHandler handler) => | |
logger.Log(handler.GetTemplateAndArguments(), logLevel, ex); | |
private static void Log(this ILogger logger, (string template, object[] arguments)? interpolatedValues, LogLevel logLevel, Exception? ex) | |
{ | |
if (!interpolatedValues.HasValue) return; | |
var (template, arguments) = interpolatedValues.Value; | |
logger.Log(logLevel, ex, template, arguments); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment