Trouble getting Serilog to read appsettings.json settings - c#

I have the following code in my console application.
// Build configuration
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ApplicationInfo.DataPath)
.AddJsonFile("appsettings.json", false, false)
.Build();
// Configure Serilog
string logFormat = "[{Timestamp:yyyy-MM-dd hh:mm:ss tt}][{Level:u3}] {Message:lj}{NewLine}{Exception}";
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console(LogEventLevel.Verbose, logFormat)
.WriteTo.File(ApplicationInfo.GetDataFileName("log"), LogEventLevel.Verbose, logFormat)
.CreateLogger();
AppHost = Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureServices((context, services) =>
{
services.AddDbContext<TTApplicationDbContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
});
services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));
services.Configure<SftpSettings>(configuration.GetSection("FtpSettings"));
})
.Build();
This seems to be working. However, when I add the following section to my appsettings.json file, I do not get any of the verbose logging from within the framework. All I get is the logging that I specifically log in my application.
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"System": "Information"
}
}
}
As best I can tell, Serilog is ignoring these settings. How can I change that?
Update:
If I change the Default value to Debug in appsettings.json, that does in fact control whether or not my own calls to LogDebug() show up. But I don't understand why none of Microsoft's logging shows up. Wouldn't Microsoft's code use whatever logger is configured?

While configuring a DotNet Core App, I had the same problem. I found that if I added the Nuget Package serilog-extensions-hosting, it will also start outputting the framework messages to the logs.
https://github.com/serilog/serilog-extensions-hosting
I hope this helps

Related

What's the proper way to configure Serilog in a .NET Core 6.0 Razor page application?

I'm trying to configure Serilog to work with a .NET 6 Razor page web application. I've tried following the aspnetcore directions here and applicationinsights here. Nothing I try seems to work. I see my enricher being hit but nothing is written to application insights.
The program.cs files looks like this:
using AppInsightsSerilogCustomDimensions;
using Microsoft.ApplicationInsights.Extensibility;
using Serilog;
Log.Logger = new LoggerConfiguration()
.Enrich.With<SystemInfoEnricher>()
.WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces)
.CreateBootstrapLogger();
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Host.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.With<SystemInfoEnricher>()
.WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces));
// Error: No service for type 'Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration' has been registered.
//builder.Host.UseSerilog((context, services, configuration) => configuration
// .ReadFrom.Configuration(context.Configuration)
// .ReadFrom.Services(services)
// .Enrich.With<SystemInfoEnricher>()
// .WriteTo.ApplicationInsights(services.GetRequiredService<TelemetryConfiguration>(), TelemetryConverter.Traces));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
I've configured my appsettings.json file according to the Microsoft docs here, but threw in an InstrumentationKey as well just in case that would help.
{
"ApplicationInsights": {
"InstrumentationKey": "--I put the key here--",
"ConnectionString": "---I put a connection string here---"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information"
}
},
"AllowedHosts": "*"
}
I've been banging my head against this for 3 hours now and it just doesn't work. I've been able to configure application insight WITHOUT Serilog in the application and it logged to the Application Insights resource, but I just can't get it to work WITH Serilog.
I suspect it has something to do with the instrumentation key not being read since I see error No service for type 'Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration' when I uncomment the code in program.cs.
Any help would be appreciated.
Your Instrumentation key is not configured properly.
Refer the below workaround
Make sure you have below Nuget packages installed are updated
Serilog.Settings.Configuration
Serilog.AspNetCore
Serilog.Sinks.ApplicationInsights
Microsoft.ApplicationInsights.AspNetCore
Program.cs
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.WriteTo.ApplicationInsights(TelemetryConfiguration.Active, TelemetryConverter.Traces)
);
Output :
References taken from :
Serilog.AspNetCore

Unable to get Linux container running as Azure function in isolated process

