Shri
Shri

Reputation: 367

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?

Upvotes: 0

Views: 389

Answers (1)

RithwikBojja
RithwikBojja

Reputation: 11363

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>
    

Upvotes: 1

Related Questions