【问题标题】:NLog in Azure Function Log Streams using nlog.config使用 nlog.config 在 Azure 函数日志流中的 NLog
【发布时间】:2021-02-24 19:17:27
【问题描述】:

我的问题与这个问题非常相似:How can I get NLog output to appear in the streaming logs for an Azure Function?。在那个问题中,接受的答案显示了如何在函数调用开始时配置 nlog。我想通过 nlog.config 文件进行配置,或者至少在设置时配置一次,而不是在每次调用该函数时配置一次。

使用下面的代码,我在 Application Insight 日志中看到 NLog 消息,但在日志流中看不到。我希望从 NLog 记录的消息也显示在 Log Streams 中。

功能代码

    private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger();

    [FunctionName("TestLog")]
    public static async Task<IActionResult> TestLog([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
                                                HttpRequest req, ILogger log)
    {
        //shows in both Application Insights and Log Stream.
        log.LogInformation("Log From ILogger");

        //shows in Application Insights but not in Log Stream.
        _logger.Info("Log From NLog");

        return new OkObjectResult("OK");
    }

函数启动

    [assembly: FunctionsStartup(typeof(MyNamespace.TestFunctionApp.Startup))]
    
    namespace MyNamespace.TestFunctionApp
    {            
        public class Startup : FunctionsStartup
        {            
            public override void Configure(IFunctionsHostBuilder builder)
            {
                //nLog file ends 1 directory up from bins when deployed
                var binDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                var rootDirectory = Path.GetFullPath(Path.Combine(binDirectory, ".."));
                var nLogConfigPath = Path.Combine(rootDirectory, "NLog.config");
        
                //create MicrosoftILoggerTarget (I think this has to be done from code since it needs a ref to an ILogger instance).  But this does not seem to work.
                var loggerFactory = new Microsoft.Extensions.Logging.LoggerFactory();
                var azureILogger = loggerFactory.CreateLogger("NLog");
                var loggerTarget = new NLog.Extensions.Logging.MicrosoftILoggerTarget(azureILogger);
        
                //setup NLog
                LogManager.Setup()
                          .SetupExtensions(e => e.AutoLoadAssemblies(false))
                          .LoadConfigurationFromFile(nLogConfigPath, optional: false)
                          .LoadConfiguration(builder => builder.Configuration.AddRuleForAllLevels(loggerTarget));            
             }
        }
   }

NLog.config

<?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">
  <extensions>
    <add assembly="Microsoft.ApplicationInsights.NLogTarget" />
  </extensions>
  <targets>
    <target name="a" xsi:type="ApplicationInsightsTarget"/>
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="a" />
  </rules>
</nlog>

【问题讨论】:

    标签: azure azure-functions nlog


    【解决方案1】:

    通常目标是让 Microsoft ILogger 的输出达到 NLog 目标。这是通过调用UseNLog()AddNLog() 来完成的,这会将NLog 添加为LoggingProvider。

    替代方案有一个已经使用 NLog 的现有库,但希望将输出重定向到 Microsoft ILoggerFactory。但是由于循环/递归的噩梦,如果还想将 NLog 添加为 LoggingProvider,则必须小心。

    但我想您可以执行以下操作将所有输出从 NLog 重定向到 Microsoft ILoggerFactory:

        public class Startup : FunctionsStartup
        {            
            public override void Configure(IFunctionsHostBuilder builder)
            {
                var serviceProvider = builder.Services.BuildServiceProvider();
                var executionContextOptions = serviceProvider.GetService<IOptions<ExecutionContextOptions>>().Value;
                var appDirectory = executionContextOptions.AppDirectory;
    
                var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
                              
                // Setup NLog redirect all logevents to Microsoft ILoggerFactory
                var nlogLoggerName = "NLog";
                var nlogLogger = loggerFactory.CreateLogger(nlogLoggerName);
                var nlogTarget = new NLog.Extensions.Logging.MicrosoftILoggerTarget(nlogLogger);
                var nLogConfigPath = Path.Combine(appDirectory, "NLog.config");
        
                //setup NLog
                LogManager.Setup()
                          .SetupExtensions(e => e.AutoLoadAssemblies(false))
                          .LoadConfigurationFromFile(nLogConfigPath, optional: false)
                          .LoadConfiguration(builder => {
                              // Ignore output from logger named "NLog" to avoid recursion
                              builder.Configuration.Rules.Add(new LoggingRule() { LoggerNamePattern = nlogLoggerName, MaxLevel = LogLevel.Off, Final = true });
                              builder.Configuration.AddRuleForAllLevels(loggerTarget));
                          });
             }
        }
    

    【讨论】:

    【解决方案2】:

    我设法从 Rolf 的答案中得到了一些有用的东西。但它很老套,只有在函数应用中只有 1 个函数时才有意义。

    函数启动代码:

        public class Startup : FunctionsStartup
        {
    
            public override void Configure(IFunctionsHostBuilder builder)
            {
                builder.Services.AddSingleton<NLog.Logger>(SetupNLog);
            }
            
            private NLog.Logger SetupNLog(IServiceProvider serviceProvider)
            {
                //find NLog.config
                var executionContextOptions = serviceProvider.GetService<IOptions<ExecutionContextOptions>>().Value;
                var appDirectory = executionContextOptions.AppDirectory;
                var nLogConfigPath = Path.Combine(appDirectory, "NLog.config");
    
                //setup target to forward NLog logs to ILogger
                //NOTE: string passed to CreateLogger must match "function.<function name>" or logs will not show in Log Stream
                var msLoggerFactory = serviceProvider.GetService<ILoggerFactory>();
                var msLogger = msLoggerFactory.CreateLogger("Function.TestLog");
                var nLogToMsLogTarget = new MicrosoftILoggerTarget(msLogger);
                
                //setup NLog
                LogManager.Setup()
                          .SetupExtensions(e => e.AutoLoadAssemblies(false))
                          .LoadConfigurationFromFile(nLogConfigPath, optional: false)
                          .LoadConfiguration(c => c.Configuration.AddRuleForAllLevels(nLogToMsLogTarget));
    
                //return an NLog logger
                return LogManager.GetLogger("NLog");
            }
       }
    

    功能代码:

        public class Function1
        {
    
            private readonly NLog.Logger _logger;
    
            public Function1(NLog.Logger logger)
            {
                _logger = logger;
            }
    
            [FunctionName("TestLog")]
            public async Task<IActionResult> TestLog([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
                                                        HttpRequest req, Microsoft.Extensions.Logging.ILogger log)
            {
                log.LogInformation("Log From ILogger");
        
                _logger.Info("Log From NLog");
    
                var other = new SomeOtherClassWithNLog();
                other.SomeMethod();
        
                return new OkObjectResult("OK");
            }
        }
    
    
            public class SomeOtherClassWithNLog
            {
               private static readonly NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
    
               public void SomeMethod()
               {
                   _logger.Info("Logs From Another Class"); 
               }
    
            }
    

    我还发现 Azure 似乎将日志流中显示的日志过滤为仅类别名称中带有“Host.*”或“Function..*”的日志。在查看单个功能的监控页面时,它会进一步过滤,在这种情况下,它会过滤为“Function..*”。

    因此,您要么查看功能应用级别的日志流,要么在其中写入大量“不是我的应用”日志。或者只查看其中一个功能级别的日志流,即使那不是您调试的功能。

    我认为最后我将只使用默认的 ILogger,并且可以接受直接到 NLog 记录器的代码记录在 Log Stream 查看器中不可见的事实。

    【讨论】:

      【解决方案3】:

      是我第三个答案的时候了。与NLog.Extensions.Logging 版本。 1.7.1 然后MicrosoftILoggerTarget 可以将ILoggerFactory 作为输入参数,并且可以覆盖LoggerName。

      所以你可以这样设置:

          var msLoggerFactory = serviceProvider.GetService<ILoggerFactory>();
          var nLogToMsLogTarget = new MicrosoftILoggerTarget(msLoggerFactory);
          nLogToMsLogTarget.LoggerName = "${mdlc:FunctionName}";
      

      并像这样使用它:

          [FunctionName("TestLog")]
          public async Task<IActionResult> TestLog([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
                                                      HttpRequest req, Microsoft.Extensions.Logging.ILogger log)
          {
              using (NLog.MappedDiagnosticsLogicalContext.SetScoped("FunctionName", log.ToString())
              {
                   log.LogInformation("Log From ILogger");
      
                   LogManager.GetCurrentClassLogger().Info("Log From NLog");
      
                   var other = new SomeOtherClassWithNLog();
                   other.SomeMethod();
      
                   return new OkObjectResult("OK");
              }
          }
      

      不再受限于 MicrosoftILoggerTarget 的单个静态 Logger-Name

      【讨论】:

        【解决方案4】:

        您可以activate log stream from log-files,因此它将监视存储在 HOME 文件夹中的任何以 .txt 或 .log 结尾的文件。然后你可以使用文件目标:

        <?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">
          <extensions>
            <add assembly="Microsoft.ApplicationInsights.NLogTarget" />
          </extensions>
          <targets>
            <target name="a" xsi:type="ApplicationInsightsTarget"/>
            <target name="logfile" xsi:type="File" filename="${environment:HOME:cached=true}/logfiles/application/app-${shortdate}-${processid}.txt" />
          </targets>
          <rules>
            <logger name="*" minlevel="Info" writeTo="logfile, a" />
          </rules>
        </nlog>
        

        另见:https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-cloud-logging-with-Azure-function-or-AWS-lambda#writing-to-azure-diagnostics-log-stream

        【讨论】:

        • 没用。即使尝试直接写入(例如 File.AppendAllText("C:\home\LogFiles\Application\Functions\MyLog.log", "Direct Log Test")),日志流中也不会显示任何内容。不确定链接中的描述是否已过时,或者它可能不适用于功能应用。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-10
        • 1970-01-01
        • 2020-05-24
        • 2016-05-05
        相关资源
        最近更新 更多