【问题标题】:Log custom object in Application Insights from Function App ILogger (C#)从 Function App ILogger (C#) 在 Application Insights 中记录自定义对象
【发布时间】:2021-09-19 03:22:44
【问题描述】:

我有一个 C# .NET Core Azure Function App,我正在使用 ILogger 将日志发送到 Application Insights。到目前为止,这运行良好。

函数头:

public static void Run([TimerTrigger("0 30 * * * *")] TimerInfo myTimer, ILogger log, ExecutionContext context)

ILogger 用法:

log.LogInformation($"MyFunction trigger function executed at: {DateTime.Now}");

在 App Insights 中,我看到了包含默认信息的日志,例如它来自哪个 Function App,以及包含上述字符串的 message

但是,现在我想记录自定义日志。我有一个IEnumerable<IDictionary<string, string>>,我希望列表中的每个字典元素都是一个单独的日志。理想情况下,我可以有一个日志,其中每个字段都是字典中的一个键,其值是相应的值。或者,我可以在日志中使用某种 customDimensions 字段,它是一个包含列表中 1 个字典中所有键值对的对象。

目的是使日志在 Kusto 中易于查询。我想避免在 App Insights 中查询它们时解析它们。

注意事项:

  • 由于我已经使用 ILogger 进行现有日志记录,有没有办法使用 ILogger 接口进行上述对象日志记录?
  • 如果没有,如何使用不同的记录器记录上述对象?

我查看了许多其他类似的帖子,但似乎没有一个得到完全回答。

【问题讨论】:

  • 创建一个记录器范围 (log.BeginScope()) 并将它传递给您的字典。或者,将您的字典添加到 Activity.Current 的行李/标签中。应用洞察将为任一场景添加自定义维度。对于 Activity,如果一个不存在,则启动一个新的。其他一切都是自动的
  • 对我来说 Activity.Current.AddBaggage() 有效,但 AddTag() 无效。

标签: c# azure-functions azure-application-insights appinsights ilogger


【解决方案1】:

这是我之前使用的一个模式:

public class LogService : ILogService
{
    private readonly ILogger<LogService> _log;
    
    private readonly Dictionary<string, object> _customProperties = new Dictionary<string, object>();

    public LogService(ILogger<LogService> log)
    {
        _log = log;
    }
    
    public void SetCustomProperty(string key, object value)
    {
        _customProperties.Add(key, value);
    }

    public void LogInformation(string message, params object[] args)
    {
        Log(LogLevel.Information, message, args);
    }

    public void LogWarning(string message, params object[] args)
    {
        Log(LogLevel.Warning, message, args);
    }
    
    ...etc 

    private void Log(LogLevel logLevel, string message, params object[] args)
    {
        using (_log.BeginScope(_customProperties))
        {
            _log.Log(logLevel, message, args);
        }
    }
}

重要的是最后一个方法Log(LogLevel logLevel, string message, params object[] args)。它将_log.Log() 包装在using 中,并使用_log.BeginScope() 将自定义属性添加到日志消息中,该日志消息应该在Application Insights“自定义属性”部分中可见。

【讨论】:

    【解决方案2】:

    详细说明@pinkfloydx33 的评论:您可以通过

    _logger.BeginScope( < your state here > )
    {
        // All log methods here include state, regardless 
        // of which ILogger object is used. 
    }
    

    或通过使用

    System.Diagnostics.Activity.Current.AddBaggage()
    

    这无需额外配置即可工作(例如,范围已在 AI 上默认启用)。

    例如,这是一个记录租户信息的中间件类,它显示了这两种方法:

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace MyApp
    {
    
        public static class StoreTenantForLoggingMiddlewareExtensions
        {
            /// <summary>
            /// Register StoreTenantForLoggingMiddleware as middleware.
            /// Call this from Configure() in Startup, as: 
            /// app.UseStoreTenantForLogging()
            /// </summary>
            /// <param name="builder"></param>
            /// <returns></returns>
            public static IApplicationBuilder UseStoreTenantForLogging(
                this IApplicationBuilder builder)
            {
                return builder.UseMiddleware<StoreTenantForLoggingMiddleware>();
            }
        }
    
        /// <summary>
        /// Middleware to log the Tenant's domain to Application 
        /// Insights as a customDimension
        /// </summary>
        public class StoreTenantForLoggingMiddleware
        {
            private readonly RequestDelegate _next;
            private readonly ILogger<StoreTenantForLoggingMiddleware> _logger;
    
            public StoreTenantForLoggingMiddleware(RequestDelegate next,
                        ILogger<StoreTenantForLoggingMiddleware> logger)
            {
                _next = next;
                _logger = logger;
            }
    
            // Here TenantContext is my own class that gets the state
            // I want to be logged. You'd replace with your own object 
            // or just call a method on httpContext.
            public async Task InvokeAsync(HttpContext httpContext, TenantContext tenantContext)
            {
                // Example 1: Add data to current activity. AI will pick this
                // up and add as a customDimension in traces logs.
                var currentActivity = System.Diagnostics.Activity.Current;
                if (currentActivity != null)
                {
                    currentActivity.AddBaggage("TenantDomain1", tenantContext?.Domain);
                }
    
                // Example 2: Use a scope. 
                // If you go with option 1, remove this 'using' but still  
                // call await _next(httpContext);
                using ( var scope = _logger.BeginScope(new Dictionary<string, object>() 
                                     { { "TenantDomain2", tenantContext?.Domain } }))
                {    
                    await _next(httpContext);
                }
            }
        }
    }
    

    我不确定哪个最好。 Activity 对我来说更有吸引力,而且我猜数据可能会在管道中保留一段时间。

    如果您使用 nlog 并希望能够在那里记录该属性,您可以在上面的 Invoke() 开头添加这一行,然后在您的 nlog.config 文件中使用 ${mdlc:item=TenantDomain}

    NLog.MappedDiagnosticsLogicalContext.Set("TenantDomain", tenantContext?.Domain);
    

    您也许可以使用https://github.com/NLog/NLog.DiagnosticSource 作为替代方案,但我没有尝试过。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-27
      • 2021-09-21
      • 2020-06-20
      • 2019-07-14
      • 1970-01-01
      • 2020-01-08
      • 2022-12-19
      • 1970-01-01
      相关资源
      最近更新 更多