How to overwrite application name specified in appsettings.json when using Serilog? - c#

My appsettings.json looks like this:
{
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "%APPDATA%\\FancyProject\\logs\\RR.log",
"formatter": "Serilog.Formatting.Json.JsonFormatter",
"rollingInterval": "Day",
"retainedFileCountLimit": 20,
"buffered": false
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "WithExceptionDetails" ],
"Properties": {
"Application": "SampleName"
}
}
}
Loading the settings:
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
I'd like to use the same config file for multiple projects in my solution but with a different application name in order to distinguish between services.
Is there a way to change the application name ("SampleName" in my config) in code when loading the config?

You can use the following extension method for IConfiguration interface to update the configuration instance after reading it from appsettings.json
public static class Ext
{
public static IConfiguration ApplyAppName(this IConfiguration configuration, string appName)
{
foreach (var (key, _) in configuration.AsEnumerable())
if (key.StartsWith("Serilog") && key.EndsWith("Application"))
configuration[key] = appName;
return configuration;
}
}
And use it in following way (based on
serilog-settings-configuration sample from GitHub) before configuring and creating a logger
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build()
.ApplyAppName("MyApp");
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
Alternatively, you can follow Serilog integration for ASP.NET Core 2+ and use UseSerilog() method with the above ApplyAppName extension during CreateHostBuilder call
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.UseSerilog((hostingContext, loggerConfiguration) =>
{
var config = hostingContext.Configuration.ApplyAppName("MyApp");
loggerConfiguration.ReadFrom.Configuration(config);
});

I solved it by removing the Properties section from the config and load it this way:
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.WithProperty("ApplicationName", "my application")
.CreateLogger();
PavelAnikhouski's (deleted) answer was actually the correct answer to my question but I think directly adding the correct application name is cleaner than modifying a placeholder.

Related

.NET Core 6 Console App hosted service not starting as Windows Service?

I got a .NET 6.0 Core Console Application that starts a service and log some data with Serilog which works good. But after installing it as a Windows Service and hitting start the services seems to start but I get no log data anymore? What am I missing?
Start up :
public static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration().DefaultLoggerSetup<Program>();
var microsoftLogger = new SerilogLoggerFactory(Log.Logger).CreateLogger<IProgram>();
var serviceName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
Log.Information("Starting {serviceName} version {version}.", serviceName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString());
Log.Information("{#serviceName} microservice loading MySettings.json.", serviceName);
var configurationBuilder = new ConfigurationBuilder().AddJsonFile("MySettings.json").Build();
var MySettings = configurationBuilder.Get<MySettings>();
Log.Information("{#serviceName} microservice loading appsettings.json.", serviceName);
configurationBuilder = new ConfigurationBuilder().AddJsonFile("appSettings.json").Build();
var appSettings = configurationBuilder.Get<AppSettings>();
var sqlConnectionString = appSettings.ConnectionString;
Log.Information("{#serviceName} building and starting.", serviceName);
CreateHostBuilder(args, configurationBuilder, appSettings, MySettings, sqlConnectionString).Build().Run();
Log.Information("{#serviceName} microservice Built and stated.", serviceName);
}
Method to build the host :
public static IHostBuilder CreateHostBuilder(string[] args,
IConfiguration configurationBuilder,
AppSettings appSettings,
MySettings mySettings,
string sqlConnectionString) =>
Host.CreateDefaultBuilder(args)
.UseCloudMQ(context => context.UseSettings(appSettings.MQSettings))
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext())
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services
.AddTransient<ConnectorConfig>(s => appSettings.ConnectorConfig)
.AddTransient<IMyService, MyService>()
.AddTransient<IMyController, MyController>()
.AddTransient<IMySettings>(s => MySettings)
.AddTransient<IMyService, MyService>()
.AddSingleton<IInputOuputLogger>(new InputOuputLogger(configurationBuilder))
.AddDbContext<AppDbContext>(options => options.UseSqlServer(sqlConnectionString, opt => opt.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)))
.AddHostedService<MyService>();
});
Simple hosted service :
public class MyService : IHostedService, IMyAnestesiService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
_logger.LogInformation("{#serviceName} microservice started.", GetType().Name);
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
public void Run()
{
_logger.LogInformation("MyService started.");
}
}
Edit : When running the service in CMD the log in displayed in the CMD but its also saved to file with this :
"serilog": {
"Using": [
"Serilog",
"Serilog.Sinks.File",
"Serilog.Sinks.Console"
],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss.fff} [{Level}] {SourceContext} {Message}{NewLine}{Exception}",
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
}
},
{
"Name": "File",
"Args": {
"path": "C:\\DEVSTUFF\\log.txt",
"outputTemplate": "{Timestamp:G} {SourceContext} [{Level}] {Message}{NewLine:1}{Exception:1}",
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"fileSizeLimitBytes": 1000000,
"rollOnFileSizeLimit": "true",
"shared": "true",
"flushToDiskInterval": 3
}
}
]
}
So even if I canĀ“t see the log in the CMD while running it as a Windows Services I should at least get log rows in file like before.
Where are you writing logs in the Windows Service scenario?
For the console application, it can write logs to the Terminal which is not possible for the windows service scenarios. You need to write somewhere like a file, or database.
You can have a look at this link

