Search code examples
c#csvnlogline-numbers

NLog: Recording Line number and calling method when using wrapper class for NLog


I'm using NLog for logging and thanks to these answers:

Getting Logger Name into Excel file with NLog

NLog: Limit Logger Names to Enum or other structure

I have most things working. I wrote my own wrapper class to force user to pick a LoggerName from a set list of names. All code is below. All works well except for one minor problem.

  1. The linenumber and calling method printed out by callsite and callsite-linenumber are the calling function and line number from within the wrapper class and not the method and line number that called the wrapper class. This isn't surprising, and I'm guessing it's a common problem. Is there a simple fix OR can I somehow create my own layout option that uses a line number passed into the method? Or any other suggestions?

    using System;
    namespace Common.NLogEx
    {
        public enum LoggerNames
        {
        Database,
        Thermal,
        StateMachine,
        App,
        Poll
       }
    
        public enum LogLevel
       {
        Trace,
        Debug,
        Info,
        Warn,
        Error,
        Fatal,
        Off,
        Warning
        }
    
    public static class Logger
    {
    
        public static void Log(LoggerNames name, LogLevel level , string message)
        {
            if (level == LogLevel.Warning)
                level = LogLevel.Warn;
            NLog.LogLevel nLevel = NLog.LogLevel.FromString(level.ToString());
            NLog.LogManager.GetLogger(name.ToString()).Log(nLevel,message);
        }
    
        public static void Log(LoggerNames name, LogLevel level, Exception ex, string message)
        {
            if (level == LogLevel.Warning)
                level = LogLevel.Warn;
            NLog.LogLevel nLevel = NLog.LogLevel.FromString(level.ToString());
            NLog.LogManager.GetLogger(name.ToString()).Log(nLevel, ex, message, null);
        }
    }
    

    }

and the NLog configuration file:

<?xml version="1.0" encoding="utf-8" ?>

<target name="logfile" xsi:type="File" fileName="${basedir}/Logs/file.txt" />
<target name="logconsole" xsi:type="Console" />
<target name="excelfile" xsi:type="File" fileName="${cached:cached=true:Inner=${basedir}/Logs/${date:format=yyyy-MM-dd hh.mm.ss}:CacheKey=${shortdate}}.csv" archiveAboveSize="32000000" archiveFileName="${basedir}/Logs/Archives/${date:format=yyyy-MM-dd hh.mm.ss}.{#####}.csv" archiveEvery="Day" archiveNumbering="Sequence" maxArchiveFiles="0">

  <layout xsi:type="CsvLayout">
    <!-- Layout Options -->

    <column name="time" layout="${longdate}" />
    <column name="level" layout="${level}"/>
    <column name="logger" layout="${logger}"/>
    <column name="message" layout="${message}" />
    <column name="callsite" layout="${callsite}" />
    <column name="callsite-linenumber" layout="${callsite-linenumber}" />
    <column name="exception" layout="${exception:format=toString,StackTrace}${newline}" />

  </layout>
</target>
</targets>

<rules>

<logger name="*" minlevel="Debug" writeTo="logconsole" />    
<logger name="*" minlevel="Debug" writeTo="excelfile" />    
</rules>
</nlog>

Solution

  • Got it! Did a better search and found this fantastic question and answer:

    How to retain callsite information when wrapping NLog You simply need to pass in your wrapper class type to the "Log" method.

    For anyone interested, here's my new wrapper class. The last method uses the "typeof" idea.

    public static class Logger
    {
        private static string _unclassified = "Unclassified";
        [Obsolete("Please supply a LoggerName and LoggerLevel")]
        public static void Log(string message)
        {          
            Log(_unclassified,LogLevel.Info, message,null);
        }
        [Obsolete("Please supply a LoggerName")]
        public static void Log(LogLevel level, string message)
        {
            Log(_unclassified,level,message,null);
        }
    
        [Obsolete("Please supply a LoggerName and LoggerLevel")]
        public static void Log(Exception ex)
        {          
            Log(_unclassified,LogLevel.Info, "",ex);
        }
    
        public static void Log(LoggerNames name, LogLevel level , string message)
        {
    
            NLog.LogLevel nLevel = NLog.LogLevel.FromString(level.ToString());
            Log(name.ToString(), level, message);
        }
    
        public static void Log(LoggerNames name, LogLevel level, Exception ex, string message)
        {
    
            NLog.LogLevel nLevel = NLog.LogLevel.FromString(level.ToString());
            Log(name.ToString(),level,message,ex);
        }
    
        private static void Log(string loggerName, LogLevel level, string message, Exception ex = null)
        {
            if (level == LogLevel.Warning)
                level = LogLevel.Warn;
            NLog.LogLevel nLevel = NLog.LogLevel.FromString(level.ToString());
    
            LogEventInfo logEvent = new LogEventInfo(nLevel,loggerName,null,message,null,ex);
            NLog.LogManager.GetLogger(loggerName).Log(typeof(Logger), logEvent);
        }
    }