33

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"

JohnC
  • 3,095
  • 4
  • 34
  • 48

4 Answers4

52

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 intialize 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");
JohnC
  • 3,095
  • 4
  • 34
  • 48
  • 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(); – alv Apr 26 '19 at 17:52
  • 1
    This no longer works as if the announcement in https://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..." – Anders Apr 09 '20 at 06:44
  • Solution is to move ILogger logger, ILoggerFactory logFactory to Configure() – Anders Apr 09 '20 at 08:35
  • Suggestion: change the return Type to ILogger : internal static ILogger CreateLogger() => LoggerFactory.CreateLogger(); Usage: private static ILogger log = Util.ApplicationLogging.CreateLogger(); Otherwise explicit cast is mandantory – Dominik Sand Apr 22 '20 at 06:26
  • But what if static class is in project/dll with no ref to server project with startup ? – user1785960 Jul 03 '20 at 11:44
7

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.

pekaaw
  • 937
  • 10
  • 12
  • This works if you don´t need to use the separation of concerns principle where you could replace the logging framework easily if needed. – Banshee Feb 23 '21 at 15:47
  • 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](https://stackoverflow.com/a/54234887/3032543) described. – pekaaw Feb 24 '21 at 10:28
3

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>()

Adrian
  • 47
  • 1
  • 1
  • 2
    Did you mean "**_a_** static `LoggerFactory` instance"? There is no static `LoggerFactory` instance available unless you set one up. – Daniel May 05 '20 at 17:03
1

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();
    }
}
Set
  • 40,147
  • 18
  • 114
  • 133
  • 8
    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. – JohnC Feb 08 '18 at 16:05
  • 3
    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. – Cesar Oct 08 '19 at 18:35