Serilog not writing to file

Setting up Serilog for my .net core app, everything compiles and runs but not writing to the file. How can I pinpoint the problem?
I am only use Serilog package - but there seems to be a multitude of others, and I'd be happy to use another one.
In my startup.cs I have
using Serilog;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.ReadFrom.Configuration(configuration)
.CreateLogger();
}
in my appsettings.json I have
{
"AllowedHosts": "*",
"Serilog": {
"MinimumLevel": "Debug",
"Override": {
"Microsoft.AspNetCore": "Warning"
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "C:\\log\\Batching\\apptest.log"
}
}
]
}
}
in my Program.cs I have
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
And finally I use this statement to test the writing:
Log.Information("write something to file");
It is not working. What can I try next?
Just as mentioned in the document:
To use the file sink with Microsoft.Extensions.Configuration, for example with ASP.NET Core or .NET Core, use the Serilog.Settings.Configuration package
And you could find introduction to different packages in this page:
https://github.com/serilog
I tried with the packages:
Set as below in program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog((context, configuration) =>
{
var config = context.Configuration;
configuration.ReadFrom.Configuration(config);
});
It works well in my case:
Update:
I configured as below in appsettings.json:
I also tried with the absolute path of my desktop, it worked either

Serilog : RollingFile is not working in asp.net core with 'appsettings.json'

I am using asp.net core(website) and Serilog for logging.
Nuget packages installed are
Serilog
Serilog.AspNetCore
Serilog.Extensions.Logging
Serilog.Extensions.Logging.File
Serilog.Settings.Configuration
Serilog.Sinks.File
Serilog.Sinks.RollingFile
Microsoft.Extensions.Logging
Settings in Main.cs file are :
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((WebHostBuilderContext, configurationBuilder) =>
{
configurationBuilder.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
configurationBuilder.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true);
configurationBuilder.AddEnvironmentVariables();
})
.ConfigureLogging((hostingContext, loggingBuilder) =>
{
loggingBuilder.ClearProviders();
loggingBuilder.AddSerilog();
})
.UseStartup<Startup>();
}
What is working :
In Startup.cs file if I write following code the logging file is getting created. Here I am specifying the configurations in code itself.
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
string logFilePath = Path.Combine(env.ContentRootPath, "Logs/ImportManager2-{Date}.txt");
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.RollingFile(logFilePath)
.CreateLogger();
loggerFactory.AddSerilog();
--
--
}
When I am using appsettings.json file with following content
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MyDatabaseLocal": "Server=_;Database=_;MultipleActiveResultSets=true;Connection Timeout=333;Integrated Security=SSPI",
"OtherDatabaseLocal": "Server=_;Database=_;MultipleActiveResultSets=true;Connection Timeout=333;Integrated Security=SSPI"
},
"Serilog": {
"MinimumLevel": "Debug",
"Override": {
"Microsoft": "Warning"
},
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.File"
],
"WriteTo": [
{
"Name": "RollingFile",
"Args": {
"path": "Logs/MyProject.txt"
}
}
]
}
}
and changed the startup.cs file like
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration.GetSection("Serilog"))
.CreateLogger();
loggerFactory.AddSerilog();
--
--
}
After this there is not file created. What could be the problem.
The following call is not quite working how you're expecting:
.ReadFrom.Configuration(Configuration.GetSection("Serilog"))
When using this extension method, Serilog expects that there will be a section named Serilog within the configuration that you pass through. In your example, you're passing the Serilog section itself, but then it's looking for another Serilog section within that.
Update the call to just pass through Configuration, like this:
.ReadFrom.Configuration(Configuration)
Serilog will find your Serilog section and consume the settings from that.

Trouble converting Serilog Configuration code line to json configuration

