Search code examples
azureazure-functionsazure-application-insightsserilog.net-core-logging

Logs logging twice in application insights when used Serilog in Azure Function app .NET 6


I have a Azure Function app in .NET 6, I am using Microsoft.Extension.Logging for logging and everything is working perfectly.

Now I have a new requirement to support structured logging so I decided to use Serilog, but after configuring Serilog logs are getting logged twice in Application Insights.

This is my startup.cs configuration for Serilog

public override void Configure(IFunctionsHostBuilder builder)
{
    var telemetryConfiguration = TelemetryConfiguration.CreateDefault();
    telemetryConfiguration.ConnectionString = Settings.ApplicationInsightsConnectionString;

    // Create and initialize the dependency tracking module
    var dependencyModule = new DependencyTrackingTelemetryModule();
    dependencyModule.Initialize(telemetryConfiguration);

    // Create the custom telemetry processor
    var telemetryProcessorChainBuilder = telemetryConfiguration.DefaultTelemetrySink.TelemetryProcessorChainBuilder;
    var configuration = builder.Services.BuildServiceProvider().GetService<IConfiguration>();
    telemetryProcessorChainBuilder.Use(next => new CustomTelemetryProcessor(next, configuration));

    // Build the telemetry processor chain
    telemetryProcessorChainBuilder.Build();

    Log.Logger = new LoggerConfiguration()
                             .WriteTo.ApplicationInsights(telemetryConfiguration, TelemetryConverter.Traces)
                           .CreateLogger();

    builder.Services.AddSingleton(telemetryConfiguration);
    builder.Services.AddSingleton(typeof(IAppLogger<>), typeof(AppLogger<>));
  
    // rest of the code...
 }

If I remove below code from startup.cs, then it's not logging twice and only Serilog logs are shown, but I can't remove that code as I also need custom telemetry processor.

So basically when I register custom telemetry processor it also activating Microsoft Logger and that's why I'm getting logs twice as per my understating.

  // Create and initialize the dependency tracking module
  var dependencyModule = new DependencyTrackingTelemetryModule();
  dependencyModule.Initialize(telemetryConfiguration);

  // Create the custom telemetry processor
  var telemetryProcessorChainBuilder = telemetryConfiguration.DefaultTelemetrySink.TelemetryProcessorChainBuilder;
  var configuration = builder.Services.BuildServiceProvider().GetService<IConfiguration>();
  telemetryProcessorChainBuilder.Use(next => new CustomTelemetryProcessor(next, configuration));

  // Build the telemetry processor chain
  telemetryProcessorChainBuilder.Build();

How I can register custom telemetry processor with Serilog, without activating Microsoft logger?


Solution

  • Serilog logs are getting logged twice in Application Insights.

    Below code worked for me and logged only required logs and there were no duplicates:

    Function1.cs:

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    
    namespace FunctionApp16
    {
        public class Function1
        {
            private readonly Rith_Logger<Function1> rith_lg;
    
            public Function1(Rith_Logger<Function1> logger)
            {
                rith_lg = logger;
            }
    
            [FunctionName("Function1")]
            public async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
            {
                rith_lg.LogInformation("Hello Rithwik Bojja !!!");
                var rith_obj = new { Weight = 60, Height = 170 };
                var rith_e = 24;
                rith_lg.LogInformation("Hello the structered log is {@rith_obj} in {rith_e} age", rith_obj, rith_e);
                return new OkObjectResult("Hello Rithwik Bojja !!!");
            }
        }
    }
    

    host.json:

    {
      "version": "2.0",
      "logging": {
        "applicationInsights": {
          "enableLiveMetricsFilters": true,
          "samplingSettings": {
            "isEnabled": false,
            "excludedTypes": "Request"
          }
        },
        "console": {
          "isEnabled": true,
          "logLevel": {
            "default": "Information"
          }
        }
      }
    }
    

    Here samplingSettings is set to false.

    Startup.cs:

    using Microsoft.Azure.Functions.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Configuration;
    using Serilog;
    using Microsoft.ApplicationInsights.Extensibility;
    using Microsoft.ApplicationInsights.DependencyCollector;
    using Microsoft.Extensions.Logging;
    using Microsoft.ApplicationInsights.Channel;
    
    [assembly: FunctionsStartup(typeof(FunctionApp16.Startup))]
    
    namespace FunctionApp16
    {
        internal class Startup : FunctionsStartup
        {
            public override void Configure(IFunctionsHostBuilder builder)
            {
                var rith_tc = TelemetryConfiguration.CreateDefault();
                rith_tc.ConnectionString = "InstrumentationKey=af34a9dc-f1df-4ff8-9ac9-6c1f0401ef4c;IngestionEndpoint=https://eastus-8.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/;ApplicationId=ff7a6";
                var rith_dm = new DependencyTrackingTelemetryModule();
                rith_dm.Initialize(rith_tc);
                var rit_tpb = rith_tc.DefaultTelemetrySink.TelemetryProcessorChainBuilder;
                var rih_conf = builder.Services.BuildServiceProvider().GetService<IConfiguration>();
                rit_tpb.Use(next => new CustomTelemetryProcessor(next, rih_conf));
                rit_tpb.Build();
                Log.Logger = new LoggerConfiguration()
                    .WriteTo.ApplicationInsights(rith_tc, TelemetryConverter.Traces)
                    .CreateLogger();
                builder.Services.AddSingleton(rith_tc);
                builder.Services.AddSingleton(typeof(Rith_Logger<>), typeof(AppLogger<>));
                builder.Services.AddLogging(loggingBuilder =>
                {
                    loggingBuilder.AddSerilog(Log.Logger, dispose: true);  
                });
            }
        }
        public class CustomTelemetryProcessor : ITelemetryProcessor
        {
            private ITelemetryProcessor Next { get; set; }
            private readonly IConfiguration rith_conf;
            public CustomTelemetryProcessor(ITelemetryProcessor next, IConfiguration configuration)
            {
                Next = next;
                rith_conf = configuration;
            }
            public void Process(ITelemetry rit)
            {
                Next.Process(rit);
            }
        }
        public interface Rith_Logger<T>
        {
            void LogInformation(string rith_ms, object rith_po, int rith);
            void LogInformation(string rith_ms);
        }
        public class AppLogger<T> : Rith_Logger<T>
        {
            private readonly ILogger<T> rith_lg;
    
            public AppLogger(ILogger<T> logger)
            {
                rith_lg = logger;
            }
            public void LogInformation(string rith_ms, object rith_po, int rith)
            {
                rith_lg.LogInformation(rith_ms, rith_po, rith);
            }
            public void LogInformation(string rith_ms)
            {
                rith_lg.LogInformation(rith_ms);
            }
        }
    }
    

    Output:

    enter image description here

    My pacakages in csproj:

        <ItemGroup>
            <PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
            <PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.22.0" />
            <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
            <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.4.0" />
            <PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
            <PackageReference Include="Serilog.Sinks.ApplicationInsights" Version="4.0.0" />
            <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.2" />
            <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
        </ItemGroup>