Call AddFileLogger() on the logging builder to add a customized file logger provider.
using Juice.Extensions.Logging;
...
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddFileLogger(builder.Configuration.GetSection("Logging:File"));
Logging:File configuration section may be present:
// appsettings.json
{
"Logging": {
"File": {
"Directory": "C:\\Workspace\\Services\\logs",
"BufferTime": "00:00:03",
//"ForkJobLog": false
}
}
}
To separate logs folder, we will init new log scope with specified properties:
You may want to store _logScope to dispose it later.
// {Logging:File:Directory}/{ServiceDescription}/{yyyy_MM_dd-HHmm}.log
_logScope = _logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("ServiceId", Id),
new KeyValuePair<string, object>("ServiceDescription", Description)
});
To separate log file for job, we will init new log scope with specified properties:
// {Logging:File:Directory}/{ServiceDescription}/{TraceId} - {Operation}.log
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TraceId", TraceId),
new KeyValuePair<string, object>("Operation", Operation)
})){
// job processing
_logger.LogInformation("Begin invoke");
var state = await InvokeAsync();
// {Logging:File:Directory}/{ServiceDescription}/{TraceId} - {Operation}_{OperationState}.log
// {Logging:File:Directory}/{ServiceDescription}/{TraceId} - {Operation}_{OperationState} ({increased number}).log
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("OperationState", state)
})){
// job is completed with state
_logger.LogInformation("Invoke result: {0}", state);
}
}
Any log scopes specified by the string will be written to the log file
using (logger.BeginScope("Scope 1")){
using(logger.BeginScope("Scope 1.1")){
logger.LogInformation("Log inside scope");
}
}
logger.LogInformation("Log outside scope");
//---- Begin: Scope 1
//-------- Begin: Scope 1.1
//
//{Timestamp} {LogLevel}: Log inside scope
//
//-------- End: Scope 1.1
//---- End: Scope 1
//{Timestamp} {LogLevel}: Log outside scope
The library can be accessed via Nuget:
Call AddSignalRLogger() on the logging builder to add a customized file logger provider.
using Juice.Extensions.Logging;
...
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddSignalRLogger(builder.Configuration.GetSection("Logging:SignalR"));
Logging:SignalR configuration section may be present:
// appsettings.json
{
"Logging": {
"File": {
"Directory": "C:\\Workspace\\Services\\logs",
"HubUrl": "https://localhost:57754/loghub"
}
}
}
Basically, the supported scopes are like the File logger provider above, but SignalR logger provider is support more stronger named scopes:
Any log scopes specified by the string will be sent to the log hub if it is supported.
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TraceId", jobId),
new KeyValuePair<string, object>("Operation", "Invoke recurring tasks")
}))
{
// will send scopes []
_logger.LogInformation("Starting...");
for (var i = 0; i < 10; i++)
{
if (_stopRequest!.IsCancellationRequested) { break; }
using (_logger.BeginScope($"Task {i}"))
{
// will send scopes ["Task {i}"]
for (var j = 0; j < 10; j++)
{
_logger.LogInformation("Processing {subtask}", j);
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("Contextual", "success")
}))
{
_logger.LogError("Subtask {task} completed", j);
}
}
using (_logger.BeginScope("Succeeded"))
{
// will send scopes ["Task {i}", "Succeeded"]
_logger.LogInformation("Task {task} succeeded", i);
}
}
}
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("OperationState", "Succeeded")
}))
{ // will send to the state method, not logging method
_logger.LogInformation("End");
}
}
The code block below is an example of signalR hub to handle logging
public class LogHub : Hub<ILogClient>
{
public async Task LoggingAsync(Guid serviceId, string? traceId, string category, string message, LogLevel level, string? contextual, object[] scopes)
{
await Clients.Others.LoggingAsync(serviceId, traceId, category, message, level, contextual, scopes);
}
public async Task StateAsync(Guid serviceId, string? traceId, string state, string message)
{
await Clients.Others.StateAsync(serviceId, traceId, state, message);
}
public async Task BeginScopeAsync(Guid serviceId, string? traceId, string category, object scope)
{
await Clients.Others.BeginScopeAsync(serviceId, traceId, category, scope);
}
public async Task EndScopeAsync(Guid serviceId, string? traceId, string category, object scope)
{
await Clients.Others.EndScopeAsync(serviceId, traceId, category, scope);
}
}
To custom the log scopes filter to send to SingalR client, you can implement the IScopesFilter and register it with DI
// CustomScopesFilter.cs
using Microsoft.Extensions.Options;
internal class CustomScopesFilter : IScopesFilter{
public bool IsIncluded(string scope)
{
// your filter
}
}
// Program.cs
using Juice.Extensions.Logging;
builder.Logging.AddSignalRLogger<CustomScopesFilter>(builder.Configuration.GetSection("Logging:SignalR"));
NOTE
The library can be accessed via Nuget:
Call AddDbLogger() and/or AddMetricsLogger() on the logging builder to add a customized file logger provider.
using Juice.Extensions.Logging;
...
var builder = WebApplication.CreateBuilder(args);
// require RootConfiguration to access the ConnectionStrings
builder.Logging.AddDbLogger(builder.Configuration.GetSection("Logging:Db"), builder.Configuration);
builder.Logging.AddMetricsLogger(builder.Configuration.GetSection("Logging:Metrics"), builder.Configuration);
Logging:Db configuration section may be present:
Logging:Metrics configuration section is as same as the Logging:Db but added:
// appsettings.json
{
"Logging": {
"Db": {
"Directory": "C:\\Workspace\\Services\\logs",
"DatabaseProvider": "PostgreSQL",
"LogLevel": {
"Default": "Warning"
}
},
"Metrics": {
"Directory": "C:\\Workspace\\Services\\logs",
"DatabaseProvider": "PostgreSQL",
"SampleRate": "00:00:30",
"LogLevel": {
"Default": "Information"
}
}
}
}
The library can be accessed via Nuget: