动态设置每个记录器示例的Nlog日志级别ASP.NET Core 2.x

iq3niunx  于 2023-01-14  发布在  .NET
关注(0)|答案(4)|浏览(172)
    • 目标**:动态选择要详细记录的HTTP请求(不同的日志级别)。
    • 概述**:我有一个ASP.Net核心2.1网络服务器运行,一旦在生产,如果我需要调试一个问题,我希望能够改变日志级别。然而,更改日志级别是持久的...又名,不会在每次调用我的控制器后重置。
[HttpGet]
    public async Task<IEnumerable<string>> Get()
    {
        this.Logger.LogTrace("This should NOT get logged");
        SetMinLogLevel(LogLevel.Trace);
        this.Logger.LogTrace("This should be logged");

        return new string[] { "value1", "value2" };
    }

   public static void SetMinLogLevel(LogLevel NewLogLevel)
    {
        foreach (var rule in LogManager.Configuration.LoggingRules)
        {
            rule.EnableLoggingForLevel(NewLogLevel);
        }

        //Call to update existing Loggers created with GetLogger() or 
        //GetCurrentClassLogger()
        LogManager.ReconfigExistingLoggers();
    }

我希望请求者能够在他们的HTTP请求中设置一个标志(header或cookie),以启用每个请求的更详细的日志记录级别,这样我就不会用来自请求者的详细日志淹没我的日志。

    • 问题**:如何动态设置每个日志记录器示例的日志级别?(我相信这是正确的措辞)

我目前使用的是NLog软件包4.5。

xsuvu9jc

xsuvu9jc1#

我们知道NLog 4.6.7增加了对使用NLog布局的支持,比如${gdc:globalLevel}dynamically change level attributes at runtime。如果可能的话,更好的解决方案是升级你的NLog。

更新:新的解决方案我在版本4.5上试用了这段代码,它工作正常。看起来你不需要升级你的NLog版本。 在这种情况下,所有的配置都是通过编程设置的。你可以在头中发送你想要的级别loglevel。如果你在头中发送loglevel,它将被使用。否则,logLevel将是Error。请看这里。

注意:只需使用using NLog;,不需要using Microsoft.Extensions.Logging;

[Route("api/[controller]/[action]")]
    [ApiController]
    public class HomeController : ControllerBase
    {
        private readonly Logger _log = LogManager.GetCurrentClassLogger();

        [HttpGet]
        public async Task<IEnumerable<string>> Get()
        {
            var requestLogLevel = Request.Headers.SingleOrDefault(x => x.Key == "loglevel");
            LogLevel logLevel = LogLevel.Error;
            switch (requestLogLevel.Value.ToString().ToLower())
            {
                case "trace":
                    logLevel = LogLevel.Trace;
                    break;
                case "debug":
                    logLevel = LogLevel.Debug;
                    break;
                case "info":
                    logLevel = LogLevel.Info;
                    break;
                case "warn":
                case "warning":
                    logLevel = LogLevel.Warn;
                    break;
                case "error":
                    logLevel = LogLevel.Error;
                    break;
                case "fatal":
                    logLevel = LogLevel.Fatal;
                    break;
            }

            var config = new NLog.Config.LoggingConfiguration();
            var defaultMode = new NLog.Targets.FileTarget("defaultlog") { FileName = "log.txt" };
            config.AddRule(logLevel, LogLevel.Fatal, defaultMode);
            NLog.LogManager.Configuration = config;

            _log.Trace("Some logs");

            return new string[] { "value1", "value2" };
        }
    }

解决方案1)将NLog升级到4.6.7或更高版本:

var config = new NLog.Config.LoggingConfiguration();

// Targets where to log to: File and Console
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "file.txt" };
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
            
// Rules for mapping loggers to targets            
config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile);
            
// Apply config           
NLog.LogManager.Configuration = config;

**解决方案2)以编程方式更改配置文件:**因为您的NLog版本不支持自动更改配置,我们将以编程方式更改它:

