azure-functions-host icon indicating copy to clipboard operation
azure-functions-host copied to clipboard

Use scopes to direct logs to correct file

Open brettsam opened this issue 5 years ago • 20 comments

Today the FileLogger (what you see in the portal when invoking functions) figures out the correct file to write to based on the Function.{FunctionName}.User standard category that we use. However, we should be able to look at the scope as well to see if there's a function name. If so, write there.

brettsam avatar Jul 18 '19 12:07 brettsam

Is there a temp work-around I could use to get the logs to show up in the log-streaming console window of the Azure function? https://github.com/Azure/azure-functions-host/issues/4425#issuecomment-512623724

GFoley83 avatar Jul 20 '19 03:07 GFoley83

@brettsam Can you confirm if there is any workaround?

pvmraghunandan avatar Jul 31 '19 12:07 pvmraghunandan

I just wonder how such bugs get to production at Microsoft? Didn't anyone try using logger with DI? Don't you have single unit-test checking that logs messages get to destinations using default setup? It's hard to estimate the amount of time wasted by the community to find those workarounds...

I had to inject ILoggerFactory instead of ILogger<> into constructors and initialize logger manually with name which starts with "Functions.": _log = logFactory.CreateLogger("Functions.Default");

SashaPshenychniy avatar Sep 17 '19 13:09 SashaPshenychniy

@SashaPshenychniy -- are you trying to log to a specific function file? If not, you're likely hitting https://github.com/Azure/azure-functions-host/issues/4345. There's a workaround listed there -- explicitly let your category through our filter.

For others trying to log to the streaming logs for a specific function. The current ways you can get your logs to show up there are:

  • Use the ILogger that we pass into the function for your logging
  • Construct a logger yourself from the ILoggerFactory with the category generated from calling Microsoft.Azure.WebJobs.Logging.LogCategories.CreateFunctionUserCategory("{FunctionName}");. All this really does is create the category Function.{FunctionName}.User (you can do this manually as well), which should then route correctly. Those logs will then route to the correct file to show up in streaming logs for {FunctionName}.

If that doesn't work (say you need a different specific category for this logger), that will have to wait until we can get this issue fixed.

brettsam avatar Sep 19 '19 15:09 brettsam

@brettsam Any update? Is this being actively developed? What's the release plan for this?

arek-avanade avatar Sep 28 '20 08:09 arek-avanade

This is really annoying bug/stupid feature. If you use injected services and you want to see logs in those services you have to pass Ilogger instance form function to every method. Please fix this.

Oxoproline avatar Nov 05 '20 10:11 Oxoproline

I am at serious loss of words on how this made into production and then ignored for over an year. Do Microsoft not use Azure Functions? I have services that are referenced in a Web API and also in a Functions App (for batch jobs), so none of the above mentioned workarounds are good for me. Can we please have an update on this?

SinghManvir avatar Dec 17 '20 13:12 SinghManvir

@SinghManvir Somebody can correct me, but custom dependency injection was not supported in earlier versions of azure functions. So having ILogger injected into the specific function was the correct way how to do logs in azure functions, but azure functions involved and unfortunately some parts of the code/functionality missed the train.

Oxoproline avatar Dec 17 '20 14:12 Oxoproline

@brettsam 's workaround works for me, but really I shouldn't be doing:

_logger = loggerFactory.CreateLogger(Microsoft.Azure.WebJobs.Logging.LogCategories.CreateFunctionUserCategory("MyFunctionClassName"));

This breaks the very concept of Dependency Injection. Is there a fix for this?

m-ghaoui avatar Dec 28 '20 09:12 m-ghaoui

How do you use this "workaround", if you have for example a helper class which is used in multiple functions? I noticed the CreateFunctionUserCategory only works for me if you specify the name of a certain Azure Function.

coreice avatar Apr 29 '21 20:04 coreice

I feel the current need to us ILoggerFactory and LogCategories.CreateFunctionUserCategory with DI rather than ILogger<TFunction> should be clearly documented at https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-class-library?tabs=v2%2Ccmd#logging. Googling and Microsoft Docs leads you down the path of configuring host.json log level settings, rather than this guidance on how better to inject a logger.

andybooth avatar May 02 '21 09:05 andybooth

Still no update?

Bit-Shifts avatar Dec 03 '21 17:12 Bit-Shifts

any update? any timeline for a fix?

spar avatar Jan 05 '22 02:01 spar