The function app runs locally but is deployed as a Linux container. The deployed function isn't reporting any issues in the portal and I can see the three functions listed in the Functions blade. However, none are working (one is a simple HTTP ping which is returning a 502 Bad Gateway response).
So no obvious issues in the portal, but inspection of logs reveals this recurring exception:
System.Threading.Tasks.TaskCanceledException at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess
The formatted message is:
Failed to start a new language worker for runtime: dotnet-isolated
My suspicion is that this is something to do with the value of the site setting linuxFxVersion. Mind you, I've tried using DOTNET|6.0 and DOTNET-ISOLATED|6.0. In both cases it doesn't seem to make a difference and anyway when I export the function app's ARM template it has linuxFxVersion set to the URI of the container image prefixed with DOCKER|.
That seems to relate to this specific advice from Microsoft about pinning the host version on Linux. But it still isn't clear to me which value I should us, and anyway, in another place the advice from another Microsoft document states:
To support zip deployment and running from the deployment package on Linux, you also need to update the linuxFxVersion site config setting to DOTNET-ISOLATED|6.0.
Anyway, Here are the details of my config. I've followed every bit of guidance from Microsoft so I hope someone can spot what I've missed...
First two parts of project file:
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>V4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>true</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="3.0.9" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.3.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.5.2" />
</ItemGroup>
host.json:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
Main method in Program.cs:
public static async Task Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration((context, configurationBuilder) =>
{
configurationBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
if (context.HostingEnvironment.IsDevelopment())
{
configurationBuilder.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true);
}
else
{
configurationBuilder.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true);
}
configurationBuilder.AddEnvironmentVariables();
})
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build()
.RunAsync();
}
The function provisioning pipeline sets the following app settings:
FUNCTIONS_EXTENSION_VERSION: '~4'
FUNCTIONS_WORKER_RUNTIME: 'dotnet-isolated'
As stated above, the latest version of my site config defined during provisioning includes this:
linuxFxVersion: 'DOTNET-ISOLATED|6.0'
The Docker image uses mcr.microsoft.com/azure-functions/dotnet-isolated:4 as a base image for the published app, and mcr.microsoft.com/dotnet/sdk:6.0 to build it.
Please tell me there's something obvious I'm missing. We currently have two function apps, and neither can be deployed as dotnet-isolated. It's driving me mad!
The exception what you saw is not directly from your application, instead it is a general exception without detaills from an Azure Function runtime-process - so it's useless.
The reasons can be anything, for example: forget to await on a task.
public static async Task Main()
{
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
.Build();
})
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build();
host.RunAsync(); // await host.RunAsync();
}
Or a corrupted appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft": "Warning",
"Host": "Warning",
"Function": "Information",
"Host.Aggregator": "Information"
}
}
Or a missing configure call during startup.
public static async Task Main()
{
var host = new HostBuilder()
// .ConfigureFunctionsWorkerDefaults()
.ConfigureAppConfiguration(builder =>
{
builder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true)
.Build();
}))
.ConfigureServices((context, collection) => ConfigureServices(collection, context.Configuration, context.HostingEnvironment))
.Build();
await host.RunAsync();
}
That runtime-process from Microsoft which launches your application captures all your console logs and trashes them in case of exceptions during its own startup.
Btw, you should also enable Code Analysis which is per default not enabled in .net 6.
After battling with this for 2 days I decide to strip right down to the bare bones. Followed the Microsoft guide from scratch. This led me to identify the following line as the problem:
configurationBuilder.AddEnvironmentVariables();
As soon as I add this line to my Program and push a new image to the function app, everything breaks upon restart. However, there's no indication as to why. The Docker logs exposed by Kudo report this as the final entry:
2022-05-18T14:06:46.295Z INFO - Container [image-name] for site [function-name] initialized successfully and is ready to serve requests.
App Insights traces indicate that an exception has occurred at line 52 of Program.cs, which is my host.Run(); line.
The exception itself is:
System.InvalidOperationException at Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcFunctionInvocationDispatcherLoadBalancer.GetLanguageWorkerChannel
And there's a message hidden in there saying "Did not find any initialized language workers" which I understand is Azure Function language for "something bad happened during startup but I'm not going to tell you what or why".
So this at least explains why my function wasn't running, and hopefully my experience will save someone else time in the future, but since my app depends on configuration added to the function by a pipeline I still don't have a working app. For that I will ask a new question and link it here...
Update
Here is the follow up question, which I've already answered!

Serilog ignoring LogLevel from Microsoft.Extension.Logging

