0

I have successfully configured NLog in .net core 2.2 web api. But I wanted to achieve specifications with logging. How to achieve below:

  • Warnings should be logged to warn specific file
  • Errors should be logged to error specific file
  • When I log request/response that file should only consist of my requests

But currently with request log the file is also saving error/warn logs in the same file and also in error/warn specific log files too. How can I segregate the logs in categorized files so that specific logs will be stored in that file only and not in other files at the same time?

My nlog.config File

<?xml version="1.0" encoding="utf-8" ?>  
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="info" internalLogFile="internalLog.txt">  
    <extensions>  
        <add assembly="NLog.Web.AspNetCore" />  
    </extensions>  
    <!-- the targets to write to -->  
    <targets>  
        <!-- write to file -->  
        <target xsi:type="File" 
                name="allFile" 
                fileName="${var:customDir}\logs\AllLog\${shortdate}.log" 
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />  
        <!-- another file log. Uses some ASP.NET core renderers -->  
        <target xsi:type="File" 
                name="requestFile" 
                fileName="${var:customDir}\logs\RequestTrace\${shortdate}.log" 
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

        <target xsi:type="File" name="warnFile" fileName="${var:customDir}\logs\Warnings\Warn_${shortdate}.log"  />
        <target xsi:type="File" name="errorFile" fileName="${var:customDir}\logs\Errors\Error_${shortdate}.log"  />
    </targets>  
    <!-- rules to map from logger name to target -->  
    <rules>  
        <logger name="*" minLevel="Trace" writeTo="allFile" />  
        <!--Skip non-critical Microsoft logs and so log only own logs-->  
        <!--<logger name="Microsoft.*" maxLevel="Info" final="true" />-->  
        <logger name="*" minLevel="Info" writeTo="requestFile" />  
        <logger name="*" minLevel="Warn"  writeTo="warnFile" />  
        <logger name="*" minLevel="Error"  writeTo="errorFile" />  
    </rules>  
</nlog> 

My Startup File


    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(config =>
            {
                config.Filters.Add(new ApiLoggingMiddleware());
                config.Filters.Add(new ApiExceptionLoggingMiddleware());
            }
            ).SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore)
            .ConfigureApiBehaviorOptions(options =>
            {
                options.SuppressInferBindingSourcesForParameters = true;
            });
        }


        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseDeveloperExceptionPage();
            app.UseAuthentication();
            //app.UseMiddleware<ApiLoggingMiddleware>();
            LogManager.Configuration.Variables["customDir"] = Directory.GetCurrentDirectory();
        }
    }
}

My apilogginmiddleware.cs File

   public class ApiLoggingMiddleware : TypeFilterAttribute
    {
        public ApiLoggingMiddleware() : base(typeof(AutoLogActionFilterImpl))
        {

        }

        private class AutoLogActionFilterImpl : IActionFilter
        {
            private readonly ILogger _logger;
            public AutoLogActionFilterImpl(ILoggerFactory loggerFactory)
            {
                _logger = loggerFactory.CreateLogger<ApiLoggingMiddleware>();
            }

            public void OnActionExecuting(ActionExecutingContext context)
            {
                // perform some business logic work
            }

            public void OnActionExecuted(ActionExecutedContext context)
            {
                ...
                _logger.LogInformation("Log request data");
                ...
            }
        }
    }
}

MY apiexceptionloggingmiddleware.cs file

    public class ApiExceptionLoggingMiddleware : TypeFilterAttribute
    {
        public ApiExceptionLoggingMiddleware() : base(typeof(AutoLogExceptionImpl))
        {

        }

        private class AutoLogExceptionImpl : IExceptionFilter
        {
            private readonly ILogger _logger;
            public AutoLogExceptionImpl(ILoggerFactory loggerFactory)
            {
                _logger = loggerFactory.CreateLogger<ApiLoggingMiddleware>();
            }

            public void OnException(ExceptionContext context)
            {
                _logger.LogError("Errors : " + context.Exception 
                    + Environment.NewLine + Environment.NewLine);
            }
        }
    }
}
Julian
  • 33,915
  • 22
  • 119
  • 174
Dhiren
  • 153
  • 1
  • 13

2 Answers2

1

The rules are matched from top to bottom. So minlevel=info will match also errors etc.

Simple solution here, use level instead of minlevel

  <rules>  
        <logger name="*" minLevel="Trace" writeTo="allFile" />  
        <!--Skip non-critical Microsoft logs and so log only own logs-->  
        <!--<logger name="Microsoft.*" maxLevel="Info" final="true" />-->  
        <logger name="*" level="Info" writeTo="requestFile" />  
        <logger name="*" level="Warn"  writeTo="warnFile" />  
        <logger name="*" level="Error"  writeTo="errorFile" />  
    </rules>

Alternative option

Another option is to use final and match first on error, then warn etc.

E.g.

  <rules>  
      <logger name="*" minLevel="Trace" writeTo="allFile" />  
      <!--Skip non-critical Microsoft logs and so log only own logs-->        
      <!--<logger name="Microsoft.*" maxLevel="Info" final="true  />-->  
      <logger name="*" minLevel="Error" writeTo="errorFile" final="true” />
      <logger name="*" minLevel="Warn" writeTo="warnFile" final="true" />  
      <logger name="*" minLevel="Info" writeTo="requestFile" final="true" />
    </rules>

See also https://github.com/NLog/NLog/wiki/Configuration-file#rules

Julian
  • 33,915
  • 22
  • 119
  • 174
0

I recommend that you make a special logging rule for your request-logger ApiLoggingMiddleware:

<rules>  
    <logger name="*" minLevel="Trace" writeTo="allFile" />  
    <!--Skip non-critical Microsoft logs and so log only own logs-->  
    <!--<logger name="Microsoft.*" maxLevel="Info" final="true" />-->  
    <logger name="*ApiLoggingMiddleware" minLevel="Info" writeTo="requestFile" />  
    <logger name="*" minLevel="Error"  writeTo="errorFile" final="true" />  
    <logger name="*" minLevel="Warn"  writeTo="warnFile" final="true" />  
</rules>  

Then the requestFile-target will not be polluted by other Info-logging-events.

But make sure that ApiExceptionLoggingMiddleware and ApiLoggingMiddleware is not sharing the same Logger-name. Looks like ApiLoggingMiddleware performs the request-logging, so avoid reuse/abuse of its Logger-name (See loggerFactory.CreateLogger<ApiLoggingMiddleware>())

Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70
  • I have tried this and still getting request log polluted with error logs, even in warn logs also I am getting error logs their. – Dhiren Nov 16 '19 at 12:11
  • I am getting WARN,INFO,ERROR logs all in request log file. – Dhiren Nov 16 '19 at 12:12
  • @Dhiren Updated answer with new NLog.config, that fixes so ERROR doesn't drip down to warnFile. – Rolf Kristensen Nov 16 '19 at 12:23
  • @Dhiren Can you give an example of the unwanted Logger-Name in the requestFile (The name after the LogLevel). ? It should not be possible unless you have other loggers also named `ApiLoggingMiddleware`. – Rolf Kristensen Nov 16 '19 at 12:25
  • @Dhiren See the issue now. `ApiExceptionLoggingMiddleware` is using the same logger-name as `ApiLoggingMiddleware`. Please fix that. – Rolf Kristensen Nov 16 '19 at 12:28
  • ok I got your point Let me try and I will update you, Thank you for your good support and time. – Dhiren Nov 18 '19 at 03:04