.NET如何手动渲染结构化日志模板?求匹配签名的渲染方法
.NET 结构化日志模板手动渲染方案
在.NET中,结构化日志的常规写法如下:
logger.LogInformation("Hello {What}", "World");
其渲染后的输出结果为:
Hello World
由于上游代码无法修改,需要手动渲染这类日志模板,但string.Format(message, args)并不适配结构化日志的占位符格式,因此需要寻找签名类似string Render(string message, params object[] args)的实现方法,以下是可行方案:
基于Microsoft.Extensions.Logging的实现
Microsoft.Extensions.Logging未直接提供公共的渲染方法,但可以借助其内部的LoggerMessage相关逻辑实现,以下是两种可行方式:
方式一:利用LogValuesFormatter
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; public static string RenderLogTemplate(string message, params object[] args) { var formatter = LoggerMessage.CreateLogValuesFormatter(message, args.Length); var logValues = formatter.GetValues(args); return formatter.Format(logValues); }
注意:
CreateLogValuesFormatter属于内部API,不同.NET版本可能存在兼容性风险。
方式二:自定义Logger捕获输出
通过定义日志委托并使用自定义Logger捕获格式化后的消息:
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; public static string RenderLogTemplate(string message, params object[] args) { var capturingLogger = new StringCapturingLogger(); LoggerMessage.Define(LogLevel.Information, new EventId(0), message) .Invoke(capturingLogger, args, null); return capturingLogger.CapturedMessage; } private class StringCapturingLogger : ILogger { public string CapturedMessage { get; private set; } public IDisposable BeginScope<TState>(TState state) => NullScope.Instance; public bool IsEnabled(LogLevel logLevel) => true; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { CapturedMessage = formatter(state, exception); } }
基于第三方库Serilog的实现
Serilog提供了专门的消息模板格式化类MessageTemplateTextFormatter,可以直接处理结构化日志模板:
using Serilog.Formatting.Display; using System.IO; using System.Reflection; public static string RenderLogTemplate(string message, params object[] args) { var formatter = new MessageTemplateTextFormatter(message, null); // 若模板使用顺序占位符,按索引映射为Property0、Property1... var properties = new Dictionary<string, object>(); for (int i = 0; i < args.Length; i++) { properties.Add($"Property{i}", args[i]); } using var writer = new StringWriter(); formatter.Format(properties, writer); return writer.ToString().Trim(); } // 针对命名占位符的专用方法(如模板"Hello {What}") public static string RenderNamedTemplate(string message, object namedProperties) { var formatter = new MessageTemplateTextFormatter(message, null); var properties = namedProperties.GetType().GetProperties() .ToDictionary(p => p.Name, p => p.GetValue(namedProperties)); using var writer = new StringWriter(); formatter.Format(properties, writer); return writer.ToString().Trim(); } // 使用示例:RenderNamedTemplate("Hello {What}", new { What = "World" });
内容的提问来源于stack exchange,提问作者lonix
相关产品推荐
相关产品推荐

