Call AddBgServiceFileLogger() on the logging builder to add a customized file logger provider.
using Juice.BgService.Extensions.Logging;
...
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddBgServiceFileLogger(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}/{JobId} - {JobDescription}.log
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("JobId", JobId),
new KeyValuePair<string, object>("JobDescription", JobDescription)
})){
// job processing
_logger.LogInformation("Begin invoke");
var state = await InvokeAsync();
// {Logging:File:Directory}/{ServiceDescription}/{JobId} - {JobDescription}_{JobState}.log
// {Logging:File:Directory}/{ServiceDescription}/{JobId} - {JobDescription}_{JobState} ({increased number}).log
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("JobState", 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 AddBgServiceFileLogger() on the logging builder to add a customized file logger provider.
using Juice.BgService.Extensions.Logging;
...
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddBgServiceSignalRLogger(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>("JobId", jobId),
new KeyValuePair<string, object>("JobDescription", "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>("JobState", "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
{
public async Task LoggingAsync(Guid serviceId, string? jobId, string message, LogLevel level, string? contextual, string[] scopes)
{
try
{
await Clients.Others.SendAsync("LoggingAsync", serviceId, jobId, message, level, contextual, scopes);
}
catch { }
}
public async Task StateAsync(Guid serviceId, string? jobId, string state, string message)
{
try
{
await Clients.Others.SendAsync("StateAsync", serviceId, jobId, state, message);
}
catch { }
}
}
NOTE