Search code examples
c#asp.net-core-2.0nlogasp.net-core-webapihttpcontent

Nlog Log IP Address from NetCore Library Project


I have a VS 2017 solution with multiple projects, I have a separated project with the NLog methods to use it from other projects for not include NLog in all projects.

  • NetCore Rest API
  • NetCore Infraestructure
  • NetCore Database And persistence
  • NetCore Logger
  • Other NetStandard Projects with helpers and ViewModel Classes

I want to Log Client IP Address and user if it's logged in. I have seen that I have to use a template in the NLog.config, but it is not working and the log IPAddress field is empty.

What's the correct way to do it?

NLog.Config

<extensions>    
  <add assembly="NLog.Web.AspNetCore"/>
</extensions>

<targets>
  <target name="Database" xsi:type="Database">
    <commandText>
      INSERT INTO T_LOG
        (DATE_, APP, IPADDRESS, USER_, CENTRE, LEVEL_, LOGGER,
        METHOD, MESSAGE, EXCEPTION,
        SOURCE_FILE_PATH, LINE_NUMBER)
      VALUES(
        @Date, @App, @IPAddr, @User, @Centre, @Level, @Logger,
        @Method, @Message, @Exception,
        @SourceFilePath, @LineNumber);
    </commandText>

    <parameter name="@App" layout="${AppName}" />
    <parameter name="@IPAddr" layout="${aspnet-request-ip}" />
    <parameter name="@User" layout="" />
    <parameter name="@Centre" layout="" />
    <parameter name="@Date" layout="${longdate}" />
    <parameter name="@Level" layout="${level:upperCase=true}"/>
    <parameter name="@Logger" layout="${logger:shortName=false}"/>
    <parameter name="@Method" layout="${event-context:item=callermember}"/>
    <parameter name="@Message" layout="${message}" />
    <parameter name="@Exception" layout="${exception:format=ToString}"/>
    <parameter name="@SourceFilePath" layout="${event-context:item=callerpath}" />
    <parameter name="@LineNumber" layout="${event-context:item=callerline}" />

</target>

Logger Class

using Microsoft.AspNetCore.Http;
using NLog;
using System;
using System.Runtime.CompilerServices;

namespace Project.Log
{
    public class CustomLogger
    {
        private readonly Logger logger;
        private Type type;

        public RhesusLog(Type type)
        {
            this.type = type;
            this.logger = LogManager.GetLogger(type.FullName);
        }

        public void Debug(string msg, Exception ex = null,
            [CallerFilePath] string CallerPath = "",
            [CallerMemberName] string CallerMember = "",
            [CallerLineNumber] int CallerLine = 0, params object[] args)
        {
            this.Log(LogLevel.Debug, msg, ex, CallerPath, CallerMember, CallerLine);
        }

        public void Info(string msg, Exception ex = null,
            [CallerFilePath] string CallerPath = "",
            [CallerMemberName] string CallerMember = "",
            [CallerLineNumber] int CallerLine = 0)
        {
            this.Log(LogLevel.Info, msg, ex, CallerPath, CallerMember, CallerLine);
        }

        // Other Levels logs ....

        private void Log(LogLevel Level, string msg,
            Exception Exception = null, string CallerPath = "",
            string CallerMember = "", int CallerLine = 0, params object[] parameters)
        {
            LogEventInfo LogEvent = new LogEventInfo(Level, this.type.FullName, msg);
            LogEvent.Parameters = parameters;
            LogEvent.Exception = Exception;
            LogEvent.Properties.Add("callerpath", CallerPath);
            LogEvent.Properties.Add("callermember", CallerMember);
            LogEvent.Properties.Add("callerline", CallerLine);

            this.logger.Log(LogEvent);
        }
    }
}

I have used NLog Log and I can see this message on the Log

2018-09-06 23:31:44.6183 Debug Setting 'DatabaseParameterInfo.layout' to '${aspnet-request-ip}'
2018-09-06 23:31:44.6183 Debug Missing serviceProvider, so no HttpContext

Solution

  • If NLog logs "Missing serviceProvider, so no HttpContext", the serviceProvider isn't correctly registered to NLog.

    The UseNLog() on IWebHostBuilder will register the ServiceProvider and add the HttpContextAccessor to the DI system.

    So you should double check if you call UseNLog() on the right time, see this example of program.cs:

    public static void Main(string[] args)
    {
        // NLog: setup the logger first to catch all errors
        var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
        try
        {
            logger.Debug("init main");
            BuildWebHost(args).Run(); 
        }
        catch (Exception ex)
        {
            //NLog: catch setup errors
            logger.Error(ex, "Stopped program because of exception");
            throw;
        }
        finally
        {
            // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
            NLog.LogManager.Shutdown();
        }
    }
    
    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders();
                logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
            })
            .UseNLog()  // NLog: setup NLog for Dependency injection
            .Build();
    

    See also https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-2