Date and time in console log - asp.net-core

Is their a way to mention the date and time in the console log of an asp.net core 2.0 project for production and development environment?
I have following in my startup:
services.AddLogging(builder =>
{
builder.AddConfiguration(Configuration.GetSection("Logging"+Environment.EnvironmentName))
.AddConsole()
.AddDebug();
});
Appsettings.json:
"LoggingDevelopment": {
"IncludeScopes": false,
"Console": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
},
"LoggingProduction": {
"IncludeScopes": false,
"Console": {
"LogLevel": {
"Default": "Error",
"System": "Error",
"Microsoft": "Error"
}
}
},
Current [development] log layout (without a date or time of the log line):
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action a.Controller (project) in 531.2457ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 532.5812ms 200 text/html; charset=utf-8
A date and time would be extremly handy in production mode for error lines.

Default Console logger is pretty limited. Of course there is always the possibility to use lambda formatter like said in Github issue provided by serpent5. But it's accessible only if using raw ILogger.Log() method.
It's not available for ILogger extensions like LogInformation or LogError. This option is also not available if you don't control logging call. It's actually your case when logging is done by ASP.NET Core internal classes.
So you need some more flexible implementation of logger for .Net Core. I suggest using of Serilog. It's pretty simple but very flexible and powerful at the same time.
To integrate Serilog into ASP.Net Core application do the following:
Install following NuGet packages:
Serilog.AspNetCore
Serilog.Sinks.Console
Besides Console, Serilog supports many other targets like Rolling File, E-mail, SQL Server, etc. See this list of other Serilog targets.
Configure Serilog on application startup:
Here is a sample:
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}")
.CreateLogger();
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
Provided output template adds date and time to logged message:

To expand on CodeFuller's response, here's how you can modify the Serilog Console output formatter purely from configuration:
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"System": "Information",
"Microsoft": "Information"
}
},
"WriteTo:Sublogger": {
"Name": "Logger",
"Args": {
"configureLogger": {
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{TraceId}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
}
}
}
If you take this approach, remove the WriteTo.Console logger method from your startup configuration. This line:
.WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}")
I've also modified the output format template slightly. Either template will work fine.

Related

How to make Serilog pick up both the expression template and a console (color) theme from appsettings.json

I'm trying to configure Serilog for my ASP.NET app using appsettings.json and am following the instructions from the GitHub Serilog.Settings.Configuration project readme. I cannot get the Console output format expression working from appsettings.json when I also set a theme. When both are configured directly in code it works just fine.
This is the Serilog section of appsettings.json. Note the full template is more complex, it uses Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1) amonst others, but this is how I am testing for now:
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": {
"type": "Serilog.Templates.ExpressionTemplate, Serilog.Expressions",
"template": "[{#t:HH:mm:ss:fff} {#l:u3}] {SourceContext,48} : {#m}\n{#x}"
},
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console"
}
}
],
"Enrich": [ "FromLogContext" ]
}
}
And this is the resulting output (with theme colors):
[17:13:38 INF] Configure
[17:13:38 INF] Now listening on: https://localhost:5001
[17:13:38 INF] Now listening on: http://localhost:5000
[17:13:38 INF] Application started. Press Ctrl+C to shut down.
Which appears to be the default format (note the msec field in the timestamp and the Source Context field are missing).
When I omit the theme setting from the json, the output looks like this, i.e. with the correct expression template applied (e.g. source context) but without any color:
[17:13:37:498 INF] App.Web.Server.Program : Server started
[17:13:37:797 INF] App.Web.Server.Startup : ConfigureServices
This is the same format I get when I configure the logger in code, but then I get the theme color too!
I also tried to use this format from the project's samples:
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] {Message}{NewLine}{Exception}",
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"
}
}
]
This is without the format expression, just the format (and a slight variation on the format itself) and, as expected, results in:
[14:35:11 Microsoft.Hosting.Lifetime [Information] Now listening on: "https://localhost:5001"
[14:35:11 Microsoft.Hosting.Lifetime [Information] Now listening on: "http://localhost:5000"
I tried umpteen variations and combinations without any success. So it seems that using appsettings.json I can either get the expression template working, or the theme working, but not both at the same time.
Again: when configured directly in code it "just works" ...
Suggestion are welcome.
I tried as below:
Packages:
in appsettings.json:
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "Time:{Timestamp: HH:mm:ss.fff} Level:{Level} DetailedInfo:{Message}{NewLine}{Exception}"
}
}
],
"Enrich": ["FromLogContext"]
}
In program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog((context, logger) =>
{
logger.ReadFrom.Configuration(context.Configuration);
});
Result:
and with
"theme": "Serilog.Sinks.SystemConsole.Themes.SystemConsoleTheme::Grayscale, Serilog.Sinks.Console"

