Microsoft.Extensions.Logging.ILogger实现按类名写入不同的日志文件

问题描述:记得以前使用Log4net时,可以在config文件中配置多个Appender,
每一个类对应一个Appender,即每个类产生的日志会写入到不同的日志文件中,
可避免日志堆累在一起。
目前环境有所变化,使用的是ABP框架,日志使用的类为Microsoft.Extensions.Logging.ILogger
首先我定义了两个Appender, 其目的就是不同的类日志写入到不同的Appender所定义的日志文件中去

<?xml version="1.0" encoding="utf-8"?>
<log4net>
    <appender name="DataSyncServices" type="log4net.Appender.RollingFileAppender">
        <file value="../../../App_Data/Logs/日志文件1.txt" />
        <appendToFile value="true" />
        <rollingStyle value="Size" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10000KB" />
        <staticLogFileName value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
        </layout>
    </appender>
    <appender name="DataSyncTwoServices" type="log4net.Appender.RollingFileAppender">
        <file value="../../../App_Data/Logs/日志文件2.txt" />
        <appendToFile value="true" />
        <rollingStyle value="Size" />
        <maxSizeRollBackups value="10" />
        <maximumFileSize value="10000KB" />
        <staticLogFileName value="true" />
        <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
        </layout>
    </appender>-->
    <root>
        <appender-ref ref="DataSyncServices" />
        <appender-ref ref="DataSyncTwoServices" />
        <level value="DEBUG" />
    </root>
    <logger name="NHibernate">
        <level value="WARN" />
    </logger>
</log4net>

代码内容如下

private readonly ILogger<DataSyncServices> _logger;
public class DataSyncServices : AMSApiServiceBase, IDataSyncServices
{
    public DataSyncServices(ILogger<DataSyncServices> logger,。。。。)
    {
        _logger = logger; //这里是大家都知道的,应用了依赖注入去实例化日志类
    }

    private void Method()
    {
        ....
        /*
        这里输出至日志文件
        奇怪的是,日志内容会同时写入到日志文件1.txt 和 日志文件2.txt两个文件中,与目的可谓是南辕北辙
        */
        _logger.LogInformation("写入DataSyncServices类产生的日志"); 
    }
}


private readonly ILogger<DataSyncTwoServices> _logger;
public class DataSyncTwoServices : AMSApiServiceBase, IDataSyncTwoServices
{
    public DataSyncTwoServices(ILogger<DataSyncTwoServices> logger,。。。。)
    {
        _logger = logger; //这里是大家都知道的,应用了依赖注入去实例化日志类
    }

    private void Method()
    {
        ....
        /*
        这里输出至日志文件
        奇怪的是,日志内容会同时写入到日志文件1.txt 和 日志文件2.txt两个文件中,与目的可谓是南辕北辙
        */
        _logger.LogInformation("写入DataSyncTwoServices类产生的日志"); 
    }
}



提出问题:我的目的是将不同的类产生的日志输入到不同的文件中去,但是按照上面的做法,
结果相反,一个类的日志内容似乎会往两个日志文件中写入。
对于.net core6的Microsoft.Extensions.Logging.ILogger使用,怎样才能实现不同的类产生的日志
输入到不同的文件中去? 有什么好的建议或代码片断可供参考吗?
期待得到各位的指点,感谢!

当使用 Microsoft.Extensions.Logging 日志框架时,可以使用 Microsoft.Extensions.Logging.File 提供程序来将不同类产生的日志输入到不同的文件中。下面是一个完整的示例代码,展示了如何配置和使用该提供程序:

首先,确保在项目中引用以下 NuGet 包:

  • Microsoft.Extensions.Logging
  • Microsoft.Extensions.Logging.File

接下来,创建一个名为 "LoggingConfig.cs" 的配置类,用于配置日志提供程序和日志过滤器:

using Microsoft.Extensions.Logging;

public static class LoggingConfig
{
    public static void ConfigureLoggerFactory(LoggerFactory loggerFactory)
    {
        // 添加文件日志提供程序
        loggerFactory.AddFile("MyClass.log");
        loggerFactory.AddFile("AnotherClass.log");

        // 添加过滤器,将不同类的日志写入不同文件
        loggerFactory.AddFilter("MyNamespace.MyClass", LogLevel.Information);
        loggerFactory.AddFilter("MyNamespace.AnotherClass", LogLevel.Information);
    }
}

然后,在程序的入口点(例如 Program.cs 文件中的 Main 方法)中进行日志配置:

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

public static class Program
{
    public static void Main()
    {
        // 创建服务集合
        var services = new ServiceCollection();

        // 添加日志
        services.AddLogging(builder =>
        {
            builder.ClearProviders(); // 清除默认的日志提供程序

            // 配置日志提供程序
            builder.Services.AddSingleton<ILoggerProvider, FileLoggerProvider>();

            // 配置 LoggerFactory
            builder.Services.AddSingleton<ILoggerFactory>(loggerFactory =>
            {
                var factory = new LoggerFactory();

                // 调用自定义的配置方法
                LoggingConfig.ConfigureLoggerFactory(factory);

                return factory;
            });
        });

        // 构建服务提供程序
        var serviceProvider = services.BuildServiceProvider();

        // 获取 ILoggerFactory 实例
        var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

        // 创建 MyClass 实例并使用日志记录
        var myClass = new MyClass(loggerFactory.CreateLogger<MyClass>());
        myClass.DoSomething();

        // 创建 AnotherClass 实例并使用日志记录
        var anotherClass = new AnotherClass(loggerFactory.CreateLogger<AnotherClass>());
        anotherClass.DoSomethingElse();
    }
}

在上述示例中,我们创建了一个静态类 LoggingConfig 用于配置日志提供程序和过滤器。在 ConfigureLoggerFactory 方法中,我们添加了 FileLoggerProvider 提供程序并配置了两个日志文件("MyClass.log" 和 "AnotherClass.log")。然后,我们通过添加过滤器,将 MyClass 类的日志写入 "MyClass.log" 文件中,将 AnotherClass 类的日志写入 "AnotherClass.log" 文件中。

在程序的入口点,我们使用 ServiceCollectionServiceProvider 配置和构建服务提供程序。然后,我们获取 ILoggerFactory 实例并创建 MyClassAnotherClass 的实例,为它们创建对应的日志记录器。

请注意,根据实际的命名空间和类名,你需要相应地修改代码中的命名空间和类名,以及文件名和路径。

首先你需要在log4net.config中定义多个appender,并给每个appender指定一个name和一个file。然后,在log4net.config中定义多个logger,并给每个logger指定一个name和一个appender-ref。name属性应该与你的类名或命名空间相对应,appender-ref属性应该与你想要输出到的文件的appender相对应就可以了
看下边我举的例子:
还可以参考这个: https://stackoverflow.com/questions/56674910/log-to-multiple-logfiles-with-microsoft-extensions-logging-abstractions
比如你有两个类DataSyncServices和DataSyncTwoServices,分别输出到日志文件1.txt和日志文件2.txt,这样配置log4net.config看是否可行:

<log4net>
    <appender name="DataSyncServices" type="log4net.Appender.RollingFileAppender">
        <file value="../../../App_Data/Logs/日志文件1.txt" />
        <!-- 省略其他配置 -->
    </appender>
    <appender name="DataSyncTwoServices" type="log4net.Appender.RollingFileAppender">
        <file value="../../../App_Data/Logs/日志文件2.txt" />
        <!-- 省略其他配置 -->
    </appender>
    <root>
        <level value="DEBUG" />
    </root>
    <logger name="DataSyncServices">
        <level value="DEBUG" />
        <appender-ref ref="DataSyncServices" />
    </logger>
    <logger name="DataSyncTwoServices">
        <level value="DEBUG" />
        <appender-ref ref="DataSyncTwoServices" />
    </logger>
</log4net>

采用chatgpt:
在Microsoft.Extensions.Logging中,每个ILogger实例是对应一个类的,而不是对应一个日志文件。因此,直接使用ILogger接口无法实现将不同的类产生的日志输入到不同的文件中。

要实现类似的功能,你可以通过扩展ILoggerProvider接口来自定义一个ILoggerProvider,并在其中创建不同的ILogger实例,每个实例关联一个特定的日志文件。

以下是一个示例代码片段,展示如何创建一个自定义的ILoggerProvider,实现按类名写入不同的日志文件:

using Microsoft.Extensions.Logging;
using System.IO;

public class ClassLoggerProvider<T> : ILoggerProvider
{
    private readonly string _logFilePath;
    
    public ClassLoggerProvider(string logFilePath)
    {
        _logFilePath = logFilePath;
    }
    
    public ILogger CreateLogger(string categoryName)
    {
        if (categoryName == typeof(T).FullName)
        {
            var logPath = Path.Combine(_logFilePath, $"{typeof(T).Name}.txt");
            
            // Create a logger that writes to the specified log file
            var loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddFile(logPath);
            });
            
            return loggerFactory.CreateLogger(categoryName);
        }
        
        // Return a null logger for other categories
        return NullLogger.Instance;
    }
    
    public void Dispose()
    {
        // Clean up resources if needed
    }
}

上述代码中,我们创建了一个ClassLoggerProvider类,其中T是具体的类。它实现了ILoggerProvider接口,用于创建ILogger实例。

