View on GitHub

Devendra's Log

Logging to Syslog using NLog

NLog’s Network target can be used to stream to Syslog, using a target definition such as

    <target xsi:type="Network"
            name="syslog"
            onOverflow="Split"
            newLine="false"
            lineEnding="None"
            layout="${literal:text=&lt;14&gt;1} ${shortdate:universalTime=true}T${time:universalTime=true}Z ${machinename} ${processname} ${processid} ${literal:text=-} ${literal:text=-} [${level:uppercase=true}] ${message}"
            maxMessageSize="65000"
            encoding="utf-8"
            connectionCacheSize="5"
            maxConnections="0"
            maxQueueSize="0"
            keepConnection="true"
            onConnectionOverflow="AllowNewConnnection"
            address="udp://127.0.0.1:514" />

Here’s an example of a log entry written using that target

<14>1 2018-06-01T13:18:47.5949Z DESKTOP-KUQ62LL MyTestApplication 10256 - - Some log message

One problem with this approach—as we’ve hardcoded syslog severity to informational—is that the log level does not get translated to a syslog severity.

To properly translate the severity, we can write a custom renderer for NLog such as

    public enum SyslogFacility
    {
        UserLevelMessages = 1
    };

    public enum SyslogSeverity
    {
        Emergency = 0,      // system is unusable
        Alert = 1,          // action must be taken immediately
        Critical = 2,       // critical conditions
        Error = 3,          // error conditions
        Warning = 4,        // warning conditions
        Notice = 5,         // normal but significant condition
        Informational = 6,  // informational messages
        Debug = 7           // debug-level messages
    };

    [LayoutRenderer("syslogpriority")]
    public class SyslogPriorityRenderer : LayoutRenderer
    {
        Dictionary<NLog.LogLevel, SyslogSeverity> NLogLevelToSyslogSeverity =
            new Dictionary<NLog.LogLevel, SyslogSeverity>
            {
                { NLog.LogLevel.Debug, SyslogSeverity.Debug },
                { NLog.LogLevel.Error, SyslogSeverity.Error },
                { NLog.LogLevel.Fatal, SyslogSeverity.Critical },
                { NLog.LogLevel.Info, SyslogSeverity.Informational },
                { NLog.LogLevel.Trace, SyslogSeverity.Debug },
                { NLog.LogLevel.Warn, SyslogSeverity.Warning }
            };

        protected override void Append(StringBuilder builder, LogEventInfo logEvent)
        {
            builder.Append($"<{(int)SyslogFacility.UserLevelMessages * 8 + NLogLevelToSyslogSeverity[logEvent.Level]}>");
        }
    }

Next, we register our assembly so NLog can find the renderer, and modify the target’s layout as shown

    <extensions>
        <add assembly="MyTestApplication" />
    </extensions>

    <target xsi:type="Network"
            name="syslog"
            onOverflow="Split"
            newLine="false"
            lineEnding="None"
            layout="${syslogpriority}${literal:text=1} ${shortdate:universalTime=true}T${time:universalTime=true}Z ${machinename} ${processname} ${processid} ${literal:text=-} ${literal:text=-} ${message}"
            maxMessageSize="65000"
            encoding="utf-8"
            connectionCacheSize="5"
            maxConnections="0"
            maxQueueSize="0"
            keepConnection="true"
            onConnectionOverflow="AllowNewConnnection"
            address="udp://127.0.0.1:514" />

This will produce a similar log entry as before for LogLevel Info, and will properly translate other levels using the NLogLevelToSyslogSeverity dictionary.

If the syslog facility discards messages that are too long, you may need to restrict the size of the message. This can be achieved—since NLog 4.4—using a lambda expression such as

    LayoutRenderer.Register("shortmessage", (logEvent) =>
        logEvent.Message.Substring(0, logEvent.Message.Length > 1000 ? 1000 : logEvent.Message.Length));

Now, when you use the renderer {shortmessage}, the message will be truncated to a 1000 bytes.