72

I'm logging just fine using dependency injection on my controllers, now I need to log something from a static class.

How can I log from a static class?

I can't use dependency injection because it's static and I can't just pass in an existing logger object to the static class because it would have the wrong name of class in the log file.

In other words how can I get a logger from the loggerfactory inside my static class?

I came across a similar question and they pointed to an article on a loggerfactory in a static class but that doesn't actually work as I can't get a new logger from it because it won't accept a static class as the parameter:

"Static types cannot be used as type arguments"

3
  • 2
    Did you ever figure this out? I want to log from an extension method. Commented Jun 1, 2018 at 9:03
  • Solution here @illug Commented Jun 1, 2018 at 15:01
  • related: stackoverflow.com/q/50712720/11635 Commented Jun 23, 2018 at 8:39

7 Answers 7

97

Solution is to have a static reference to the LoggerFactory in a utility static class initialized on startup:

/// <summary>
/// Shared logger
/// </summary>
internal static class ApplicationLogging
{
    internal static ILoggerFactory LoggerFactory { get; set; }// = new LoggerFactory();
    internal static ILogger CreateLogger<T>() => LoggerFactory.CreateLogger<T>();        
    internal static ILogger CreateLogger(string categoryName) => LoggerFactory.CreateLogger(categoryName);
}

Which you initialize on Startup.cs:

public Startup(ILogger<Startup> logger, ILoggerFactory logFactory, IHostingEnvironment hostingEnvironment)
{
    _log = logger;
    _hostingEnvironment = hostingEnvironment;
    Util.ApplicationLogging.LoggerFactory = logFactory;//<===HERE
}

Then you can build a logger to use from your static class like so:

internal static class CoreJobSweeper
{
    private static ILogger log = Util.ApplicationLogging.CreateLogger("CoreJobSweeper");
}
Sign up to request clarification or add additional context in comments.

7 Comments

Perfect! However just worth adding an example of a strongly-typed CreateLogger, especially for us copy/paste junkies just looking for a quick fix to a problem :) i.e. private static ILogger log = Util.ApplicationLogging.CreateLogger<CoreJobSweeper>();
This no longer works as if the announcement in github.com/aspnet/Announcements/issues/353. Injecting ILogger and ILoggerFactory does not work. "The new ApplicationInsightsLoggerProvider can capture logs from early in the application-startup pipeline. Although ApplicationInsightsLoggerProvider is automatically enabled in Application Insights (starting with version 2.7.1), it doesn't have an instrumentation key set up until later in the pipeline. So, only logs from Controller/other classes will be captured..."
Solution is to move ILogger<Startup> logger, ILoggerFactory logFactory to Configure()
But what if static class is in project/dll with no ref to server project with startup ?
Hello... how to do this in .NET Core 6 where there is no Startup class?
|
21

I found this question with answers useful, but I remembered I already have installed Serilog. Instead of implementing a custom static instance, I could use the default implementation from Serilog.

It comes with multiple nice logging features with multiple overloaded version.
Here are two examples:

using Serilog;
...
    Log.Error(myException, "my message");
    Log.Warning("My message", myValue);

Serilog.Log is a static instance that is ready when configured in Program.cs or Startup.cs.

5 Comments

This works if you don´t need to use the separation of concerns principle where you could replace the logging framework easily if needed.
True, and if one would like to use Serilog in a custom logger, one can let the custom logger basically be a wrapper around Serilog :) To get a binding to the calling class, as OP asked for, one can implement something like the factorymethod with type parameter as @Adrian described.
You are coding to a concrete, not coding to an interface. Future readers, just "be aware" of this situation.
@Banshee - This works if you don't want to rely on Abstraction where you could replace the logging framework easily if needed.
As clarification to my last comment: Separation of Concerns is an observation of the different Vertical Concerns of an application: Design, UI Logic, API, BLL, DLL, Persistence. Cross-Cutting Concerns is when you share logic Horizontally at a particular layer. ie: Auth (or Logging, Caching, etc) is a CCC across API1, API2, API3, etc. Lastly, Bounded Context is observing ownership (Low Coupling, High Cohesion) of models between Horizontally-Split applications or MicroServices.
9

You can use the static LoggerFactory instance with the following extension method which accepts a regular parameter of type Type, rather than a generic type parameter: CreateLogger(ILoggerFactory, Type)

