Logging in .NET with Serilog - c#

In my application, users can run multiple jobs, each of which will do some operations on files and output to another file. In the course of these operations I need to log any errors I run into in multiple log files, one per job.
I know that I can set up different sinks (i.e. multiple log files) in code with Serilog, so I'm injecting it into my service for performing operations and then do the logging there. This service is injected into my API controller which just calls service methods.
What I'm not sure about here is how to handle calling Serilog logging methods from within the controller, since I need to do some logging in there while still using the same sink that I'm using in my service. For example, for one job there will be one log file which is shared between the controller and the service for the duration of the job.
If I expose an Interface method like
public void SetLogFile(string fileName)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.RollingFile(pathFormat: _hostingEnvironment.ContentRootPath + "/" + fileName + ".log",
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {SourceContext} [{Level}] {Message}{NewLine}{Exception}")
.CreateLogger();
}
in my service which I then call my the controller or wherever, is the configuration global and usable in my controller if I also inject the ILogger in there?
Any tips would be much appreciated!

Related

What is the difference between AddSerilog and UseSerilog in .NET 6 Web API?

I'm trying to configure Serilog for a Web API project in .NET 6.
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(Log.Logger);
//builder.Host.UseSerilog(Log.Logger);
What behavior difference is there between adding Serilog to the logging pipeline and setting Serilog as the logging provider? Should I call both methods?
There is a huge difference. 🙂
The .AddSerilog() provider adds a Serilog provider as one of potentially many providers. With the following configuration, the Microsoft logger will first log to the Console provider, then to the Serilog provider:
.ConfigureLogging(logging => logging.AddConsole().AddSerilog())
The .UseSerilog() configures Serilog as the only provider. The following will send all logs to Serilog regardless of whether you've configured the logging pipeline:
.UseSerilog();
The difference really boils down to using Micosoft's pluggable model or using Serilog's pluggable model.
Typically you wouldn't use .AddSerilog() as the Serilog library is really intended to be used as the sole provider with one or more "sinks", but there may be cases where you need to log to a particular destination for which there exists a Micosoft ILogger and ILoggerProvider, but for which no Serilog sink exists (and you don't want to have to write it yourself). In such cases, you might choose to add Serilog as an additional provider.
There is no difference, only syntax varies. However you need to ensure that if you read from configuration then there is no need to mention enrichers or files in the declaration, else you might end up creating two log file output:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext() // No need
.WriteTo.Console() // No need
.WriteTo.File("Logs/Log.txt") // No Need
.CreateLogger();
With intellisense it is easier to define the configuration in code rather than in appsetting.json. As opposed to configuration you can detect error in this method quite easily. Hence better use :
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("Logs/Log.txt")
.CreateLogger();

Serilog WCF usage without constructors?

I am new to c# and I am trying to using serilog in a WCF application. This WCF application is hosted on IIS 6.0.
I need to create the logger once only when the service is initialized. However WCF does not seem to have constructors. How can I create the logger below once only and have it available to the rest of the application. Any code examples would be greatly appreciated.
ILogger logger = new LoggerConfiguration()
.ReadAppSettings()
.CreateLogger();
Log.Logger = logger;
I thought about using the AppInitialize in the App_Code folder but I cannot seem to get the program to trigger the AppInitialize method.
public class InitializeApp
{
public static void AppInitialize()
{
ILogger logger = new LoggerConfiguration()
.ReadAppSettings()
.CreateLogger();
Log.Logger = logger;
}
}
When I run the project in Visual Studio 2019 AppInitialize never gets hit.
First, you can do the constructor with WCF Service via some Dependency Inject library which supports WCF Service
You can use AutoFac.WCF then you can register type of ILogger by Autofac
builder.Register<ILogger>((c, p) =>
{
return new LoggerConfiguration()
.ReadAppSettings()
.CreateLogger();
}).SingleInstance();
Second, About the "InitializeApp" of the application. there are couple ways to do it.
Add Global.asax file and then you can write the code in Application_Start
Use Owin Middleware to write own pipeline. You can write your code in Startup.cs
Use WebActivator, you can write PreApplicationStartMethod and PostApplicationStartMethod (extension for Global.asax)

How to use Serilog in .NET Core Console app

