serilog multiple instance per functionality - c#

While I have a general purpose logger for errors, exceptions etc that use rolling file sink to log stuff to my-{date}.log.
However, I need another instance for audit to audit-{date}.log and another instance to write perf info to perf-{date}.log.
How do I create multiple instances of serilog with different configuration or sink?

You can do this like following:
var loggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext();
var fileBasePath = "<base log path>";
loggerConfiguration
.WriteTo.Console()
.WriteTo.RollingFile(fileBasePath + "log-info-{Date}.txt")
.WriteTo.Logger(fileLogger => fileLogger
.MinimumLevel.Error()
.WriteTo.RollingFile(fileBasePath + "log-error-{Date}.txt"))
.WriteTo.Logger(fileLogger => fileLogger
.Filter.ByIncludingOnly(x =>
x.Level == LogEventLevel.Information &&
x.Properties.ContainsKey("<Audit Key>"))
.WriteTo.RollingFile(fileBasePath + "log-audit-{Date}.txt"));
Log.Logger = loggerConfiguration.CreateLogger();
Note: Audit Key is a key which used in audit log

I assume you would need different interface name to write audit and perf data (to be injected to your main business code). If that's the case just implement your IAudit, IPerfData with a new Serilog instance which you can totally customize where/how you save the log.

Related

How to write logs to a specific sink depending on the developement environment in Serilog?