在CreateLogger方法中,我们检查传入的categoryName是否与typeof(T).FullName相等,即是否为目标类的全名。如果是,我们创建一个基于文件的ILogger实例,将日志写入指定的日志文件。

对于其他类,我们返回一个NullLogger实例,该实例不执行任何日志操作,以避免不必要的日志记录。

要使用这个自定义的ILoggerProvider,你可以在程序启动时进行注册:

using Microsoft.Extensions.DependencyInjection;

// 注册自定义ILoggerProvider
services.AddSingleton(typeof(ILoggerProvider), new ClassLoggerProvider<DataSyncServices>(logFilePath1));
services.AddSingleton(typeof(ILoggerProvider), new ClassLoggerProvider<DataSyncTwoServices>(logFilePath2));

在上述代码中,logFilePath1和logFilePath2分别是目标类DataSyncServices和DataSyncTwoServices对应的日志文件路径。

通过这种方式,你可以实现将不同的类产生的日志输入到不同的文件中。每个类都有自己的ILogger实例,与相应的日志文件关联起来。

引用chatgpr回答: 在使用.NET Core 6Microsoft.Extensions.Logging.ILogger时,您可以实现将不同的类产生的日志输入到不同的文件中。以下是一种可能的解决方法:

  1. 配置不同的日志类别:首先,您需要在配置文件(例如appsettings.json)中为不同的日志类别配置不同的日志目标。例如:
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "NHibernate": "Warning",
      "DataSyncServices": "Information",
      "DataSyncTwoServices": "Information"
    },
    "Appenders": [
      {
        "Name": "DataSyncServices",
        "Type": "log4net.Appender.RollingFileAppender",
        "FileName": "Logs/DataSyncServices.log",
        "...
      },
      {
        "Name": "DataSyncTwoServices",
        "Type": "log4net.Appender.RollingFileAppender",
        "FileName": "Logs/DataSyncTwoServices.log",
        "...
      }
    ]
  }
}

在这个示例中,我们为DataSyncServicesDataSyncTwoServices这两个类设置了Information级别的日志,并为它们定义了自己的日志文件。

  1. 注册和配置日志提供程序:在应用程序的启动代码中(例如Startup.cs),添加以下代码来注册和配置日志提供程序:
public void ConfigureServices(IServiceCollection services)
{
    // 注册日志
    services.AddLogging(loggingBuilder =>
    {
        loggingBuilder.ClearProviders();   // 清除所有默认的日志提供程序(例如Console和Debug)
        loggingBuilder.SetMinimumLevel(LogLevel.Trace);   // 设置最低日志级别

        // 添加自定义的log4net提供程序
        loggingBuilder.AddLog4Net();
    });
}

AddLogging方法中,我们清除了默认的日志提供程序并配置了最低的日志级别。然后,我们添加了自定义的log4net日志提供程序。

  1. 修改DataSyncServices类和DataSyncTwoServices类:您的DataSyncServices类和DataSyncTwoServices类的构造函数已正确使用依赖注入实例化了ILogger类。确保在您的类中使用的ILogger泛型参数与类名称匹配(建议使用类的全名):
public class DataSyncServices
{
    private readonly ILogger<DataSyncServices> _logger;

    public DataSyncServices(ILogger<DataSyncServices> logger)
    {
        _logger = logger;
    }

    // ...
}

public class DataSyncTwoServices
{
    private readonly ILogger<DataSyncTwoServices> _logger;

    public DataSyncTwoServices(ILogger<DataSyncTwoServices> logger)
    {
        _logger = logger;
    }

    // ...
}
  1. 日志记录:在您的代码中,使用适当的日志级别来记录日志。例如,使用_logger.LogInformation来记录Information级别的日志。确保每个类使用了适当的日志级别。

看下这几篇文章:
https://learn.microsoft.com/zh-cn/dotnet/core/extensions/custom-logging-provider
https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.extensions.logging.ilogger?view=dotnet-plat-ext-7.0
https://blog.csdn.net/lindexi_gd/article/details/107204416

在使用Microsoft.Extensions.Logging框架时,可以通过配置不同的Logger Provider来实现不同类的日志输出到不同的文件中。下面是一种可能的解决方案:

  1. 创建自定义的Logger Provider:首先,创建一个自定义的Logger Provider,用于处理不同类的日志输出。可以实现ILoggerProvider接口并重写CreateLogger方法,根据不同的类名创建不同的Logger实例。
