nreco / logging

Generic file logger for .NET Core (FileLoggerProvider) with minimal dependencies
MIT License
284 stars 56 forks source link

Logging through singleton #58

Closed Klexoid closed 11 months ago

Klexoid commented 11 months ago

Hello, firstly thanks for your work.

Is it possible to pass this code below to a singleton ? From:

builder.Services.AddLogging(loggingBuilder => {
    loggingBuilder.AddFile("app.log", append:true);
});

To:

builder.Services.AddSingleton(new ServiceA(configuration, logFactory.CreateLogger<ServiceA>())); 

I'm trying to migrate logging from EventLog to File. For now it keeps logging into EventLog obvly. I'm new to C# and I'm struggling with this.. My Service look like this:

public ServiceA(IConfiguration configuration, ILogger<ServiceA> logger){}
VitaliyMF commented 11 months ago

I'm not sure that I understand the question. MVC app logging providers should be added via loggingBuilder, you shouldn't add them directly as services. See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/

Klexoid commented 11 months ago

The issue is that I have a singleton checking a localmachine certificate is present (ServiceA) before app is running but I also need to log information inside this singleton.

I think I did somethings disgusting but it works like I want:

builder.Services.AddLogging(loggingBuilder => {
    loggingBuilder.AddFile("logs/app_{0:yyyy}-{0:MM}-{0:dd}.log", fileLoggerOpts => {
        fileLoggerOpts.Append = true;
        fileLoggerOpts.FileSizeLimitBytes = 104857600;
        fileLoggerOpts.MaxRollingFiles = 30;
        fileLoggerOpts.FormatLogFileName = fName => {
            return String.Format(fName, DateTime.UtcNow);
        };
        fileLoggerOpts.FormatLogEntry = (msg) =>
        {
            var sb = new System.Text.StringBuilder();
            StringWriter sw = new StringWriter(sb);
            var jsonWriter = new Newtonsoft.Json.JsonTextWriter(sw);
            jsonWriter.WriteStartObject();
            jsonWriter.WritePropertyName("TimeStamp");
            jsonWriter.WriteValue(DateTime.Now.ToString("o"));
            jsonWriter.WritePropertyName("LogLevel");
            jsonWriter.WriteValue(msg.LogLevel.ToString());
            jsonWriter.WritePropertyName("LogName");
            jsonWriter.WriteValue(msg.LogName);
            jsonWriter.WritePropertyName("EventId");
            jsonWriter.WriteValue(msg.EventId.Id);
            jsonWriter.WritePropertyName("Context");
            jsonWriter.WriteValue(msg.Message);
            jsonWriter.WritePropertyName("Exception");
            jsonWriter.WriteValue(msg.Exception?.ToString());
            jsonWriter.WriteEndObject();
            return sb.ToString();
        };
    });
});
var logFactory = LoggerFactory.Create(loggingBuilder => {
    loggingBuilder.AddFile("logs/app_{0:yyyy}-{0:MM}-{0:dd}.log", fileLoggerOpts => {
        fileLoggerOpts.Append = true;
        fileLoggerOpts.FileSizeLimitBytes = 104857600;
        fileLoggerOpts.MaxRollingFiles = 30;
        fileLoggerOpts.FormatLogFileName = fName => {
            return String.Format(fName, DateTime.UtcNow);
        };
        fileLoggerOpts.FormatLogEntry = (msg) =>
        {
            var sb = new System.Text.StringBuilder();
            StringWriter sw = new StringWriter(sb);
            var jsonWriter = new Newtonsoft.Json.JsonTextWriter(sw);
            jsonWriter.WriteStartObject();
            jsonWriter.WritePropertyName("TimeStamp");
            jsonWriter.WriteValue(DateTime.Now.ToString("o"));
            jsonWriter.WritePropertyName("LogLevel");
            jsonWriter.WriteValue(msg.LogLevel.ToString());
            jsonWriter.WritePropertyName("LogName");
            jsonWriter.WriteValue(msg.LogName);
            jsonWriter.WritePropertyName("EventId");
            jsonWriter.WriteValue(msg.EventId.Id);
            jsonWriter.WritePropertyName("Context");
            jsonWriter.WriteValue(msg.Message);
            jsonWriter.WritePropertyName("Exception");
            jsonWriter.WriteValue(msg.Exception?.ToString());
            jsonWriter.WriteEndObject();
            return sb.ToString();
        };
    });
});
ILogger logger = logFactory.CreateLogger<Program>();
builder.Services.AddSingleton(new ServiceA(configuration, logFactory.CreateLogger<ServiceA>())); 
...
var app = builder.Build();

But now I would like this logging configuration in my appsettings.json but not sure if it's possible.. EDIT : Do not work x) "File used by an other process"

VitaliyMF commented 11 months ago

I'm not sure that I understand the purpose of this:

builder.Services.AddSingleton(new ServiceA(configuration, logFactory.CreateLogger()));

If this is a singleton service of MVC Core app, it is unclear why you're trying to create a separate logFactory and then obtain an instance of ILogger<ServiceA> from it instead of resolving it from the DI service provider:

builder.services.AddSingleton<ServiceA>( (srvPrv) => {
    var logger= srvPrv.GetRequiredService<ILogger<ServiceA>>();
    return new ServiceA(configuration, logger);
});

EDIT : Do not work x) "File used by an other process"

This is because you have 2 file loggers that are configured for the same file. If you want to use 2 different file loggers they should write to different log files (or your code should resolve ILogger instances from the same logger factory to write all logs to the same file).

Hope this helps.

Klexoid commented 11 months ago

Ok, this is perfect ! \o/ Love u ! Thanks for helping me!

My last question, is it possible to put this logging configuration into appsettings.json ? Like for FormatLogEntry and FormatLogFileName (code above), I have no idea how to transcribe that..

Thanks again for your time.

VitaliyMF commented 11 months ago

My last question, is it possible to put this logging configuration into appsettings.json ? Like for FormatLogEntry and FormatLogFileName (code above), I have no idea how to transcribe that..

Standard options (like log file name, FileSizeLimitBytes, MaxRollingFiles etc) may be read from appsettings.json section via

var loggingSection = Configuration.GetSection("Logging");   // "File" sub-section is used by default
loggingBuilder.AddFile(loggingSection, fileLoggerOpts => {
});

in appsetting.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
    },
    "File": {"Path":"app.log"}
  }
}

FormatLogFileName and FormatLogEntry handlers are code snippets, they cannot be in appsettings.json directly; however, you can make them configurable by using app's configuration (somehow) inside these handlers.

Klexoid commented 11 months ago

Ok, thanks you very much!