We've got an Azure service bus resource that is being logged to by a .net core 5.0 worker service. We have an Azure application insights resource that is configured in the appsettings.json using the following key:
"ApplicationInsights": { "InstrumentationKey": "guid" },
During development, this works well and we can see the service bus dependency and message logs in the application insights.
When this app is deployed by Azure Devops, the application insights key value is saved to an environment variable (app is deployed to AKS). This value is then supposed to be read on startup by the application. However, what we see is that the service when deployed to AKS logs to the development application insights, not the production app insights. We think this is because the service bus is somehow creating it's own logger and reading from the locally deployed appsettings.json file rather than reading from the environment values.
I've had a look at this page, but can't get it to work: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-end-to-end-tracing?tabs=net-standard-sdk-2
The question is, how can we configure the service bus client to use the correct logger and log to the right application insights resource?
Our sb startup code looks something like this:
protected void Start(Func<Message, CancellationToken, Task> handler, string name, string connectionString)
{
QueueClient = new QueueClient(connectionString, name, ReceiveMode.ReceiveAndDelete);
var messageHandlerOptions = new MessageHandlerOptions(e => {
ProcessError(e.Exception);
return Task.CompletedTask;
})
{
MaxConcurrentCalls = Config.ServiceBus.ConcurrentThreads,
AutoComplete = false
};
QueueClient.RegisterMessageHandler(handler, messageHandlerOptions);
}
Update
In our application startup, we are doing something like the following:
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.Enrich.FromLogContext()
.WriteTo.Debug()
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}")
.CreateLogger();
try
{
var host = CreateHostBuilder(args).Build();
host.Services.UseScheduler(scheduler => {
scheduler
.Schedule<FxRatesInvocableService>()
.DailyAtHour(01);
});
host.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
var host = Host.CreateDefaultBuilder(args);
host.ConfigureLogging(
loggingBuilder =>
{
loggingBuilder.ClearProviders();
Serilog.Debugging.SelfLog.Enable(Console.Error);
var result = ConfigureLogger();
loggingBuilder.AddApplicationInsights(result.Config.ApplicationInsightsKey);
var logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Error)
.MinimumLevel.Override("System.Net.Http.HttpClient", LogEventLevel.Warning)
.WriteTo.ColoredConsole(
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} {Level} {Properties} {Message}{NewLine}{Exception}",
restrictedToMinimumLevel: LogEventLevel.Information
) // one of the logger pipeline elements for writing out the log message
.WriteTo.File(Path.Combine(Directory.GetCurrentDirectory(), "logs", "logs.txt")
, outputTemplate: "{Timestamp:HH:mm:ss} [{EventType:x8} {Level:u3}] {Message:lj}{NewLine}{Exception}"
, rollingInterval: RollingInterval.Day
, flushToDiskInterval: TimeSpan.FromMinutes(1)
, fileSizeLimitBytes: 1_000_000
, rollOnFileSizeLimit: true
, shared: true)
.Enrich.FromLogContext()
.Enrich.WithDemystifiedStackTraces() .ReadFrom.Configuration(Configuration.GetSection("Serilog"))
.CreateLogger();
loggingBuilder.AddSerilog(logger, dispose: true);
}
);
host.ConfigureServices((hostContext, services) =>
{
...
where the result object above is a configuration object that is created by looking at where the code is running and settings properties appropriately.
It looks to me that there is some code in the Microsoft.Azure.ServiceBus nuget package that is defaulting the ILogger to be one that assumes the read configuration from the appsettings.json file. Just a hunch. But is that the case? Any way we can override this behaviour?
An instrumentation key specified in code wins over the environment variable APPINSIGHTS_INSTRUMENTATIONKEY, which wins over other options.?