Is there a way to move the MinimumLevel overrides to appsettings.json?
Sometimes I don't want to display the system logs, so I do
.MinimumLevel.Override("System", LogEventLevel.Error)
.MinimumLevel.Override("Microsoft", LogEventLevel.Error)
and sometimes I want to keep an eye on the requests for debugging purposes:
.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Debug)
.MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", LogEventLevel.Debug)
So is there a better way to do that in appsettings.json?
public static class LoggingExtensions
{
public static IHostBuilder AddLogging(this IHostBuilder builder, LogEventLevel minLevelLocal = LogEventLevel.Information)
{
const string outputTemplate = "[{Timestamp:HH:mm:ss.fff} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}";
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Is(minLevelLocal)
.MinimumLevel.Override("System", LogEventLevel.Error)
.MinimumLevel.Override("Microsoft", LogEventLevel.Error)
// .MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Debug)
// .MinimumLevel.Override("Microsoft.AspNetCore.Routing.EndpointMiddleware", LogEventLevel.Debug)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithProcessName()
.Enrich.WithMemoryUsage()
.WriteTo.Console(outputTemplate: outputTemplate)
.CreateLogger();
return builder.UseSerilog();
}
}
The documentation for the Serilog.Settings.Configuration library has explicit support for this.
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
}
And to tell it to read the config from there:
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
Pass an IConfiguration to your extension and extract the value that way.
public static void AddLogging(IConfiguration configuration)
{
var minLevelString = configuration.GetValue<string>("Logger:MinLevel");
var minLevel = Enum.Parse<LogEventLevel>(minLevelString);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Is(minLevel).CreateLogger();
}
Related
I'm trying to use serligo as my main logging utilitie but I can't seem to get it to work correctly.
In the Program.cs file, I configure the logger and also log a startup message, this message gets outputted to the console.
I also have, in one of my controllers, an ILogger<HomeController> instance that logs information when a certain action is called. These logs are not outputted to the console, and I cannot understand why.
Startup.cs
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("Starting up version {version}", Defaults.Version); // WORKS
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application start-up failed");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Controller
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Home/Index"); //Does not work
return View();
}
}
appsettings.json
"Serilog": {
"Using": [ "Serilog.Sinks.Console" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"System": "Error"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"formatter": "Serilog.Formatting.Compact.RenderedCompactJsonFormatter, Serilog.Formatting.Compact"
}
}
]
}
}
]
},
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration))
The issue relates the above code, in this part of code, you should also configure it to write to the console.
Modify the code as below:
.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration).WriteTo.Console())
Then, the output like this:
Using ILogger interface I am unable to see any output from LogDebug even when I have set:
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
And I use to register:
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logging.log")
.CreateLogger();
var host = CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.Configure<HostOptions>(option =>
{
option.ShutdownTimeout = System.TimeSpan.FromSeconds(20);
});
services.AddLogging();
// services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddSimpleInjector(container, options =>
{
// Hooks hosted services into the Generic Host pipeline while resolving them through Simple Injector
options.AddHostedService<Worker>();
// Allows injection of ILogger & IStringLocalizer dependencies into application components
options.AddLogging();
// options.AddLocalization();
});
})
.Build()
.UseSimpleInjector(container);
config.Build();
host.Run();
}
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConsole();
if (hostingContext.HostingEnvironment.IsDevelopment() == true)
logging.AddDebug();
})
.ConfigureHostConfiguration(configurationBuilder =>
{
configurationBuilder.AddCommandLine(args);
})
.ConfigureAppConfiguration((hostingContext, configApp) =>
{
var env = hostingContext.HostingEnvironment;
})
.UseConsoleLifetime();
return builder;
}
Any ideas what i am doing wrong please or missing?
I am now planning to use Serilog for all my logging and for legacy libs to use the ILogger interface, anything that i would need to change to enable (or remove any conflicting settings)?
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.
I am making .Net core application and use Serilog for logging. Now I want to use Serilog to write a log for each clients who already logged in.
I expected the application gonna have seperate logger
Global logger: write server log
Client loggers: each client gonna have their own logger to write log file.
From what I tried to do the application do generate the txt file. However, it doesn't write anything to it.
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.UseIISIntegration()
.UseSerilog((hostingcontext, loggerConfiguration) => loggerConfiguration.ReadFrom.Configuration(hostingcontext.Configuration))
.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<LoggerManager>();
}
...MORE CODE...
}
LoggerManager.cs
public class LoggerManager
{
public static Dictionary<string, ILogger> loggerDict = new Dictionary<string, ILogger>();
public void CreateUserLogger(string username, EnumType userType)
{
var logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Async(a => a.File(#$"Log/{userType}/{username}.txt",
buffered:true,
rollingInterval:RollingInterval.Day,
retainedFileCountLimit:90))
.CreateLogger();
loggerDict.TryAdd(cheID, logger);
}
public void WriteInfoLog (string username, string message)
{
loggerDict.TryGetValue(username, out ILogger logger);
logger.Information(message);
}
}
HomeController.cs
class HomeController {
private readonly LoggerManager _loggerManager;
public HomeController(LoggerManager loggerManager)
{
_loggerManager = loggerManager;
}
public IActionResult Index() {
_loggerManager.WriteInfoLog(authenticatedUser.CHEID, "HELLO HOW ARE U TODAY");
return View();
}
}
appsettings.json
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Warning",
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"path": "Logs/Server/serverlog.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"buffered": true
}
}
]
}
}
]
}
I randomly found the "sinks-map". This sinks definitely satisfy my requirement.
https://github.com/serilog/serilog-sinks-map
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.