我可以使用Serilog记录到不同的文件吗?

8

我的ASP.NET Core 2.1应用程序记录到Serilog文件汇,包括所有“通常的东西”——例如与应用程序相关的调试、监控、性能等。

但是,我们还需要将其他数据记录到单独的文件中。不是与应用程序相关的客户相关数据,这些数据应该存储到数据库中。但由于遗留原因,该系统上没有数据库,因此必须将这些数据保存到文件中。显然,它不能写入同一日志文件。

我可以简单地写入FileStream。但我更喜欢使用Serilog进行结构化日志记录。

那么是否有同时拥有两个记录器的方法?以便将不同的数据记录到不同的文件汇中。

(如果有的话,如何将它们注入到我的类中-现在我只注入ILogger<ClassName>。)


你似乎认为数据库和文件是你唯一的选择。实际上,有大量的日志库可供选择。在我们公司,我们喜欢使用Seq。 - mason
1
很抱歉,这个项目只有这些选项。而且,即使我使用不同的sink,我也不确定它如何解决问题(记录不同的数据到不同的sink - 可能需要使用不同的logger)。 - lonix
3个回答

9
我最简单的做法是实现一个新的独特接口,将其传递给DI系统,由控制器调用。
注意:在示例中,我使用了Serilog.Sinks.RollingFile NuGet包。根据需要可以使用其他Sinks。我使用asp.net Core 2.1。
using Serilog;
using Serilog.Core;

public interface ICustomLogger
{
    ILogger Log { get; }
}

public class CustomLogger : ICustomLogger
{
    private readonly Logger _logger;

    public ILogger Log { get { return _logger; } }

    public CustomLogger( Logger logger )
    {
        _logger = logger;
    }
}

在Startup.cs中的实现(.Net 6之前)

using Serilog;
...
public void ConfigureServices( IServiceCollection services )
{
    ...

    ICustomLogger customLogger = new CustomLogger( new LoggerConfiguration()
        .MinimumLevel.Debug()
        .WriteTo.RollingFile( @"Logs\CustomLog.{Date}.log", retainedFileCountLimit: 7 )
        .CreateLogger() );
    services.AddSingleton( customLogger );
     
    ...
}

在Program.cs中实现(.Net 6)

using Serilog;
...

... 
ICustomLogger customLogger = new CustomLogger( new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.RollingFile( @"Logs\CustomLog.{Date}.log", retainedFileCountLimit: 7 )
    .CreateLogger() );
builder.Services.AddSingleton( customLogger );
     
...

控制器中的使用

public class MyTestController : Controller
{
    private readonly ICustomLogger _customLogger;

    public MyTestController( ICustomLogger customLogger )
    {
        _customLogger = customLogger;
    }

    public IActionResult Index()
    {
        ...
        _customLogger.Log.Debug( "Serving Index" );
        ...
    }
}

谢谢您。我能够将其与.Net 6格式一起使用,并将其注入到控制器和Blazor中。它不是在Startup.cs中实现,而是在Program.cs中实现,而且不是services.AddSingleton,而是builder.Services.AddSingleton。令人困惑的是,大多数依赖注入都需要注入<class>,但在这种情况下并不需要。依赖注入非常重要,否则应用程序无法重复使用,并且如果按控制器或页面的方式进行操作,则会被锁定。 - drewid
通过我的实现和编辑,我现在有了常规的Serilog日志记录正常工作,以及我的第二个特殊情况日志记录将其内容写入自己的文件(例如,如果您需要将管理员活动或创建活动写入自己的文件等,则这可能非常有用)。 - drewid

7

Serilog.Sinks.Map可以实现这一点,并包括一个文件记录的示例:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Map("EventId", "Other", (name, wt) => wt.File($"./logs/log-{name}.txt"))
    .CreateLogger();

4
你绝对可以做到这一点。
  1. You need to import package Serilog.Sinks.File

  2. Then you have to configure Serilog.

    In program.cs do following thing.

    Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .Enrich.FromLogContext()
        .WriteTo.File(
            @"<<your log file path>>",
        fileSizeLimitBytes: 10000000,
        rollOnFileSizeLimit: true,
        shared: true,
        flushToDiskInterval: TimeSpan.FromSeconds(1))
            .CreateLogger();
    
在 buildWebHost 函数中添加 UseSerilog()。
public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
    .UseStartup<Startup>()
        .UseSerilog() // <-- Add this line
        .Build();

更新1

我使用了EventId属性。这只是演示如何根据eventId使用不同的文件,但对于您的需求,您需要自己实现其他内容。

Program.cs

public class Program
    {
        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .WriteTo.Logger(cc => cc.Filter.ByIncludingOnly(WithProperty("EventId",1001)).WriteTo.File("Test1001.txt",flushToDiskInterval: TimeSpan.FromSeconds(1)))
                .WriteTo.Logger(cc => cc.Filter.ByIncludingOnly(WithProperty("EventId", 2001)).WriteTo.File("Test2001.txt", flushToDiskInterval: TimeSpan.FromSeconds(1)))
                .CreateLogger();

            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args).UseSerilog()
                .UseStartup<Startup>();

        public static Func<LogEvent, bool> WithProperty(string propertyName, object scalarValue)
        {
            if (propertyName == null) throw new ArgumentNullException("propertyName");           
            ScalarValue scalar = new ScalarValue(scalarValue);
            return e=>
            {
                LogEventPropertyValue propertyValue;
                if (e.Properties.TryGetValue(propertyName, out propertyValue))
                {
                    var stValue = propertyValue as StructureValue;
                    if (stValue != null)
                    {
                        var value = stValue.Properties.Where(cc => cc.Name == "Id").FirstOrDefault();
                        bool result = scalar.Equals(value.Value);
                        return result;
                    }
                }
                return false;
            };
        }
    }

我的 HomeController.cs

  public class HomeController : Controller
    {
        ILogger<HomeController> logger;
        public HomeController(ILogger<HomeController> logger)
        {
            this.logger = logger;
        }
        public IActionResult Index()
        {
            logger.Log(LogLevel.Information,new EventId(1001), "This is test 1");
            logger.Log(LogLevel.Information, new EventId(2001), "This is test 2");
            return View();
        } 
    }

注意:最重要的是您必须使用某种类型的过滤器。


谢谢。是的,我已经这样做了,但它只写入一个日志文件。这是标准方法。我的用例不同。 - lonix
@lonix 你知道可以创建一个拥有自己配置的新 Logger 实例而不是使用静态 logger 吗? - mason
是的,但就像我的问题所说,我该如何在应用程序中使用它?标准记录器是静态的,但也可以注入,但其他记录器呢?在容器中创建它的好地方在哪里,应该如何存储等等。我找不到任何关于这种用例的解释。 - lonix
请查看我的更新 1。我已经提供了接近您要求的样本。 - dotnetstep
很酷,谢谢。唯一的问题是你需要在每个日志调用中包含这些属性。这很丑陋和混乱。但如果这是唯一的方法,那就足够好了。再次感谢。 - lonix

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