为何ILogger.LogError可显示于Application Insights而LogInformation不行?
问题:Azure Functions中ILogger.LogInformation日志无法在Application Insights中显示,仅LogError正常
在Azure Functions(.NET Isolated模式)中注入ILogger<MyClassName>后,调用Logger.LogInformation输出的日志能在Application Insights的**实时指标(Live Metrics)**中看到,但通过InvocationId或日志关键词搜索时找不到结果;而Logger.LogError的日志能正常在搜索结果中显示。
相关代码与配置
业务代码
public async ValueTask DeleteMessageAsync(string chatId, int messageId) { Logger.LogInformation( "Deleting message: {chatId} {messageId}", chatId, messageId); try { await TelegramBotClient.DeleteMessageAsync(chatId, messageId); } catch (ApiRequestException ex) when (ex.ErrorCode == (int)HttpStatusCode.BadRequest) { Logger.LogError(ex, "Could not Delete message"); } }
host.json日志配置
"logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" }, "enableLiveMetricsFilters": true }, "DurableTask.AzureStorage": "Information", "DurableTask.Core": "Information", "logLevel": { "Default": "Information", "Microsoft": "Information", "Microsoft.Hosting.Lifetime": "Information" } }
主机构建代码
IHost host = new HostBuilder() .AddServiceDefaults(useOtlpExporter: false, isDevelopment: true) .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); services.AddSingleton<ITelegramBroadcastService, TelegramBroadcastService>(); services.AddSingleton<IAuthorizationService, AuthorizationService>(); services.AddDurableTaskClient(x => { }); services.AddAzureClients(clientBuilder => { clientBuilder.AddBlobServiceClient(Environment.GetEnvironmentVariable("AzureWebJobsStorage")); }); }) .ConfigureAppConfiguration((context, config) => { if (context.HostingEnvironment.IsDevelopment()) config.AddUserSecrets<Program>(); }) .Build(); host.Run();
AddServiceDefaults扩展代码(Aspire默认生成)
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Logging; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Trace; namespace Microsoft.Extensions.Hosting; public static class Extensions { public static T AddServiceDefaults<T>(this T builder, bool useOtlpExporter, bool isDevelopment) where T : IHostBuilder { builder.ConfigureOpenTelemetry( useOtlpExporter: useOtlpExporter, isDevelopment: isDevelopment); builder.AddDefaultHealthChecks(); builder.AddServiceDiscovery(); return builder; } public static T AddServiceDiscovery<T>(this T builder) where T : IHostBuilder { builder.ConfigureServices(services => { services.AddServiceDiscovery(); }); return builder; } public static T ConfigureHttpClientDefaults<T>(this T builder) where T : IHostBuilder { builder.ConfigureServices(services => { services.ConfigureHttpClientDefaults(http => { // Turn on resilience by default http.AddStandardResilienceHandler(); // Turn on service discovery by default http.UseServiceDiscovery(); }); }); return builder; } public static T ConfigureOpenTelemetry<T>(this T builder, bool useOtlpExporter, bool isDevelopment) where T : IHostBuilder { builder.ConfigureLogging(logging => { logging.AddOpenTelemetry(telemetry => { telemetry.IncludeFormattedMessage = true; telemetry.IncludeScopes = true; }); }); builder.ConfigureServices(services => { services.AddOpenTelemetry() .WithMetrics(metrics => { metrics.AddRuntimeInstrumentation() .AddBuiltInMeters(); }) .WithTracing(tracing => { if (isDevelopment) { // We want to view all traces in development tracing.SetSampler(new AlwaysOnSampler()); } tracing.AddAspNetCoreInstrumentation() .AddGrpcClientInstrumentation() .AddHttpClientInstrumentation(); }); }); builder.AddOpenTelemetryExporters(useOtlpExporter); return builder; } private static T AddOpenTelemetryExporters<T>(this T builder, bool useOtlpExporter) where T : IHostBuilder { builder.ConfigureServices(services => { if (useOtlpExporter) { services.Configure<OpenTelemetryLoggerOptions>(logging => logging.AddOtlpExporter()); services.ConfigureOpenTelemetryMeterProvider(metrics => metrics.AddOtlpExporter()); services.ConfigureOpenTelemetryTracerProvider(tracing => tracing.AddOtlpExporter()); } // Uncomment the following lines to enable the Prometheus exporter (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) //services // .AddOpenTelemetry(); // .WithMetrics(metrics => metrics.AddPrometheusExporter()); // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.Exporter package) //services // .AddOpenTelemetry() // .UseAzureMonitor(); }); return builder; } public static T AddDefaultHealthChecks<T>(this T builder) where T : IHostBuilder { builder.ConfigureServices(services => { services.AddHealthChecks() // Add a default liveness check to ensure app is responsive .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); }); return builder; } public static WebApplication MapDefaultHealthCheckEndpoints(this WebApplication app) { // Uncomment the following line to enable the Prometheus endpoint (requires the OpenTelemetry.Exporter.Prometheus.AspNetCore package) // app.MapPrometheusScrapingEndpoint(); // All health checks must pass for app to be considered ready to accept traffic after starting app.MapHealthChecks("/health"); // Only health checks tagged with the "live" tag must pass for app to be considered alive app.MapHealthChecks("/alive", new HealthCheckOptions { Predicate = r => r.Tags.Contains("live") }); return app; } private static MeterProviderBuilder AddBuiltInMeters(this MeterProviderBuilder meterProviderBuilder) => meterProviderBuilder.AddMeter( "Microsoft.AspNetCore.Hosting", "Microsoft.AspNetCore.Server.Kestrel", "System.Net.Http"); }
Azure门户应用设置
{ "deployment_branch": "master", "SCM_TRACE_LEVEL": "Verbose", "SCM_COMMAND_IDLE_TIMEOUT": "60", "SCM_LOGSTREAM_TIMEOUT": "7200", "SCM_BUILD_ARGS": "", "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=...AccountKey=...=;EndpointSuffix=core.windows.net", "AzureWebJobsSecretStorageType": "Blob", "WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED": "1", "APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=...IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/", "WEBSITE_SLOT_NAME": "Production", "SCM_USE_LIBGIT2SHARP_REPOSITORY": "0", "WEBSITE_SITE_NAME": "...", "FUNCTIONS_EXTENSION_VERSION": "~4", "WEBSITE_AUTH_ENABLED": "False", "ScmType": "None", "WEBSITE_RUN_FROM_PACKAGE": "1", "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated" }
解决方案
1. 解决OpenTelemetry与Application Insights的日志路由冲突
代码同时启用了OpenTelemetry日志采集和Application Insights原生SDK,但OpenTelemetry未配置导出到Application Insights,导致持久化日志丢失。
- 安装NuGet包:
Azure.Monitor.OpenTelemetry.Exporter - 修改
AddOpenTelemetryExporters方法,取消Azure Monitor导出器的注释:
// 启用Azure Monitor导出器 services .AddOpenTelemetry() .UseAzureMonitor();
2. 临时禁用日志采样验证
当前采样设置可能过滤了Info级日志,先禁用采样确认问题:
修改host.json中的samplingSettings:
"applicationInsights": { "samplingSettings": { "isEnabled": false, "excludedTypes": "Request" }, "enableLiveMetricsFilters": true }
验证正常后,可根据需求调整采样规则(如设置maxTelemetryItemsPerSecond)。
3. 配置OpenTelemetry日志级别过滤
确保OpenTelemetry采集Info及以上级别的日志,在ConfigureOpenTelemetry的日志配置中添加过滤规则:
logging.AddOpenTelemetry(telemetry => { telemetry.IncludeFormattedMessage = true; telemetry.IncludeScopes = true; // 指定你的命名空间,确保Info级日志被采集 telemetry.AddFilter("YourNamespace.*", LogLevel.Information); // 设置服务名称,便于日志筛选 telemetry.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("YourFunctionAppName")); });
4. 调整服务注册顺序
在.NET Isolated模式下,将Application Insights服务注册移到AddServiceDefaults之前,避免日志提供者冲突:
IHost host = new HostBuilder() .AddApplicationInsightsTelemetryWorkerService() .AddServiceDefaults(useOtlpExporter: false, isDevelopment: true) .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { services.ConfigureFunctionsApplicationInsights(); // ...其他服务注册 }) // ...其他配置 .Build();
内容的提问来源于stack exchange,提问作者Peter Morris
相关产品推荐
相关产品推荐

