28

How can I reuse the TraceWriter object available in an Azure Function to log information in an externally referenced library? I tried passing in the object using the constructor and referencing the TraceWriter class (web.http.tracing). I didn't have luck as the classes seem different.

Trevor Reid
  • 3,310
  • 4
  • 27
  • 46
Lance
  • 592
  • 2
  • 7
  • 13

3 Answers3

40

Short version Use the Microsoft.Azure.WebJobs.Host.TraceWriter available in this nuget package.

Alternatively, build your functions as a Web Project and you can debug locally. You can find a sample here.

Long Version

Your problem here is that you are using the wrong TraceWriter.

I used the Azure Function logger in an Azure function to output the type of the logger.

log.Info(log.GetType().ToString());

Which gave the following:

Microsoft.Azure.WebJobs.Script.InterceptingTraceWriter

I too was expecting a Web/Http TraceWriter and was surprised that there is yet another implementation to deal with. Microsoft could really do with creating a standard approach, or at least giving us a nice clean interface for Error, Warning, Info, Verbose etc. Maybe something for .Net Standard... please.

I will create my own interface and wrap my app logger and the Azure one so that I can inject whichever I need without causing headaches further along in my code. This will also offer some protection from potential pain inflicted by future breaking changes.

Anyway, I digress, I then tracked Microsoft.Azure.WebJobs.Script.InterceptingTraceWriter down to the Azure Functions / Webjobs scripting GitHub repo and then on to the Nuget package. I have tested this and it works fine to pass the Azure Function logger in to your external assembly and continue to log to the Azure Function environment from there.

Here is an example:

using Microsoft.Azure.WebJobs.Host;

public static void TryLog(TraceWriter azureFunctionsLogger)
{
    azureFunctionsLogger.Info("************** IT WORKED **************");
}

I love the potential of Azure functions, but it is still a little immature and overly complex.

I hope this helps.

Added a very simple single class logger to illustrate.

It writes to either the Azure Functions Logger or a standard Systems.Diagnostics.Trace. You need to paste this over the contents of Program.cs of a standard C# Console Application. You will also need to include the Nuget package Microsoft.Azure.WebJobs.

namespace LoggingTestConsole
{
    using System;

    /// <summary>
    /// Generic logging interface for portability 
    /// </summary>
    public interface ILogger
    {
        void Error(string message);
        void Information(string message);
        void Warning(string message);
    }


    /// <summary>
    /// Azure Functions logger
    /// </summary>
    public class AzureFunctionLogger : ILogger
    {
        private static Microsoft.Azure.WebJobs.Host.TraceWriter _logger;

        public AzureFunctionLogger(Microsoft.Azure.WebJobs.Host.TraceWriter logger)
        {
            _logger = logger;
        }

        public void Error(string message)
        {
            _logger.Error(message);
        }

        public void Information(string message)
        {
            _logger.Info(message);
        }

        public void Warning(string message)
        {
            _logger.Warning(message);
        }
    }


    /// <summary>
    /// Windows Trace logger
    /// </summary>
    public class TraceLogger : ILogger
    {
        public TraceLogger()
        {
            System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(Console.Out));
        }

        public void Error(string message)
        {
            System.Diagnostics.Trace.TraceError(message);
        }


        public void Information(string message)
        {
            System.Diagnostics.Trace.TraceInformation(message);
        }

        public void Warning(string message)
        {
            System.Diagnostics.Trace.TraceWarning(message);
        }

        public void Warning(string format, params object[] args)
        {
            System.Diagnostics.Trace.TraceWarning(format, args);
        }
    }

    /// <summary>
    /// You would put this in a separate project and just share the ILogger interface.
    /// Pass the relevant logger in from Azure Functions or a standard windows Trace logger.
    /// </summary>
    public class DoStuff
    {
        public DoStuff(ILogger logger)
        {
            logger.Information("We are logging to logger you passed in!");
        }
    }

    public class Program
    {

        /// <summary>
        /// Sample usage
        /// </summary>
        static void Main(string[] args)
        {
            // var loggerEnvironment = "AzureFunctions";
            var loggerEnvironment = "ConsoleApp";

            ILogger logger = null;

            if (loggerEnvironment == "AzureFunctions")
            {
                Microsoft.Azure.WebJobs.Host.TraceWriter azureFunctionLogger = null;
                logger = new AzureFunctionLogger(azureFunctionLogger);
            }
            else if (loggerEnvironment == "ConsoleApp")
            {
                logger = new TraceLogger();
            }

            var doStuff = new DoStuff(logger);
            Console.ReadKey();
        }
    }
}
Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
  • 4
    Thanks for this, it's strangely hard to find this out from docs/code! What is the interface that TraceWriter implements? – Rory Feb 15 '17 at 17:05
  • 1
    Start here and work your way around - https://github.com/Azure/azure-webjobs-sdk/blob/663a508e8a851629c26a51e7de3af36629dfd120/src/Microsoft.Azure.WebJobs.Host/TraceWriter.cs it's been a while since I worked with this. I ended up building my own interface (added to the answer above) and then implementing it with the .NET TraceWriter and the WebJobs TraceWriter which gave me an easy way to debug local with the bog standard .NET TraceWriter and then flip on the Azure logging for publishing. – Murray Foxcroft Feb 15 '17 at 22:58
  • 1
    using Microsoft.Azure.WebJobs.Host locally with visual studio doesn't work. but when deployed it does. – raklos Feb 21 '17 at 07:50
  • 2
    @raklos, create an interface as per my answer, then implement it for Microsoft.Azure.WebJobs.Host for the cloud and whatever you use to log locally, e.g. I use System.Diagnostics.Trace. Then use a factory pattern or your favourite DI container to switch between the two depending on the environment you are configured for. – Murray Foxcroft Feb 21 '17 at 08:06
  • 1
    @MurrayFoxcroft, please can you put a fuller sample, including the way to differentiate between cloud and local environment for azure functions, thanks... my main problem is creating the Cloudlogger class. thanks – raklos Feb 21 '17 at 12:23
  • 2
    @Rakos, I have added a very simple sample to the code. – Murray Foxcroft Feb 21 '17 at 18:55
  • @MurrayFoxcroft i get this error: `run.csx(21,67): error CS1503: Argument 1: cannot convert from 'Microsoft.Azure.WebJobs.Host.TraceWriter [\AppData\Local\Azure.Functions.Cli\1.0.0-beta.91\Microsoft.Azure.WebJobs.Host.dll]' to 'Microsoft.Azure.WebJobs.Host.TraceWriter [\.nuget\packages\Microsoft.Azure.WebJobs\1.1.2\lib\net45\Microsoft.Azure.WebJobs.Host.dll]'`. but `1.0.0-beta.91` isnt' even available anymore :( do i need to add a binding redirect? what version did you install in the external lib? – RPM1984 Feb 28 '17 at 04:51
  • i ended up just referencing the dll manually in my external lib :( – RPM1984 Feb 28 '17 at 05:08
  • @RPM1984 I just recreated the sample from scratch and it worked fine. The Nuget Package pulled in was Microsoft.Azure.WebJobs 1.1.2. The console project is .Net 4.6.1 and the tooling Visual Studio 2015 with all the latest updates installed. – Murray Foxcroft Feb 28 '17 at 09:20
  • Did it really work for folks after just installing the WebJobs NuGet package? I had to add this https://github.com/Azure/azure-webjobs-sdk/wiki/%22Nightly%22-Builds as a NuGet source, include pre-release packages, switch the app to .Net framework 4.6.1 and then install Microsoft.Azure.WebJobs.Script, only then could I access the TraceWriter implementation. – DivineOps Apr 02 '17 at 18:47
  • Yes, works fine with just the WebJobs Nuget. Obviously of you cant log to Azure WebJobs when running locally though. I haven't tested with VS2017 - are you using that? – Murray Foxcroft Apr 02 '17 at 21:09
  • Thanks for the interface. – divinci Sep 13 '17 at 11:24
  • A pleasure @divinci, glad it came in handy. – Murray Foxcroft Sep 13 '17 at 12:05
10

As an update, Azure Functions now support consuming an ILogger instead of TraceWriter so you could consume any logging framework that implements ILogger.

See the GitHub issue and the subsequent wiki documentation.

d219
  • 2,707
  • 5
  • 31
  • 36
Chris
  • 5,040
  • 3
  • 20
  • 24
6

If I am correct the necessary versions for getting ILogger to work with Azure functions would be Microsoft.Azure.WebJobs 2.1.0-beta1. However, I can't seem to get an Azure function running using an ILogger instead of a TraceWriter.

Also very little information and documentation is available about developing Azure Functions using ILogger. Does anyone have more information or tips to get this working?

My C# code snippet:

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.ServiceBus;
using System;
using Newtonsoft.Json;
using Microsoft.Extensions.Logging;

namespace Experimental.Functions
{
    public static class ListenToEventFunction
    {
        [FunctionName("ListenToEventFunction")]
        public static void Run([EventHubTrigger("events", Connection = "EventHubConnectionString")]string myEventHubMessage, ILogger log)
        {
            log.LogInformation($"C# Event Hub trigger function processed a message: {myEventHubMessage}");
        }
    }
}

I get the following error when debugging my Azure Function using the Azure Function Tools for VS2017:

A ScriptHost error has occurred
Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.EnrichTelemetryLocation'. 
Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'log' to type ILogger. 
Make sure the parameter Type is supported by the binding. 
If you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).
Boxed
  • 71
  • 1
  • 3
  • 1
    There's almost no information about azure functions... I had the same problem, that I could not run my function when asking for an `Microsoft.Extensions.Logger.ILogger`. I was targeting net461. It could be different if you run against netcore2.0 – Jim Aho Mar 29 '18 at 13:12
  • 1
    ILogger interface does not support the info function... it's silly =P – Gaspa79 Jun 25 '18 at 13:55