public class CustomLoggerProvider : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName)
    {
        // 根据类名创建不同的Logger实例
        if (categoryName == typeof(DataSyncServices).FullName)
        {
            return new Logger<DataSyncServices>();
        }
        else if (categoryName == typeof(DataSyncTwoServices).FullName)
        {
            return new Logger<DataSyncTwoServices>();
        }

        // 如果没有匹配的类名,返回默认的Logger实例
        return new Logger(categoryName);
    }

    public void Dispose()
    {
        // 清理资源
    }
}
  1. 注册自定义的Logger Provider:在应用程序的启动代码中,将自定义的Logger Provider注册到Logging框架中。
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureLogging((hostingContext, loggingBuilder) =>
        {
            loggingBuilder.ClearProviders(); // 清除默认的Logger Provider
            loggingBuilder.AddProvider(new CustomLoggerProvider()); // 添加自定义的Logger Provider
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
  1. 定义自定义的Logger类:为每个类创建一个自定义的Logger类,继承自ILogger接口,并重写相关的日志输出方法。
public class Logger<T> : ILogger<T>
{
    private readonly string _categoryName;

    public Logger()
    {
        _categoryName = typeof(T).FullName;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return NullScope.Instance; // 可以根据需要返回不同的Scope
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return true; // 可以根据需要进行日志级别的判断
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        // 在这里实现日志的输出到对应的文件,可以使用log4net等日志框架
        // 可以根据_logCategory判断是哪个类的日志,然后输出到对应的文件
    }
}

通过上述步骤,你可以实现不同类的日志输出到不同的文件中。在CustomLoggerProvider中根据类名创建对应的Logger实例,在Logger类中根据类名来决定将日志输出到哪个文件。你可以根据需要使用合适的日志框架(如log4net)来实现日志的输出到文件中。

希望这个解决方案对你有帮助!

将不同级别的logging 日志信息写入不同文件
编写python测试脚本

#!/usr/bin/env python
import logging
import logging.config
 
logging.config.fileConfig("logger.conf")
 
def logerror():
    logger = logging.getLogger("errorLogger")
    logger.error("There is a error in this file",exc_info=1)
 
def logdebug():
    logger = logging.getLogger("debugLogger")
    logger.debug("There is a debug in this file")
 
logdebug()
 
def testfun():
    print "test"
 
try:
    testfun(1)
except TypeError,e:
    logerror()

编写logging配置文档,将一般调试日志文件写入myapp.debug文件,将脚本运行过程中出错信息写入myapp.err文件。

[loggers]
keys=root,errorLogger,debugLogger
 
[logger_root]
level=DEBUG
handlers=errorHand,debugHand
 
[logger_errorLogger]
handlers=errorHand
qualname=errorLogger
propagate=0
 
[logger_debugLogger]
handlers=debugHand
qualname=debugLogger
propagate=0
 
###############################################
[handlers]
keys=errorHand,debugHand
 
[handler_errorHand]
class=handlers.RotatingFileHandler
level=ERROR
formatter=form01
args=('myapp.err', 'a', 10*1024*1024, 1)
 
[handler_debugHand]
class=handlers.RotatingFileHandler
level=DEBUG
formatter=form01
args=('myapp.debug', 'a', 10*1024*1024, 1)
 
###############################################
[formatters]
keys=form01
 
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%Y-%e-%d %H:%M:%S

执行python测试脚本,查看日志文件信息

[root@localhost tmp]# python mytest.py 
[root@localhost tmp]# cat myapp.debug 
2016-21-21 07:31:29 mytest.py[line:13] DEBUG There is a debug in this file
[root@localhost tmp]# cat myapp.err 
2016-21-21 07:31:29 mytest.py[line:9] ERROR There is a error in this file
Traceback (most recent call last):
  File "mytest.py", line 21, in <module>
    testfun(1)
TypeError: testfun() takes no arguments (1 given)

在每个需要记录日志的类中创建一个私有字段,该字段是 Microsoft.Extensions.Logging.ILogger 的实例。例如:


private ILogger<MyClass> logger;

在类的构造函数中获取 ILogger 实例,并将其分配给 logger 字段。例如:


public MyClass()  
{  
    logger = LogManager.GetLogger<MyClass>();  
}

在类的方法中使用 logger 字段记录日志。例如:

public void MyMethod()  
{  
    logger.LogInformation("This is an information message.");  
}

在配置文件中配置不同级别的日志方案

要实现不同类产生的日志输入到不同的文件,可以使用Microsoft.Extensions.Logging的日志配置功能,在appsettings.json文件中配置不同的日志输出目标和规则,定义每个文件的日志级别和文件路径,在Startup.cs文件的ConfigureServices方法中,添加日志服务并配置使用文件日志,在每个需要记录日志的类中,通过构造函数注入ILogger实例,在每个类的方法中,使用ILogger实例调用合适的日志记录方法,日志会根据配置输出到不同的文件中

在构造函数中注入ILogger