Skip to content

Instantly share code, notes, and snippets.

@bronumski
Last active May 4, 2022 17:25
Show Gist options
  • Save bronumski/8ffbd37b9446794be4acc5a9a810c776 to your computer and use it in GitHub Desktop.
Save bronumski/8ffbd37b9446794be4acc5a9a810c776 to your computer and use it in GitHub Desktop.
Using `InterpolatedStringHandler` to support writing safe structured log messages
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}");
[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())!;
}
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