[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
    private readonly Logger _log = LogManager.GetCurrentClassLogger();

    // Special Authorization needed
    public bool ChangeToDebugMode()
    {
        try
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(AppDomain.CurrentDomain.BaseDirectory +  "nlog.config");
            XmlNode root = doc.DocumentElement;
            XmlNode myNode = root["include"].Attributes["file"];
            myNode.Value = "debugmode.config";
            doc.Save(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
        }
        catch (Exception)
        {
            return false;
        }

        return true;
    }

    // Special Authorization needed
    public bool RestToDefault()
    {
        try
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
            XmlNode root = doc.DocumentElement;
            XmlNode myNode = root["include"].Attributes["file"];
            myNode.Value = "defaultmode.config";
            doc.Save(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
        }
        catch (Exception)
        {
            return false;
        }

        return true;
    }

    [HttpGet]
    public async Task<IEnumerable<string>> Get()
    {
        _log.Trace("Some logs");

        return new string[] { "value1", "value2" };
    }
}

在这种情况下,您需要在配置文件中进行一些更改。您需要将autoReload=true添加到配置中。现在,当任何配置更改时,NLog都会自动重新加载配置,您不需要重新启动应用程序。您需要查看autoReloadincludehere

nlog.配置

<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true">
  <include file="defaultmode.config" />
</nlog>

默认模式配置

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <targets>
    <target name="logfile" xsi:type="File" fileName="file.txt" />
  </targets>

  <rules>
    <logger name="*" minlevel="Debug" writeTo="logfile" />
  </rules>
  <!-- ... -->
</nlog>

调试模式配置

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <targets>
    <target name="logfile" xsi:type="File" fileName="file.txt" />
  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="logfile" />
  </rules>
  <!-- ... -->
</nlog>

我做了另外两个配置文件。debugmode.configdefaultmode.config。默认情况下,在nlog.config文件中,包含deafultmode.config。当ChangeToDebugMode被调用时,它会变成debugmode.config,当RestToDefault被调用时,它会变成defaultmode.config。我使用include,并将配置分成两个文件,只是为了简单起见。

**解决方案3)根据您的问题:**在本例中,我使用了您在问题中提供的代码。如果您在请求标头中发送日志级别,则会考虑它。如果您不发送,则会使用您在配置中设置的默认值。因此,您不需要在客户端更改应用程序。它工作正常。只需在调试时发送所需的日志级别。

[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
    private readonly Logger _log = LogManager.GetCurrentClassLogger();

    [HttpGet]
    public async Task<IEnumerable<string>> Get()
    {
        var requestLogLevel = Request.Headers.SingleOrDefault(x => x.Key == "loglevel");
        LogLevel logLevel = LogLevel.Error;
        switch (requestLogLevel.Value.ToString().ToLower())
        {
            case "trace":
                logLevel = LogLevel.Trace;
                break;
            case "debug":
                logLevel = LogLevel.Debug;
                break;
            case "info":
                logLevel = LogLevel.Info;
                break;
            case "warn":
            case "warning":
                logLevel = LogLevel.Warn;
                break;
            case "error":
                logLevel = LogLevel.Error;
                break;
            case "fatal":
                logLevel = LogLevel.Fatal;
                break;
        }
        SetMinLogLevel(logLevel);               

        _log.Trace("Some logs.");

        return new string[] { "value1", "value2" };
    }

    public static void SetMinLogLevel(LogLevel NewLogLevel)
    {
        foreach (var rule in LogManager.Configuration.LoggingRules)
        {
            rule.EnableLoggingForLevel(NewLogLevel);
        }

        //Call to update existing Loggers created with GetLogger() or 
        //GetCurrentClassLogger()
        LogManager.ReconfigExistingLoggers();
    }
}

问题是,这种情况每次都需要发送日志级别,在这些截图中,您看到了如何在调试模式下发送日志级别。

ltskdhd1

ltskdhd12#

也许您可以使用会话cookie来控制是否启用调试模式:

<targets>
    <target type="file" name="logfile" filename="applog.txt" />
