ASP.NET Core Web API 静态类中的日志记录

62

我已经在我的控制器上使用依赖注入来记录日志了,现在我需要从一个静态类中记录日志。

如何从静态类中记录日志?

由于它是静态的,我不能使用依赖注入,也不能将现有的日志记录器对象传递给静态类,因为在日志文件中会有错误的类名。

换句话说,我该如何从loggerfactory中获取一个记录器以在静态类中使用?

我看到有个类似的问题,并且他们指向了一篇关于在静态类中使用loggerfactory的文章,但实际上这并不起作用,因为我无法从中获取新的记录器,因为它不接受静态类作为参数:

“静态类型不能用作类型参数”


2
你解决了这个问题吗?我想从扩展方法中记录日志。 - illug
解决方案在这里 @illug - JohnC
将以下与编程有关的内容从英语翻译成中文。仅返回翻译后的文本:相关链接:https://dev59.com/IVUL5IYBdhLWcg3wM1k6 - Ruben Bartelink
6个回答

90

解决方法是在启动时初始化一个实用静态类中的LoggerFactory的静态引用:

/// <summary>
/// Shared logger
/// </summary>
internal static class ApplicationLogging
{
    internal static ILoggerFactory LoggerFactory { get; set; }// = new LoggerFactory();
    internal static ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();        
    internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
}

你需要在Startup.cs中进行初始化:

public Startup(ILogger<Startup> logger, ILoggerFactory logFactory, IHostingEnvironment hostingEnvironment)
{
    _log = logger;
    _hostingEnvironment = hostingEnvironment;
    Util.ApplicationLogging.LoggerFactory = logFactory;//<===HERE
}

然后,您可以像这样从静态类中构建一个日志记录器:

internal static class CoreJobSweeper
{
    private static ILogger log = Util.ApplicationLogging.CreateLogger("CoreJobSweeper");
}

1
太好了!不过最好再加上一个强类型的CreateLogger示例,特别是对于我们这些只想快速解决问题的复制/粘贴爱好者 :) 即 private static ILogger log = Util.ApplicationLogging.CreateLogger<CoreJobSweeper>(); - alv
3
这个已经不再像 https://github.com/aspnet/Announcements/issues/353 中的公告所述一样工作。注入 ILogger 和 ILoggerFactory 已经不再起作用。新的 ApplicationInsightsLoggerProvider 可以捕获应用程序启动管道早期的日志。虽然ApplicationInsightsLoggerProvider会在Application Insights中自动启用(从版本2.7.1开始),但它直到稍后的管道才设置仪表板密钥。因此,只有来自Controller/其他类的日志将被捕获... - Anders
3
解决方案是将ILogger<Startup> logger和ILoggerFactory logFactory移动到Configure()中。 - Anders
3
但是如果静态类在没有与启动服务器项目相关的项目/dll中呢? - user1785960
3
你好...在没有Startup类的.NET Core 6中如何实现这个? - jstuardo
显示剩余2条评论

21

我发现这个带答案的问题很有用,但是我记得我已经安装了Serilog。我可以使用Serilog自带的默认实现,而不是实现一个自定义的静态实例。

它带有多个很好的日志记录功能和多个重载版本。以下是两个示例:

using Serilog;
...
    Log.Error(myException, "my message");
    Log.Warning("My message", myValue);

Serilog.Log 是一个静态实例,在 Program.cs 或 Startup.cs 中配置后即可使用。


如果您不需要使用关注点分离原则,那么这将起作用,其中您可以轻松替换日志记录框架(如果需要)。 - Banshee
1
如果想在自定义记录器中使用Serilog,可以让自定义记录器基本上成为Serilog的包装器 :) 要获取到调用类的绑定,就像OP所要求的那样,可以实现类似@Adrian描述的带类型参数的工厂方法。 - pekaaw
你正在编写具体代码,而不是针对接口进行编码。未来的读者,只需“注意”这种情况即可。 - granadaCoder
@Banshee - 如果你不想依赖于抽象化,那么这个方法是可行的,因为如果需要的话,你可以轻松地替换日志框架。 - Suamere
作为对我上一条评论的澄清:关注点分离是观察应用程序的不同垂直关注点的一种方法:设计、UI逻辑、API、BLL、DLL、持久性。横切关注点是当您在特定层次上水平共享逻辑时发生的情况。例如:身份验证(或日志记录、缓存等)是跨API1、API2、API3等的CCC。最后,有界上下文是观察模型所有权(低耦合、高内聚)在水平拆分的应用程序或微服务之间的情况。 - Suamere

7
您可以使用静态的LoggerFactory实例,通过以下扩展方法来接受一个普通类型参数Type,而不是一个泛型类型参数: CreateLogger(ILoggerFactory, Type) 例如:loggerFactory.CreateLogger(typeof(T)) 而不是 loggerFactory.CreateLogger<T>()

8
你的意思是 "一个 静态的 LoggerFactory 实例吗?如果你没有设置一个静态的实例,是没有可用的静态 LoggerFactory 实例的。 - Daniel

1

我已在Startup类中添加了一个静态的ILoggerFactory成员,以便在其他类中使用。

public class Startup
{
    internal static ILoggerFactory LogFactory { get; set; }
    …

    public void Configure(…, ILoggerFactory logFactory)
    {
        …
        LogFactory = logFactory;
    }
}

然后在静态类中使用它。
static class YourClass
{
    private static ILogger<YourClass> Logger = Startup.LogFactory.CreateLogger<YourClass>();
…

然而,当CreateLogger抛出异常时,这段代码将导致TypeInitializationException异常,因此您应该尽力改进。 [1]

-1
首先,我同意NightOwl888的回答。
但是作为一种选择(将其视为解决方法),您可以通过方法参数注入ILogger依赖项,所以调用此静态方法的类应该负责提供正确的ILogger实现。
static class YourClass
{
    public static void DoSomething(ILogger logger)
    {
         // do something
         // call log.Log();
    }
}

9
Nightowl完全没有回答问题,反而发表了一个不需要的演讲。我在问题中明确阐述了你建议的内容以及为什么我不能这样做。 - JohnC
7
要在任何地方记录日志,你需要在你成千上万行的代码中强制引入所需的日志依赖项。我认为这听起来很糟糕。在这种情况下,静态工厂是更明智的选择。 - Cesar

-1

这是@JohnC和@Pekaaw的综合答案,

可以从SeriLog记录器创建LoggingFactory以保留相同的日志配置,并可用于日志记录机制。

(Microsoft.Extensions.Logging.LoggerFactory)Serilog.SerilogLoggerFactoryExtensions.AddSerilog(new Microsoft.Extensions.Logging.LoggerFactory(), Serilog.Log.Logger);

/// <summary>
/// Shared logger
/// </summary>
internal static class ApplicationLogging
{
    internal static ILoggerFactory LoggerFactory { get; set; } = (Microsoft.Extensions.Logging.LoggerFactory)Serilog.SerilogLoggerFactoryExtensions.AddSerilog(new Microsoft.Extensions.Logging.LoggerFactory(), Serilog.Log.Logger);
    internal static Microsoft.Extensions.Logging.ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();
    internal static Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
}

public static class YourClass
{
   public static readonly ILogger _logger = ApplicationLogging.CreateLogger("YourClass");
   public static void MyMethod(string param)
   {
      try
      {
         _logger.LogInformation($"Inside MyMethod { param }");
         //Todo: my static code;
      }
   }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接