i.e. loggerFactory.CreateLogger(typeof(T)) rather than loggerFactory.CreateLogger<T>()

1 Comment

Did you mean "a static LoggerFactory instance"? There is no static LoggerFactory instance available unless you set one up.
2

Here is the combined answer of @JohnC & @Pekaaw,

LoggingFactory can be created from SeriLog logger to retain same the log configuration and it can be used for logging mechanism

(Microsoft.Extensions.Logging.LoggerFactory)Serilog.SerilogLoggerFactoryExtensions.AddSerilog(new Microsoft.Extensions.Logging.LoggerFactory(), Serilog.Log.Logger);

using Microsoft.Extensions.Logging;

/// <summary>
/// Shared logger
/// </summary>
internal static class ApplicationLogging
{
    internal static ILoggerFactory LoggerFactory { get; set; } = 
        (LoggerFactory)Serilog.SerilogLoggerFactoryExtensions.AddSerilog
        (
            new LoggerFactory(),
            Serilog.Log.Logger
        );

    internal static ILogger CreateLogger<T>() => 
        LoggerFactory.CreateLogger<T>();

    internal static ILogger CreateLogger (string categoryName) => 
        LoggerFactory.CreateLogger(categoryName);
}
public static class YourClass
{
   public static readonly ILogger _logger = 
        ApplicationLogging.CreateLogger("YourClass");

   public static void MyMethod(string param)
   {
      try
      {
         _logger.LogInformation($"Inside MyMethod { param }");
         //Todo: my static code;
      }
   }
}

Comments

1

I've added a static member of ILoggerFactory in Startup class so it can be used in other classes.

public class Startup
{
    internal static ILoggerFactory LogFactory { get; set; }
    …

    public void Configure(…, ILoggerFactory logFactory)
    {
        …
        LogFactory = logFactory;
    }
}

Then use it in the static class.

static class YourClass
{
    private static ILogger<YourClass> Logger = Startup.LogFactory.CreateLogger<YourClass>();
…

However, this code will result in a TypeInitializationException when the CreateLogger throws an exception, so you should try to do better. [1]

Comments

1

For .net core 7/.net7, I have prepared static logger class which has a initialization method to initialize an instance of static ILogger object from ILoggerFactory

public static class LogHelperService{

    private static ILoggerFactory _loggerFactory;
    private static ILogger _logger;
    private static IHttpContextAccessor _httpContextAccessor;

    public static void Initialize(ILoggerFactory loggerFactory,IHttpContextAccessor httpContextAccessor)
    {
        _loggerFactory = loggerFactory;
        _logger = _loggerFactory.CreateLogger("LogHelperService");
        _httpContextAccessor = httpContextAccessor;
    }

    
    public static void LogError( Exception exception, string message, params object[] args)
    {
        _logger.Log(LogLevel.Error, exception, message, args);
    }
    public static void LogError( string message, params object[] args)
    {
        _logger.Log(LogLevel.Error, message, args);
    }
    public static void LogInformation(string message, params object[] args)
    {
        _logger.Log(LogLevel.Information, message, args);
    }
    public static void LogInformation(Exception exception, string message, params object[] args)
    {
        _logger.Log(LogLevel.Information, exception, message, args);
    }
}

Then In Program.js, After the app object created from WebApplicationBuilder, I have called the Initialization function to create the static Logger

var app = builder.Build();

//initialize Static service with server resources
LogHelperService.Initialize( app.Services.GetService<ILoggerFactory>() ,app.Services.GetService<IHttpContextAccessor>());

Comments

0

First of all, I agree with NightOwl888 answer.

But as an option (consider this as a workaround) you could inject ILogger dependency via method parameter => so a class that will call this static method should be responsible for providing a correct implementation of ILogger.

static class YourClass
{
    public static void DoSomething(ILogger logger)
    {
         // do something
         // call log.Log();
    }
}

2 Comments

Nightowl didn't answer the question at all and instead delivered an unwanted lecture, I specifically covered what you suggest and why I can't do it in my question.
To be able to log Everywhere, you have to force a required logger dependency on every method/object in your tens of thousands of lines of code. That sounds like a terrible idea to me. A static factory is the saner option in this case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.