I found a post where the blogger explained how to filter by LogEvent level to a separate file for Serilog configuration. I am doing all my Serilog configuration in my appsettings.json. How would this look in json configuration, I can't seem to figure how to json the lambda expression....
Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning).WriteTo.RollingFile(#"Logs\Warning-{Date}.log"))
I am using configuration for Serilog from my appsettings.json and am trying to convert this
.WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning).WriteTo.RollingFile(#"Logs\ApplicationName\Serilog\Warning-{Date}.log"))
to json, to include in my Serilog section of my appsettings file
EDIT:
appsettings partial shown here
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] ({SourceContext}) {Message}{NewLine}{Exception}"
}
},
{
"Name": "Seq",
"Args": { "serverUrl": "http://localhost:5341" }
},
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "RollingFile",
"Args": { "pathFormat": "C:/Logs/Serilog/%APPLICATION_NAME%/log-{Date}.log" }
}
]
}
}
],
"SubLogger": {
"Level": "Warnings",
"pathFormat": "C:/Logs/Serilog/%APPLICATION_NAME%/Warnings/log-{Date}.log"
},
The sub logger pathFormat is not producing the same folder naming as the RollingFile pathFormat
For this moment Serilog does not support configuration of sub-loggers through JSON appsettings. See this issue on github.
It's not an easy task actually because you pass Func<LogEvent, bool> to ByIncludingOnly() filter. Mapping configuration data from json file to c# code is not a trivial task.
However if you are just interested in creation of sub-logger for specific log level, you could combine configuration from JSON config with ByIncludingOnly() filter.
Define a POCO that will hold filter configuration:
public class SubLoggerConfiguration
{
public LogEventLevel Level { get; set; }
private string pathFormat;
public string PathFormat
{
get => pathFormat;
set => pathFormat = value.Replace("%APPLICATION_NAME%", Environment.GetEnvironmentVariable("APPLICATION_NAME"));
}
}
Add SubLogger section to your JSON config:
{
"Serilog": {
"Using": [
"Serilog.Sinks.RollingFile"
],
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "RollingFile",
"Args": { "pathFormat": "c:\\Logs\\log-{Date}.log" }
}
],
"SubLogger": {
"Level": "Warning",
"pathFormat": "Logs\\ApplicationName\\Serilog\\Warning-{Date}.log"
}
}
}
It's a good idea to keep it inside native Serilog section, it will not break configuration of Serilog itself.
Then load SubLogger configuration from config file:
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("AppSettings.json");
IConfiguration configuration = configurationBuilder.Build();
SubLoggerConfiguration subLoggerConfiguration = new SubLoggerConfiguration();
configuration.GetSection("Serilog:SubLogger").Bind(subLoggerConfiguration);
Note that you have to install Microsoft.Extensions.Configuration.Binder NuGet package for binding configuration to a POCO.
Now subLoggerConfiguration will contain desired log level and path format for the log. You can use this settings to call ByIncludingOnly() filter:
Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Logger(l => l.Filter.ByIncludingOnly(e => e.Level == subLoggerConfiguration.Level).WriteTo.RollingFile(subLoggerConfiguration.PathFormat));

Serilog not getting application events, only coded logger events

I set up my application to use Serilog as the logging mechanism. And I do in fact get log files and can view them on Seq. Initially I was getting the application events logged, but for some reason I am no longer getting them. See the images below.
In the first image I am getting application events. Later, when doing the same testing operations, I am NOT getting the application events any more, only the coded events in the files (i.e. _logger.LogWarning("Warning");)
I set up Serilog in Main method.
public static int Main(string[] args)
{
var currentEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{currentEnv}.json", optional: true)
.AddEnvironmentVariables()
.Build();
//Configure logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
Log.Information("Logger created");
try
{
Log.Information("Starting web host");
BuildWebHost(args).Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Web Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
My Serilog settings
"Serilog": {
"Using": [
"Serilog.Sinks.RollingFile",
"Serilog.Sinks.Async",
"Serilog.Sinks.ApplicationInsights",
"Serilog.Sinks.Console",
"Serilog.Sinks.Seq"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "RollingFile",
"Args": { "pathFormat": "C:/Logs/Serilog/log-{Date}.log" }
},
{
"Name": "Seq",
"Args": { "serverUrl": "http://localhost:5341" }
}
]
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "WebTemplate"
}
}
Before I move on to making Serilog more functional, I would like to sort out why I am not getting application events any longer.
I expect this is down to the following section in your Serilog settings:
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning"
}
}
Nicholas Blumhardt's blog post goes into detail about how the Override setting works:
The effect of the configuration above, then, is to generate events only at or above the Warning level when the logger is owned by a type in a Microsoft.* namespace.
This quote seems to apply directly to your situation, whereby you are asking Serilog to filter out any events that belong to the Microsoft.* namespace and are lower than a severity of Warning.
The messages you show in your first screenshot are coming from either MVC or Entity Framework, which live in the Microsoft.* namespace and will be logging at a lower severity than warning.
It looks like you are missing UseSerilog() in BuildWebHost():
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseSerilog() // <- Add this line
.UseStartup<Startup>()
.Build();
This assumes you have the Serilog.AspNetCore NuGet package installed.

Categories