I am at serious loss of words on how this made into production and then ignored for over an year. Do Microsoft not use Azure Functions? I have services that are referenced in a Web API and also in a Functions App (for batch jobs), so none of the above mentioned workarounds are good for me. Can we please have an update on this?

Microsoft has become a marketing company of lately . It has ceased to be a tech company it seems.

KapilGBunnings avatar Jan 05 '22 04:01 KapilGBunnings

@KapilGBunnings I think Microsoft is working on a successor to Azure Functions:

  • https://techcommunity.microsoft.com/t5/apps-on-azure-blog/net-on-azure-functions-roadmap/ba-p/2197916
  • https://visualstudiomagazine.com/articles/2021/03/24/azure-functions-net5.aspx

If I'm not mistaken, the github repo is https://github.com/Azure/azure-functions-dotnet-worker but I have not looked at it yet.

Maybe it's been fixed there? I really don't know.

m-ghaoui avatar Jan 05 '22 10:01 m-ghaoui

Just add this in a code file to your project, and it will Just Work ™️ after adding the relevant usings:

[assembly: FunctionsStartup(typeof(LoggingStartup))]

public class LoggingStartup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        // Replace ILogger<T> with the one that works fine in all scenarios 
        var logger = builder.Services.FirstOrDefault(s => s.ServiceType == typeof(ILogger<>));
        if (logger != null)
            builder.Services.Remove(logger);

        builder.Services.Add(new ServiceDescriptor(typeof(ILogger<>), typeof(FunctionsLogger<>), ServiceLifetime.Transient));
    }

    class FunctionsLogger<T> : ILogger<T>
    {
        readonly ILogger logger;
        public FunctionsLogger(ILoggerFactory factory)
            // See https://github.com/Azure/azure-functions-host/issues/4689#issuecomment-533195224
            => logger = factory.CreateLogger(LogCategories.CreateFunctionUserCategory(typeof(T).FullName));
        public IDisposable BeginScope<TState>(TState state) => logger.BeginScope(state);
        public bool IsEnabled(LogLevel logLevel) => logger.IsEnabled(logLevel);
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
            => logger.Log(logLevel, eventId, state, exception, formatter);
    }
}

from https://www.cazzulino.com/azure-functions-logging.html

kzu avatar Mar 10 '22 20:03 kzu

This is still biting people 3 years later... @brettsam any updates?

IronSean avatar Jul 28 '22 19:07 IronSean

To articulate the reasons I'm unhappy with this:

  1. I cannot use basic ILogger DI for core classes that get called by an Azure Function as the logs will not appear in the functions log stream
  2. I cannot use ILogger<T> because it will not log into the log stream
  3. I have to install the Microsoft.Azure.WebJob NuGet package to get the right namespace string which is otherwise unnecessary
  4. I have to pollute my class implementation with the knowledge that it's called within an Azure Function in order for logging to work
  5. I have to pollute the logging in my main app if that class happens to be used within a normal Azure App Service project as well, since the logs in the normal app service will also have the function namespace instead of ILogger<T>
  6. I have to re-write my unit tests to Mock an ILoggerFactory that returns an ILogger because I no longer take an ILogger as a constructor param

Note if you try to be clever and use LogCategories.CreateFunctionUserCategory(this.GetType().FullName) it won't work, the extra . in the name will still break it. But this.GetType().Name will work at the loss of some specificity.

Edit: Actually, this.GetType().Name only works within the CreateFunctionUserCategory function locally, on Azure's log stream you still don't see it. Does this mean we need to actually pass the name of the function calling our method into the logging factory to make this work?!

IronSean avatar Jul 28 '22 20:07 IronSean

@IronSean It looks like Microsoft addressed this issue with .NET out-of-process Azure Functions:

https://docs.microsoft.com/en-us/azure/azure-functions/dotnet-isolated-process-guide#differences-with-net-class-library-functions

"Logging ... ILogger obtained from FunctionContext"

So I think that's probably the way to go, as I was speculating in my comment on Jan 5.

m-ghaoui avatar Aug 02 '22 08:08 m-ghaoui

@m-ghaoui Yes, but we have to wait until the .NET 7 release as out-of-process Functions are not as feature-rich as in-process ones.

rubo avatar Aug 02 '22 15:08 rubo

4 years later and I'm still wasting hours of my time trying to get basic stuff to work because your own project templates do not work properly. I am not happy.

Neutrino-Sunset avatar Jun 12 '23 09:06 Neutrino-Sunset