繁体   English   中英

从 Function App ILogger (C#) 在 Application Insights 中记录自定义对象

[英]Log custom object in Application Insights from Function App ILogger (C#)

我有一个 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 中,我看到日志包含默认信息,例如它来自哪个函数应用程序,以及包含上述字符串的message

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

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

笔记:

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

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

这是我以前使用过的模式:

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“自定义属性”部分中可见。

详细说明@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 one 对我更有吸引力,而且我猜数据可能会在管道中保留一段时间。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM