You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 20:37:47