Serilog WCF usage without constructors? - c#

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)

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();

C# ConsoleApp injection startup?

I have a simple C# .NET Core 5 Console Application that I need to add dependency injection (Microsoft.Extensions.DependencyInjection) to. I know how to do this if it suppose to start a micro service but what If I want to just run it as a regular Console Application with DI?
I got this code :
static void Main(string[] args)
{
var serviceName =
System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json").Build();
var appSettings = configurationBuilder.Get<AppSettings>();
Log.Information("{#serviceName} test starting up.", serviceName);
Host.CreateDefaultBuilder(args)
.UseMyAppMQ(context => context.UseSettings(appSettings.MQSettings))
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
.ConfigureServices((hostContext, services) =>
{
services
.Configure<MQSettings>(configurationBuilder.GetSection("MQSettings"))
.AddTransient<ITestController>(s => new TestController());
})
.Build().Run();
Log.Information("{#serviceName} test closing down.", serviceName);
}
I need a entry point where I can run my underlaying class run method, but how?
Regards
You need to implement IHostedService interface (or extend BackgroundService base class) and register it with services.AddHostedService<YourServiceClass>() or
builder.Services.AddHostedService<YourServiceClass>(), if using .NET 6 minimal API as described in official docs. In this case IHostedService.StartAsync will be your entry point.
However, it does seem very inefficient, if all you need is just a simple console app with DI. As #Panagiotis Kanavos suggests you can build the host without running it, because then you can use it as a wrapper around DI container and resolve any registered service. In this case the entry point for your code is the next line after you've built your host, where you can resolve any registered dependency with
var host = Host.CreateDefaultBuilder(args)
...
.Build();
host.Services.GetService<YouService>()
But it is still an inefficient solution for just a console app, because you only need DI container, but not the entire host. Just use any third-party DI framework (like Autofac, Ninject or any other) instead of Microsoft.Extensions.DependencyInjection. Their setup is usually quite minimalistic and you will get just DI container with you services without anything else. You can still use configuration package and loggers, just register them in your container similarly to how you've done it before with UseXxxx methods.

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();

Logging in .NET with Serilog

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!

Serilog RavenDb sink not working in an asp.net 5 app

I am looking at serilog and running a few tests. So far it is working fine writing to the console or file. However I am not having any luck getting it to work with the RavenDb sink. I am trying to get this working in an asp.net 5 app.
I have reviewed the following articles:
http://nblumhardt.com/2015/05/diagnostic-logging-in-dnx-asp-net-5/
http://nblumhardt.com/2013/06/serilog-and-ravendb/
I started with an empty app, and added the following dependencies in project.json.
"Serilog.Framework.Logging": "1.0.0-rc1-final-10071",
"Serilog.Sinks.RavenDB": "1.5.4",
"RavenDB.Client": "3.0.30000"
I also removed dnxcore.
Then I added the following code in startup.cs:
public Startup()
{
var documentStore = new DocumentStore()
{
Url = "http://localhost:8080",
DefaultDatabase = "Logs"
}.Initialize();
Log.Logger = new LoggerConfiguration()
.WriteTo.File(#"c:\temp\log.txt")
.WriteTo.Console()
.WriteTo.RavenDB(documentStore)
.CreateLogger();
}
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddSerilog();
app.UseIISPlatformHandler();
app.Run(async (context) =>
{
Log.Information("Hello World");
await context.Response.WriteAsync("Hello World!");
});
}
Everything gets logged to the file and console just fine, and the Logs database gets created, but no log entries are stored in RavenDb.
I have tried various log levels. I tried reducing the batch size. I suspected this had something to do with the lifecycle of the document store, so I added the following in the ConfigureServices method.
services.AddSingleton(x =>
{
return new DocumentStore()
{
Url = "http://localhost:8080/",
DefaultDatabase = "Test",
}.Initialize();
}
Then I moved the logger configuration code into the Configure method and used DI instance, but that doesn't work either. I can store other objects in RavenDb using the same DocumentStore just fine.
Have I missed a configuration setting or something?
I was able to get this working with the latest RavenDb client. I created a new (package) style library, added Serilog nuget package v2.0.0-beta-403, added the RavenDB.Client v3 nuget package, and dropped in the .cs files from the existing Serilog.Sinks.RavenDb library. It compiled and worked, I didn't have to change any code.
I haven't had a chance to test it much yet, but it seem to be working fine. Of course I don't know how stable Serilog v2 beta is, or how long until it is released. The nice thing is that serilog v2 supports .netcore. Unfortunately the RavenDb client doesn't, at least not yet.

Categories

Resources