I am using NET5 ASP.NET MVC application. Application is using Serilog.AspNetCore 3.4.0 for logging
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Error"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.Console"],
"WriteTo": [
{
"Name": "Console"
}
]
}
}
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
})
.UseSerilog((hostingContext, logging) =>
{
logging.ReadFrom.Configuration(hostingContext.Configuration);
});
});
I have also tried
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger();
logging.AddSerilog();
});
});
Issue
My expectation is no Information log will be shown in Console since default LogLevel is Error.
However, that is not working. In console I see every request is getting logged including Information
Throughout my application I am using Microsoft.Extensions.Logging.ILogger<MyClassName> to log information. All those statements are actually logging Info even if the LogLevel is Error.
Looks like Serilog ignoring LogLevel from Microsoft.Extensions.Logging.
Note that, I can set serilog's restrictedToMinimumLevel property to Error and that stops logging information. However I think serilog should obey the LogLevel from Microsoft.Extension.Logging
Use MinimumLevel property:
"Logging": {
"MinimumLevel": {
"Default": "Error"
}
}
Also it supports overrides for categories:
"Logging": {
"MinimumLevel": {
"Default": "Error",
"Override": {
"System": "Information",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore": "Debug"
}
}
}
If you check the project's Github page it advises to actually remove the standard "Logging" section in appsettings.json :-(
I think this is bad since it actually breaks the compatibility with Microsoft.Extensions.Logging framework (you can't anymore change the actual log provider (Log4Net, NLog, Serilog) without changing the appsettings.json).
If you use Serilog.Extensions.Logging and AddSerilog() on ILoggingBuilder, you'll get what you're expecting.
However, IHostBuilder.UseSerilog() (provided by Serilog.Extensions.Hosting, via Serilog.AspNetCore) is almost always a better choice, hence that's what all the Serilog docs show.
Although it seems more "integrated" to use the default configuration section this way, what you're getting behind the scenes is actually two different logging frameworks running at the same time, e.g. in the AddSerilog() case (or in the default NLog configuration):
ILogger<T> -->
MEL (routing, configuration, levels, filters) -->
Serilog (routing, configuration, levels, filters)
Logging is supposed to be very predictable/reliable and lightweight. Having two logging frameworks running at the same time erodes this and creates problems where the levels and filters specified for one framework don't match the other. (E.g. Debug and trace logs not printed when using Nlog and ILoggerFactory in .NetCore console app).
In the UseSerilog() case, (and when NLog's ReplaceLoggerFactory option is specified - mentioned in a comment on the SO thread above), you get:
ILogger<T> -->
Serilog (routing, configuration, levels, filters)
You give up support for the default "Logging" configuration section, but in exchange, you get rid of a whole logging framework at runtime, and all of your Serilog (or NLog) configuration just works, with the Microsoft.Extensions.Logging ILogger<T> available to your application code and the framework.

Serilog not loading configuration?

I just got started with Serilog. I started out a very simple configuration, but I can't confirm it's loaded...maybe I am not looking into the right place?
appsettings (snippet):
"Serilog": {
"Using": [ "Serilog", "Serilog.Sinks.Console", "Serilog.Sinks.EventLog" ],
"MinimumLevel": {
"Default": "Debug"
}
program.cs
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(
(hostingContext, config) =>
configureConfiguration(hostingContext, config))
.UseSerilog(
(hostingContext, config) =>
configureSeriLog(hostingContext, config));
// local function
void configureSeriLog(HostBuilderContext cntxt, LoggerConfiguration builder)
{
builder.ReadFrom
.Configuration(cntxt.Configuration);
}
I set a breakpoint at the ReadFrom.Configuration() call. After the call is made, I look at the non-Public members _minimumLevel under the builder variable and it says Information. Am I looking at the wrong spot or the configuration wasn't loaded. Thanks a million!
To diagnose issues, you can try using the SelfLog
Serilog.Debugging.SelfLog.Enable(x => Debug.WriteLine(x));
Debugging and Diagnostics
When Serilog is not behaving as you expect, this may be caused by an
internal exception or configuration issue. Here are a couple of ways
to sort things out.
...

What's the point of hosting.json since appsettings.json is sufficient

In .NET Core 2 Web API app, I could override configuration urls using appsettings.json, but in the official docs they introduced extra file "hosting.json", Why? What's the point of adding complexity?
Below code is fully working using appsettings.json:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) //see Side note below
.AddJsonFile("appsettings.json", optional: true)
.AddCommandLine(args)
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();
}
}
appsettings.json content:
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
},
"urls": "http://*:5005/"
}
Side note:
Commenting .SetBasePath(Directory.GetCurrentDirectory()) will keep VS 2017 debug mode operational (means apply launchSettings.json, and auto launch url) otherwise it won't. I guess its related to the CreateDefaultBuilder implementation.
I think, hosting.json is a configuration file used specifically for asp.net core application hosting. (if you know more about hosting)
WebHostBuilder directly maps its keys with the hosting.json file and it doesn't have the capability to load the config section as we do in normal configuration settings.
According to link attached in your post
Use Configuration to configure the host. In the following example,
host configuration is optionally specified in a hosting.json file. Any
configuration loaded from the hosting.json file may be overridden by
command-line arguments.
If only we explicitly us hosting.json then the WebHostBuilder configurations can be modified using dotnet command.
for example
dotnet run --urls "http://*:8080"
this will override the urls from hostings.json file.
Hopefully, this may give some idea.
PC: hosting.json can be renamed like myappsettings.json it can
have configuration and Web Host Builder configuration.

Categories