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

如何在ILogger<TCategoryName>的类别名中包含泛型参数类型名

解决ILogger泛型类别名显示泛型参数的问题

默认情况下,使用ILogger<Foo<string>>时,日志输出的类别名是Foo[0],这是因为.NET默认会把泛型类型的名称(如Foo1)格式化为Foo[0]。要让类别名显示具体的泛型参数类型(如Foo[System.String]),可以通过以下两种方式实现:

方法一:手动指定类别名创建Logger

直接通过ILoggerFactory创建Logger时,传入解析后的泛型类型名称:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

class Foo<T> { }

// 解析泛型类型名称的工具方法
public static string GetGenericTypeName(Type type)
{
    if (!type.IsGenericType)
        return type.FullName ?? type.Name;

    var genericArgs = string.Join(", ", type.GetGenericArguments().Select(GetGenericTypeName));
    return $"{type.Name.Split('`')[0]}[{genericArgs}]";
}

using var services = new ServiceCollection()
    .AddLogging(options => options.AddConsole())
    .BuildServiceProvider();

var loggerFactory = services.GetRequiredService<ILoggerFactory>();
// 手动生成正确的类别名
var logger = loggerFactory.CreateLogger(GetGenericTypeName(typeof(Foo<string>)));
logger.LogInformation("test"); // 输出:info: Foo[System.String]

方法二:通过DI注册自定义泛型Logger(自动处理)

如果希望通过DI获取ILogger<T>时自动使用正确的类别名,可以自定义一个泛型Logger实现,替换默认的ILogger<T>注册:

1. 实现自定义泛型Logger

public class GenericAwareLogger<T> : ILogger<T>
{
    private readonly ILogger _innerLogger;

    public GenericAwareLogger(ILoggerFactory loggerFactory)
    {
        var categoryName = GetGenericTypeName(typeof(T));
        _innerLogger = loggerFactory.CreateLogger(categoryName);
    }

    public IDisposable BeginScope<TState>(TState state) => _innerLogger.BeginScope(state);

    public bool IsEnabled(LogLevel logLevel) => _innerLogger.IsEnabled(logLevel);

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        _innerLogger.Log(logLevel, eventId, state, exception, formatter);
    }

    // 递归解析泛型类型名称
    private static string GetGenericTypeName(Type type)
    {
        if (!type.IsGenericType)
            return type.FullName ?? type.Name;

        var genericArgs = string.Join(", ", type.GetGenericArguments().Select(t => t.FullName ?? t.Name));
        return $"{type.Name.Split('`')[0]}[{genericArgs}]";
    }
}

2. 注册到DI容器

using var services = new ServiceCollection()
    .AddLogging(options => options.AddConsole())
    // 替换默认的ILogger<T>实现
    .AddSingleton(typeof(ILogger<>), typeof(GenericAwareLogger<>))
    .BuildServiceProvider();

// 直接获取ILogger<Foo<string>>,自动使用正确的类别名
var logger = services.GetRequiredService<ILogger<Foo<string>>>();
logger.LogInformation("test"); // 输出:info: Foo[System.String]

可选优化:简化类型名称

如果不想显示完整的System.String,而是用string这种友好名称,可以修改GetGenericTypeName方法,添加常用类型的映射处理:

private static string GetFriendlyTypeName(Type type)
{
    var typeMap = new Dictionary<Type, string>
    {
        { typeof(string), "string" },
        { typeof(int), "int" },
        { typeof(bool), "bool" },
        // 可添加更多常用类型
    };

    if (typeMap.TryGetValue(type, out var friendlyName))
        return friendlyName;

    if (!type.IsGenericType)
        return type.Name;

    var genericArgs = string.Join(", ", type.GetGenericArguments().Select(GetFriendlyTypeName));
    return $"{type.Name.Split('`')[0]}[{genericArgs}]";
}

替换后,Foo<string>会显示为Foo[string],格式更简洁。


内容的提问来源于stack exchange,提问作者Liero

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 00:10:15