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

ASP.NET Core Web API:如何用Log4Net为每个用户单独记录日志

问题描述

我需要为每个登录用户单独记录日志,比如用户A的日志文件路径为logs/USER A/06.02.2024.log。目前使用Log4Net写入日志到文件,但尝试了以下代码后,日志文件并未创建,请求技术支持。

自定义Log4NetLogger实现

public class Log4NetLogger : ILogger
 {
     private readonly ILog _logger;

 public Log4NetLogger(ILog logger)
 {
     _logger = logger;
 }

 public IDisposable BeginScope<TState>(TState state)
 {
     return null; // No scope support in log4net
 }

 public bool IsEnabled(LogLevel logLevel)
 {
     switch (logLevel)
     {
         case LogLevel.Trace:
         case LogLevel.Debug:
             return _logger.IsDebugEnabled;
         case LogLevel.Information:
             return _logger.IsInfoEnabled;
         case LogLevel.Warning:
             return _logger.IsWarnEnabled;
         case LogLevel.Error:
             return _logger.IsErrorEnabled;
         case LogLevel.Critical:
             return _logger.IsFatalEnabled;
         default:
             throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null);
     }
 }

 public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
     Func<TState, Exception, string> formatter)
 {
     if (!IsEnabled(logLevel))
     {
         return;
     }

     var message = formatter(state, exception);
     if (string.IsNullOrWhiteSpace(message))
     {
         return;
     }

     switch (logLevel)
     {
         case LogLevel.Trace:
         case LogLevel.Debug:
             _logger.Debug(message);
             break;
         case LogLevel.Information:
             _logger.Info(message);
             break;
         case LogLevel.Warning:
             _logger.Warn(message);
             break;
         case LogLevel.Error:
             _logger.Error(message, exception);
             break;
         case LogLevel.Critical:
             _logger.Fatal(message, exception);
             break;
         default:
             _logger.Warn($"Encountered unknown log level {logLevel}, writing out as Info.");
             _logger.Info(message, exception);
             break;
     }
 }
 }
}

自定义LoggerFactory实现

public class CustomLoggerFactory : ILoggerFactory
    {
        private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomLoggerFactory(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void AddProvider(ILoggerProvider provider)
    {
        // Do nothing or implement as needed
    }

    public ILogger CreateLogger(string categoryName)
    {
        var logger = LogManager.GetLogger(categoryName);

        var user = _httpContextAccessor.HttpContext?.User;
        if (user != null && user.Identity.IsAuthenticated)
        {
            var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            if (!string.IsNullOrEmpty(userId))
            {
                var logFilePath = $"logs/{userId}/log.txt";
                var logFolder = Path.GetDirectoryName(logFilePath);
                if (!Directory.Exists(logFolder))
                {
                    Directory.CreateDirectory(logFolder);
                }
                // Configure log4net to use the dynamic file path
                GlobalContext.Properties["LogFileName"] = logFilePath;
            }
        }

        return new Log4NetLogger(logger);
    }

    public void Dispose()
    {
        // Dispose resources if needed
    }
}

Startup配置代码

public static void ConfigureLogging(ILoggingBuilder loggingBuilder)
{
    loggingBuilder.ClearProviders();
    loggingBuilder.AddLog4Net();
    loggingBuilder.Services.AddSingleton<ILoggerFactory, CustomLoggerFactory>();
}

解决方案

问题根源

  1. Log4Net配置未关联动态变量:代码中设置了GlobalContext.Properties["LogFileName"],但log4net配置文件未引用该属性,导致日志仍按默认路径输出。
  2. LoggerFactory注册顺序错误:先调用AddLog4Net()会注册默认日志提供者,后续自定义工厂无法覆盖生效。
  3. 路径格式不符合需求:当前生成的是固定文件名log.txt,未实现按日期命名的要求。
  4. HttpContextAccessor未注册:若未在服务中注入该对象,无法获取当前登录用户信息。

修复步骤

1. 修改Log4Net配置文件

在log4net.config中配置滚动文件追加器,引用动态路径并按日期生成日志文件:

<appender name="UserRollingFileAppender" type="log4net.Appender.RollingFileAppender">
  <!-- 使用GlobalContext中的动态路径 -->
  <file type="log4net.Util.PatternString" value="%property{LogFileName}" />
  <!-- 按日期滚动,生成格式如06.02.2024.log的文件 -->
  <rollingStyle value="Date" />
  <datePattern value="'.'dd.MM.yyyy'.log'" />
  <staticLogFileName value="false" />
  <appendToFile value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  </layout>
</appender>

<root>
  <level value="ALL" />
  <appender-ref ref="UserRollingFileAppender" />
</root>

2. 调整LoggerFactory注册顺序

先注册自定义工厂,再添加Log4Net支持,确保日志系统使用自定义逻辑:

public static void ConfigureLogging(ILoggingBuilder loggingBuilder)
{
    loggingBuilder.ClearProviders();
    // 先注册自定义LoggerFactory
    loggingBuilder.Services.AddSingleton<ILoggerFactory, CustomLoggerFactory>();
    // 再添加Log4Net支持
    loggingBuilder.AddLog4Net();
}

3. 修正日志路径生成逻辑

只生成用户目录,日期部分由log4net自动处理:

public ILogger CreateLogger(string categoryName)
{
    var logger = LogManager.GetLogger(categoryName);

    var user = _httpContextAccessor.HttpContext?.User;
    if (user != null && user.Identity.IsAuthenticated)
    {
        var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userId))
        {
            var userLogDir = $"logs/{userId}/";
            if (!Directory.Exists(userLogDir))
            {
                Directory.CreateDirectory(userLogDir);
            }
            // 设置基础文件名,log4net会自动追加日期后缀
            GlobalContext.Properties["LogFileName"] = Path.Combine(userLogDir, "log");
        }
    }

    return new Log4NetLogger(logger);
}

4. 注册HttpContextAccessor

在ConfigureServices中添加依赖注入:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // 其他服务注册...
}

5. 检查目录权限

确保应用程序对logs目录拥有读写权限,避免因权限不足导致文件创建失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:14:52