</targets>
<rules>
    <logger name="*" minlevel="Off" writeTo="logfile" ruleName="debugCookieRule">
      <filters defaultAction="Log">
         <when condition="'${aspnet-session:EnableDebugMode}' == ''" action="Ignore" />
      </filters>
    </logger>
</rules>

然后像这样激活会话cookie:

public void SetMinLogLevel(LogLevel NewLogLevel)
{
    var cookieRule = LogManager.Configuration.FindRuleByName("debugCookieRule");
    if (cookieRule != null)
    {
        cookieRule.MinLevel = NewLogLevel;

        // Schedule disabling of logging-rule again in 60 secs.
        Task.Run(async () => { await Task.Delay(60000).ConfigureAwait(false); cookieRule.MinLevel = LogLevel.Off; LogManager.ReconfigExistingLoggers(); });

        // Activate EnableDebugMode for this session
        HttpContext.Session.SetString("EnableDebugMode", "Doctor has arrived");
    }

    LogManager.ReconfigExistingLoggers();  // Refresh loggers
}

如果不需要session-cookies和${aspnet-session},则NLog.Web.AspNetCore具有用于提取HttpContext-details的其他选项。另请参见:https://nlog-project.org/config/?tab=layout-renderers&search=package:nlog.web.aspnetcore

lx0bsm1f

lx0bsm1f3#

我认为您不应该尝试定制NLog日志级别(这将影响整个过程),而应该选择修改日志语句本身的日志级别的解决方案。
要实现此功能,您需要以下各项:
1.一种机制,用于标识要对其进行调试日志记录的请求
1.记录器的 Package ,以便可以动态覆盖日志级别
第一个要求很简单-设置一个cookie或一个自定义HTTP头,并检查其中一个是否存在。您需要将此检查的结果提供给LogWrapper示例,以便它知道何时应该执行特殊操作。
LogWrapper必须在每个请求中示例化,这样示例就不会在请求之间共享。最简单的方法是在控制器的构造函数中按需创建它(但也可以将它连接到DI容器以进行自动注入)。
这看起来像这样:

public class HomeController : Controller
{
    private readonly LogWrapper _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        var isDebugRequest = ...;
        _logger = new LogWrapper<HomeController>(logger, isDebugRequest);
    }    

    ...
}

here介绍了为NLog创建日志 Package 器的基础知识,但看起来您已经在使用为Microsoft.extensions.logging创建的 Package 器,因此您需要 Package 该接口:

public class LogWrapper<T> : Microsoft.Extensions.Logging.ILogger
{
    private readonly ILogger<T> _logger;
    private readonly bool _debug;

    public LogWrapper(ILogger<T> logger, bool isDebug)
    {
        _logger = logger;
        _debug = isDebug;
    }    

    public void Log<TState>(LogLevel logLevel,
                            EventId eventId,
                            TState state,
                            Exception exception,
                            Func<TState, Exception, string> formatter)
    {
        if (_debug) 
        {
            // override log level here
            _logger.Log(LogLevel.Warning, eventId, state, exception, formatter); 
        }
        else 
        {
            _logger.Log(logLevel, eventId, state, exception, formatter);
        }
    }

    // ILogger has two other methods you'll need to implement
}

这种方法的缺点是日志语句没有原始的日志级别,这对于您的用例可能重要,也可能不重要。

a0zr77ik

a0zr77ik4#

NLog 4.6.7允许您在minLevel/maxLevel的日志规则过滤器中使用布局
您可以拥有一个具有默认日志级别的NLog-Config-Variable,然后在您的Web应用上创建一个隐藏方法,用于修改NLog-Config-Variable并调用ReconfigExistingLoggers()
然后设置一个计时器,在30秒后将NLog-Config-Variable恢复为原始值,并调用ReconfigExistingLoggers()
另见:www.example.comhttps://github.com/NLog/NLog/wiki/Filtering-log-messages#semi-dynamic-routing-rules
另请参阅:https://github.com/NLog/NLog/wiki/Environment-specific-NLog-Logging-Configuration

相关问题