0

I am trying to implement the solution that was posted in response to this question but it is not working.

My objective is to log to a file (working) and also have the LogHandler methods fire (not working).

class Program
{
    private static Logger Logger;
    static void Main(string[] args)
    {
        Target.Register<CallbackTarget>("CallbackTarget"); // https://github.com/NLog/NLog/wiki/Register-your-custom-component
        LogManager.Configuration.AddTarget("CallbackTarget", new CallbackTarget(LogHandlerA, LogHandlerB));
        Logger = LogManager.GetCurrentClassLogger();
        Worker.DoNothing();
        Logger.Debug("Log msg from Program");
        Console.ReadLine();

    }

    public static void LogHandlerA(string msg)
    {
        Console.WriteLine("LogHandlerA " + msg);
    }

    public static void LogHandlerB(string msg)
    {
        Console.WriteLine("LogHandlerB " + msg);
    }
}


public class Worker
{
    private static Logger Logger;

    static Worker()
    {
        Logger = LogManager.GetCurrentClassLogger();
    }

    public static void DoNothing()
    {
        Logger.Debug("Log msg from DoNothing");  // should trigger callbacks
    }
}

[Target("CallbackTarget")]
public sealed class CallbackTarget : TargetWithLayout
{
    private readonly Action<String>[] _callbacks;

    public CallbackTarget(params Action<string>[] callbacks)
    {
        this._callbacks = callbacks;

    }

    protected override void Write(LogEventInfo logEvent)
    {
        base.Write(logEvent);

        foreach (var callback in _callbacks)
            callback(logEvent.FormattedMessage);
    }
}

Edit: adding nlog.config

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!--https://github.com/nlog/NLog/wiki/File-target#time-based-file-archival-->

  <variable name="methodName"
            value="${callsite:className=True:fileName=False:includeSourcePath=False:methodName=True:cleanNamesOfAnonymousDelegates=False:includeNamespace=False:skipFrames=0}" />

  <targets>
    <target name="file" xsi:type="File"
        layout="${longdate} [${level:uppercase=true}] [thread ${threadid}] [${methodName}]  ${message} "
        fileName="${basedir}/logs/logfile.txt"
        archiveFileName="${basedir}/archives/log.{#}.txt"
        archiveEvery="Day"
        archiveNumbering="Rolling"
        maxArchiveFiles="7"
        concurrentWrites="true"
        keepFileOpen="false"
        encoding="iso-8859-2" />
  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="file" />
  </rules>

</nlog>
  • 1
    Have you read -> https://github.com/NLog/NLog/wiki/Combine-XML-config-with-C%23-config (See method ExtendNLogConfig where target is added) – Rolf Kristensen Jan 12 '18 at 22:34
  • @RolfKristensen Thanks for the info. I am 99% sure I will be moving to Serilog for a number of reasons, one of which is this one. [See here](https://stackoverflow.com/questions/48268854/how-to-get-formatted-output-from-logevent/48272467#48272467). –  Jan 16 '18 at 00:23
  • ForContext looks like it will give an allocation overhead, but if performance is not a problem then it looks nice. – Rolf Kristensen Jan 16 '18 at 21:03

1 Answers1

0

Nlog has recently received some new features:

  • NLog ver. 4.5.8 introduces lambda for the MethodCallTarget
  • NLog ver. 4.6.4 introduces Logger.WithProperty that matches Serilog.ForContext. Alternative for the existing NLog.Fluent.LogBuilder.
using System;
using System.Runtime.CompilerServices;
using NLog;
using NLog.Fluent;

namespace ConsoleExample
{
    static class Program
    {
        static void Main(string[] args)
        {
            var outputTemplate = "${longdate} ${level} ${logger}${newline}${message}${newline}in method ${event-properties:CallerMemberName} at ${event-properties:CallerFilePath}:${event-properties:CallerLineNumber}${NewLine}${exception:format=tostring}";
            Action<string> handler = LogHandler;

            // Setup NLog Config
            var nlogConfig = new NLog.Config.LoggingConfiguration();
            var customTarget = new NLog.Targets.MethodCallTarget("CustomTarget", (logEvent, parms) => CustomTargetLogger(logEvent, parms[0].ToString(), handler));
            customTarget.Parameters.Add(new NLog.Targets.MethodCallParameter(outputTemplate));
            nlogConfig.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, customTarget);
            NLog.LogManager.Configuration = nlogConfig;

            // Start Logging
            var nlogLogger = NLog.LogManager.GetCurrentClassLogger();
            nlogLogger.Info().Message("Hello World").Write();   // NLog.Fluent.LogBuilder
            nlogLogger.Here().Info("Hello World");  // Requires NLog ver. 4.6.4
            Console.ReadLine();
        }

        public static void CustomTargetLogger(LogEventInfo logEvent, string outputTemplateResult, params Action<string>[] handlers)
        {
            foreach (Action<string> handler in handlers)
                handler(outputTemplateResult);
        }

        public static void LogHandler(string logMsg)
        {
            Console.WriteLine("LogHandler: " + logMsg);
        }

        public static NLog.Logger Here(this NLog.Logger logger,
           [CallerMemberName] string callerMemberName = "",
           [CallerFilePath] string callerFilePath = "",
           [CallerLineNumber] int callerLineNumber = 0)
        {
            return logger.WithProperty("CallerMemberName", callerMemberName)
                .WithProperty("CallerFilePath", callerFilePath)
                .WithProperty("CallerLineNumber", callerLineNumber);
        }

    }
}
Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70