I'm using Serilog with Azure Functions v4 and .NET 7. I am trying to write logs to both the local console and Application Insights. I already spent hours trying different solutions and the situation at the time of writing seems to be that this technology stack (Azure Functions + Application Insights) is not mature or too recent, and not yet very well documented.
Setting up appsettings.{env}.json files by environment and trying to add a different Serilog config by environment does not work with Azure Functions. Depending on the solution I tried, either Microsoft logger is used or there are no logs.
The code below is working for the console but I get duplicate logs on Application Insights. And if I remove Console sink, it works well in Application Insights but I get no more local logs:
.UseSerilog((hostBuilderContext, serviceProvider, loggerConfiguration) =>
loggerConfiguration
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.ApplicationInsights(
serviceProvider.GetRequiredService<TelemetryConfiguration>(),
TelemetryConverter.Traces)
)
I also tried other ways of setting up the logger, for example in .ConfigureServices(..., injecting it differently in the function code, but no luck. Many samples talk about a Startup method that I don't have. If it helps I can post the whole Program.cs and the function code here.
So I now added .Enrich.WithEnvironmentName() to get the environment name from an environment variable, and the question is: how can I filter the logs using ByIncludingOnly in the code above (not config file) to include logs depending on the environment name (Local or Development)? Same question for ByExcluding - I guess answer will be similar.
This is the way to do it. I ended up not using the Environment enricher because it is at the time of writing not supporting the Azure environment variable for Azure Functions (AZURE_FUNCTIONS_ENVIRONMENT). See this issue:
https://github.com/serilog/serilog-enrichers-environment/issues/49
This is how I got this working:
First option enriching the log event:
// Careful: Serilog is not able to enrich with the value of this variable and will default to 'Production'
var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
var predicate = $"EnvironmentName = 'Local'";
Log.Logger = new LoggerConfiguration()
.Enrich.WithProperty("EnvironmentName", value)
.MinimumLevel.Debug()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(predicate)
.WriteTo.Console(outputTemplate: "{Properties} {Message}"))
.WriteTo.Logger(lc => lc
.WriteTo.File(path: Directory.GetCurrentDirectory() + #"\test.txt"))
.CreateLogger();
Second option without enriching the log event:
var value = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT") ?? "Development";
var predicate = $"{value} = 'Local'";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(predicate)
.WriteTo.Console(outputTemplate: "{Properties} {Message}"))
.WriteTo.Logger(lc => lc
.WriteTo.File(path: Directory.GetCurrentDirectory() + #"\test.txt"))
.CreateLogger();

Serilog - Splitting the log files using Correlation ID

How to split the log file based on correlation ID enricher?
I have configured the correlation ID enricher but I want to create log files based on correlation ID.
(Example: fisjdbs-13727-hrjsb).
It needs to write all the logs for that ID into fjsjdbs-13727-hrjsb.log
Please suggest some approaches.
You can do something like this in .NET6
builder.Services.AddHttpContextAccessor();
Logger log = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.WithCorrelationId()
.WriteTo.Console()
.WriteTo.Map("CorrelationId", (id,wt) => wt.File($"{id}.log"))
.CreateLogger();
builder.Host.UseSerilog(log);
and you can see files are getting generated based on "CorrelationId".
Hope it helps.

How to access Log4Net after adding it to Dependency Injection

This is my first time working with Microsoft.Extensions.Logging.Log4Net.AspNetCore.
I have a WPF application where I add the Log4Net provider within the CreateDefaultBuilder in App method of App.xaml.cs. Immediately after, I want to write to the log file using LogInformation saying "Starting Application". From what I can tell, it does not seem like I can do this because of the way it has been added to the Dependency Injection container. I must either call a method that accesses the DI container in the parameter list or add Log4Net to the ServiceProvider and then retrieve the service that way. But, that does not seem right because I will effectively have Log4Net added to DI twice.
Is there a way to immediately access the DI container after configuring Log4Net so I can write to the log file?
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, appBuilder) =>
{
// Do some stuff here
}).ConfigureLogging(logBuilder =>
{
logBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
logBuilder.AddLog4Net("log4net.config");
})
.ConfigureServices((context, services) =>
{
Configuration = context.Configuration;
ConfigureServices(Configuration, services);
})
.Build();
ServiceProvider = Host.Services;
// How to access Log4Net here?
_ISomeLogger.LogInformation("Starting Application");
Update
I am using the following example: https://www.thecodebuzz.com/logging-using-log4net-net-core-console-application/
The solution is to access the Log4Net from the ServiceProvider right after building it:
logger = (ILogger<Program>)ServiceProvider.GetService(typeof(ILogger<Program>));

Logging important info in WebAPI 2 using Serilog

Just started working with Serilog, Its quite nice but I am a bit confused.
In case of errors in my Controller's actions, I'd like to log as much useful information as possible from the request (headers, parameters, etc).
What would be the best way to go about it?
You could look at using the Enrichment feature
As per the documentation from the link, you would construct your logger similar to
var log = new LoggerConfiguration()
.Enrich.WithThreadId()
.WriteTo.Console()
.CreateLogger();
"All events written through log will carry a property ThreadId with the id of the managed thread that wrote them. (By convention, any .WithXyz() methods on Enrich create properties named Xyz.)"
An example on Github is SerilogWeb.Classic. It provides a number of enrichers to capture certain information from the Request, e.g.
var log = new LoggerConfiguration()
.WriteTo.ColoredConsole()
.Enrich.With<HttpRequestIdEnricher>()
.Enrich.With<UserNameEnricher>()
.CreateLogger();
You could follow a similar apporach, creating an enricher that captures the information you always want to output in your logs, then initialising your logger using the enricher.

Serilog topshelf integration not working

I'm trying to set up a simple logging configuration for my Windows service using Topshelf and Serilog (the Serilog.Extras.Topshelf package respectively).
HostLogger.UseLogger(new SerilogHostLoggerConfigurator(new LoggerConfiguration()
.WriteTo.RollingFile(AppDomain.CurrentDomain.BaseDirectory + "\\logs\\app-{Date}.log")
.WriteTo.ColoredConsole()
.MinimumLevel.Debug()
.CreateLogger()));
HostFactory.Run(x => {
x.UseSerilog();
...
The service runs fine, however no output is made, neither to the console nor the specified log file (I can see that one is being created but it remains empty). Has anyone experience using both frameworks?
The second call "x.UseSerilog()" resets TopShelf's HostLogger to use Serilog's global instance (Log.Logger), which you have not configured.
Remove the second call and the logging should start working.
Another option is to configure the global logger:
Log.Logger = new LoggerConfiguration()
.WriteTo.RollingFile(AppDomain.CurrentDomain.BaseDirectory + "\\logs\\app-{Date}.log")
.WriteTo.ColoredConsole()
.MinimumLevel.Debug()
.CreateLogger();
HostFactory.Run(x => {
// configure TopShelf to use Serilog's global instance.
x.UseSerilog();
}

Categories

Resources