I wanted my application to have capability of logging to a file, so I started to look for something more than default .NET Core 2.2 logging framework. I see that Serilog might do the job. However, I cannot find any document on how to setup Serilog in .NET Core Console application with Dependency Injection. All I see is ASP.NET materials, which is probably not what I need.
I started doing it myself. I installed (Nuget):
Serilog
Serilog.Extensions.Logging
Serilog.Sinks.File
Serilog.Sinks.Console (to use Serilog for all my logging)
I created an extension forServiceCollection
public static void AddLogging(this IServiceCollection services, Microsoft.Extensions.Logging.LogLevel logLevel)
{
var serilogLogger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("log.txt")
.CreateLogger();
services.AddLogging(builder =>
{
builder.SetMinimumLevel(logLevel);
builder.AddSerilog(logger: serilogLogger, dispose: true);
});
}
Logging works, however:
log level is not what I set it to. It seems that serilog is using INFO level, although I wanted to have DEBUG. Why isn't my setting respected? After all, I'm still using NET Core's logging framework, so I'm using it to setup the log level
am I actually doing this setup correctly? I am not really sure if dispose should be true. Generally, I want NET Core's Dependency Injection framework to take care of disposal of services.
I'm not sure about builder.SetMinimumLevel (it doesn't use the Serilog enum).
We set the logger level when creating the LoggerConfiguration object.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug) // restricted... is Optional
(...)
.CreateLogger();
BTW. It's worth pointing out the following section from Configuration Basics
Logger vs. sink minimums - it is important to realize that the logging level can only be raised for sinks, not lowered. So, if the logger's MinimumLevel is set to Information then a sink with Debug as its specified level will still only see Information level events. This is because the logger-level configuration controls which logging statements will result in the creation of events, while the sink-level configuration only filters these. To create a single logger with a more verbose level, use a separate LoggerConfiguration.
I'm not sure about builder.AddSerilog.
Here's what works for me.
using Serilog;
(...)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
(...)
.CreateLogger();
(...)
return new HostBuilder()
.ConfigureHostConfiguration(...)
.ConfigureServices(...)
.UseSerilog();

Microsoft Access-friendly Serilog sink

I am using SEQ, file and JSON as a Serilog sinks
Log.Logger = new LoggerConfiguration()
.Enrich.With(new ThreadIdEnricher())
//.Enrich.FromLogContext()
.WriteTo.RollingFile(#"C:\QRT\Logs\QRT-LOG.txt", LogEventLevel.Information)
.WriteTo.Seq("http://localhost:5341")
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
.WriteTo.File(new CompactJsonFormatter(), "C:/QRT/Logs/log.clef")
.CreateLogger();
SEQ is for me because it looks like it would be really useful.
JSON I may do away with... I was attempting to write a file that I could import into Access. The point is that I need my non-developer friend to be able to see the logs and Access is a tool I believe he can use to easily filter on items such as Customer ID etc. I have not been able to find much documentation on the Serilog sinks other than their names. Can someone either suggest a mechanism to sink to something that can be imported to Access or another sink that a user-friendly tool can ready?
I am currently using NLog and GamutLogViewer which is awesome because it can color entries based on regular expressions!
Any suggestions would be most welcome. The idea is my friend is not looking at the logs to debug. He will be looking at the "Information" contained in the logs.
This is using C# on a console app in Windows.
Thanks
-Ed
Serilog has a sink called Serilog.Sinks.NLog which adapts Serilog to write events through your existing NLog infrastructure, which means you can effectively use Serilog throughout your app, but output log files in the NLog format, which would be readable by the GamutLogViewer (or YALV! as an alternative).
Another approach I can think of is to use the sink Serilog.Sinks.MSSqlServer where you write your logs to a SQL Server table (could even be a SQL Server Express instance on the user's machine, if you don't want/have a shared SQL Server) and then use Microsoft Access to query these logs via linked tables in Access.
Ultimately, you could develop your own sink that writes directly to a .csv file or even directly to an Access .accdb file, for example. Developing Sinks for Serilog is super easy and there are tons of examples you can use as a base for your custom sink.

LoggerFactory To Output To File

Currently the application, Logger writes to the console by using the AddConsole() method.
How can it be set to write to a file?
for example to the directory "c:\workspace\TestProject\Log.txt"
logger = new LoggerFactory()
.AddConsole()
.CreateLogger("Msg");
I guess, you are using Microsoft.Framework.Logging
But out-of-the-box implementations are provided for basic console logging and a few other targets, you’ll need a logging back-end like Serilog or NLog to gain the kind of functionality you're requesting.
I would recommend you to use NLog (just personal preference)
Install-Package NLog
then add to your code
loggerFactory.AddNLog(new global::NLog.LogFactory());
https://github.com/aspnet/Logging/tree/dev/samples/SampleApp
http://nlog-project.org/

Categories

Resources