Serilog 3.4 and ASPNET core 2.1 stops logging after 3 lines when initialized through configuration

I am integrating Serilog into an existing aspnet core 2.1 WebHost app. My single dependency is Serilog.AspNetCore Version 3.4.0.
When I initialize the logger using configuration in WebHost builder, I get 3 lines in the log file and no other logging. When I initialize the logger completely in code in main, logging works as expected.
Serilog initialization through configuration (only 3 lines output):
appsettings.json:
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Using": [ "Serilog.Sinks.File" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"outputTemplate": "[{Level}] {Timestamp:yyyy-MM-ddTHH:mm:sszzz} - {Message:lj}{NewLine}{Exception}",
"path": "/var/log/somedir/somefile.log"
}
}
]
}
The builder code:
WebHost.CreateDefaultBuilder(args)
.UseSerilog((hostingContext, loggerConfiguration) =>
loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration))
Serilog initialization in main (logging works as expected):
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.Enrich.FromLogContext()
.WriteTo.File("/var/log/somedir/somefile.log", outputTemplate:"[{Level}] {Timestamp:yyyy-MM-ddTHH:mm:sszzz} - {Message:lj}{NewLine}{Exception}")
.CreateLogger();
try
{
Log.Information("Some service is starting");
CreateWebHostBuilder(args).Build().Run();
return 0;
}
catch(Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
Code in the builder:
WebHost.CreateDefaultBuilder(args)
.UseSerilog()
Note that I'm pretty sure configuration is being read or the output would not be in the file specified in configuration.
Could the Microsoft logging stuff be smashing the Serilog stuff?
Can anyone spot what I'm doing wrong?

Only write logs for custom logging

I am following this article:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-3.1
I am trying to only log my own custom logging in a asp.net core 3.1 API. And not all logs generated from asp.net core. I have created a blank weather forecast service:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogTrace("LogTrace");
_logger.LogDebug("LogDebug");
_logger.LogInformation("LogInformation");
_logger.LogError("LogError");
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Trace",
"TestAPI": "Trace"
},
"ApplicationInsights": {
"InstrumentationKey": "xx-xx-x-x-xx",
"LogLevel": {
"Default": "Trace",
"Microsoft": "Trace",
"TestAPI": "Trace"
}
}
},
"AllowedHosts": "*"
}
I have following nuget installed both version 2.14.0:
Microsoft.Extensions.Logging.ApplicationInsights
Microsoft.ApplicationInsights.AspNetCore
Now I try to run the app, but gets no logs.
I try adding services.AddApplicationInsightsTelemetry(); to startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationInsightsTelemetry();
services.AddControllers();
}
Same no logs.
First, please note that the ApplicationInsights key means two very different things depending on where it's located in the JSON file.
If the key is on the JSON root level (ie. what you call "outside"), it's used to configure Application Insights, and it's where you specify your instrumentation key. It looks like this:
{
"ApplicationInsights": {
"Instrumentationkey":"xxx-36a5-4687-b1fc-xxxxxx"
}
}
Second, if it's located inside the Logging section, it's used to configure the ApplicationInsightsLoggerProvider, which determines which log level is sent to Application Insights. That's the ILogger log filtering mechanism.
By default, only log levels warning or higher are sent to app insights. If you only want to send all your logs to application insights, you can either configure it for your namespaces, or ignore the messages coming from the System and Microsoft namespaces:
{
"Logging": {
"ApplicationInsights": {
"LogLevel": {
"Default": "Trace"
"System": "None",
"Microsoft": "None"
}
}
}
First of all, there is no need to put those provider settings outside "Logging" (you shouldn't).
Every logging setting you want should be put inside only ( unless there is a provider configured to specifically read that)
Now to answer your question, let's say that your application's root namespace is MyNetCore. ( it would be similar to the name of the project).
If you are using Visual Studio, You can view your Project's root namespace from Project Properties -> Application -> Default Namespace
To view logs from your application only, you have to set the default logging level to None and logging level of your project MyNetCore to Trace
[Edit: You have set the logging levels for ApplicationInsights (or any other provider) separately. The default one is for kestrel.]
"Logging": {
"LogLevel": {
"Default": "None",
"MyNetCore": "Trace"
},
"ApplicationInsights": {
"InstrumentationKey": "xxx-36a5-4687-b1fc-xxxxxx",
"LogLevel": {
"Default": "None",
"MyNetCore": "Trace"
}
}
}
The Logging level set here is the minimum level. If you set it to Trace (0), all the log levels (greater than 0) will be shown. i.e. From Information to Critical
If you set it to None (6), no logs will be shown
Reference to different Log Levels : https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loglevel
If you want to view only errors from asp.net core, and every log level from your application, then you can do this.
"Logging": {
"LogLevel": {
"Default": "None",
"Microsoft": "Error",
"MyNetCore": "Trace"
},
"ApplicationInsights": {
"InstrumentationKey": "xxx-36a5-4687-b1fc-xxxxxx",
"LogLevel": {
"Default": "None",
"Microsoft": "Error",
"MyNetCore": "Trace"
}
}
}
Edit: To read the above ApplicationInsights configuration, you need to have Microsoft.Extensions.Logging.ApplicationInsights nuget package installed. Otherwise the config will be totally ignored.

Log information is displayed though "LogLevel" Property is commented

I am pretty new to .Net Core. And I'm working on basic logging of information by the default ILogger provider.
Initially my AppSetting.json was uncommented and able to see the logs that I've written.
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"AllowedHosts": "*"
}
Later, I commented theLogLevel properties and ran the application on Krestel server. Then I can still see the logged information in the console.
AppSettings.json
{
"Logging": {
//"LogLevel": {
// "Default": "Debug",
// "System": "Information",
// "Microsoft": "Information"
//}
},
"AllowedHosts": "*"
}
Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging)
=>
{
logging.ClearProviders();
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.UseStartup<Startup>();
Sample logging method in HomeController.cs
public void LogExceptionToConsole()
{
_logger.LogError("This is raised by logger information");
}
Console
Why is this behavior? If this the behavior why it has been designed like this?
Could anyone explain me.
Thanks in advance
ASP.NET Core defines the following log levels, ordered here from lowest to highest severity:
Trace = 0 (Disabled by default)
Debug = 1
Information = 2
Warning = 3
Error = 4
Critical = 5
When no LogLevel is set in the application (either in startup.cs or program.cs or appsettings.json or appsettings.development.json) for a LogCategory, then Minimum LogLevel is set as Information by default.
Hence, in your example, the Error is logged since it is higher than the default Minimum LogLevel.
You can refer to this link for more details

Is force logging available in Microsoft.Extensions.Logging

I have ASP.NET Core application. I am using Microsoft.Extensions.Logging.ILogger interface with Serilog logging provider.
In production environment, the log level is set to Error. So anything below Error loglevel will not get logged.
Is there Force logging available in Microsoft.Extensions.Logging, which would log the message regardless of what the loglevel is set?
Basically I want to log some information (temporarily) without setting LogLevel to Information because that would log all other unrelevant information messages.
Update 1
So i updated my appsettings.json as per the suggestion however that did not work. Note that serilog also has its own loglevel which i have commented out. But Still not working.
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Error",
"MyUserService": "Information"
}
},
"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer" ],
//"MinimumLevel": "Error",
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
//"restrictedToMinimumLevel": "Error",
"connectionString": "Data Source=.\\SQLExpress;Initial Catalog=MyDataBase;Integrated Security=True;MultipleActiveResultSets=True",
"tableName": "Logs"
}
}
],
"Properties": {
"Application": "MyApplication"
}
}
and then in MyUserService
public class MyUserService : BaseService, IMyUserService
{
private readonly ILogger<MyUserService> _logger;
public MyUserService(MyDBContext dbContext, ILogger<MyUserService> logger)
: base(dbContext)
{
_logger = logger;
}
public async Task DoSomethig()
{
_logger.LogInformation("Log something meaningful here");
}
}
and in program.cs
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseApplicationInsights()
.UseUrls("http://*:40006")
.ConfigureAppConfiguration((hostingContext, config) =>
{
//removed for brevity
})
.ConfigureLogging((hostingContext, logging) =>
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(hostingContext.Configuration)
.CreateLogger();
logging.AddSerilog();
})
.Build();
The idea is to configure the log level per log cagtegory.
Once you let yourself inject an ILogger<MyCategory> (where MyCategory can be any type - a special type just to indicate log categories or just the class you log from), you can configure the log level independent of everything else:
"Logging": {
"LogLevel": {
"Default": "Error",
"MyCategory": "Information"
}
}