Print a multi-line message with NLog

2020-07-14 09:57发布

Is it possible with NLog to emit a multi-line message such that each line is formatted according to the current layout? E.g.

2015-12-17 11:37:38.0845 | 64 | INFO | -----------------------------------
2015-12-17 11:37:38.0845 | 64 | INFO | Statistics:
2015-12-17 11:37:38.0845 | 64 | INFO |   Crawling Requests   46887 /min
2015-12-17 11:37:38.0845 | 64 | INFO |   Stored Documents    9910 /min
2015-12-17 11:37:38.0845 | 64 | INFO | -----------------------------------

Alternatively, is it possible with NLog to emit multiple messages as a single, non-interrupted block in a multithreaded environment?

标签: nlog
2条回答
戒情不戒烟
2楼-- · 2020-07-14 10:28

You can do all this from your config.

<variable name="statData" value="${longdate} | 64 | ${level} | "/>
<variable name="line" value="-----------------------------------"/>
<targets>
    <target xsi:type="Console"
        name="Console"
        layout="
        ${statData}${line}${newline}
        ${statData}Statistics:${newline}
        ${statData}  Crawling Requests   46887 /min ${newline}
        ${statData}  Stored Documents    9910 /min ${newline}
        ${statData}${line}${newline}"/>

Wasn't exactly sure what your 64 was or where you were getting your per minute data. Probably a variable or something your inserting.This should also work if you are logging to a file not the console.

As for your second question, if you are wanting a single log message from multiple threads I think you would have to do that on the code side. You would have to collect your threads, get your log data you want and send it 1 time to nLog. I might be misunderstanding though

查看更多
疯言疯语
3楼-- · 2020-07-14 10:35

There does not appear to be existing functionally to do this yet (as of NLog 4.2.3). A potential solution is to create your own wrapper layout renderer to improve the functionality of the replace-newlines renderer.

So replace-newlines and replace wrappers will not take layout renderers in their replacement strings. Looking at the NLog source for other wrappers, renderers, and targets, a Layout type property can be used to accept a string with (or without) layout renderers. The built in replace wrappers fail when provided a layout renderer because their Replacement property type is string. The xml parser is only looking for plain text but the } of a provided layout renderer prematurely ends the replace-newline wrapper.

The following custom wrapper changes the replacement type from string to Layout. The replacement layout then needs to be rendered by calling its Render method with some context (LogEventInfo). This can be done in the overridden Append method where LogEventInfo is available. The rendered output can be saved off for later usage in the Transform method.

using System;
using System.Text;
using NLog.Config;
using NLog.LayoutRenderers;
using NLog.LayoutRenderers.Wrappers;
using NLog.Layouts;

namespace My.Namespace
{
    [LayoutRenderer("replace-newlines-withlayout")]
    [ThreadAgnostic]
    public class ReplaceNewLinesFormatLayoutRendererWrapper : WrapperLayoutRendererBase
    {
        private string m_replacementString = " ";

        public ReplaceNewLinesFormatLayoutRendererWrapper()
        {
            // Changed from
            // Replacement = " ";
            Replacement = Layout.FromString(" ");
        }

        // Changed from
        // public string Replacement { get; set; }
        public Layout Replacement { get; set; }

        // Override Append in order to render the replacement.
        protected override void  Append(StringBuilder builder, NLog.LogEventInfo logEvent)
        {
            // Render...
            m_replacementString = Replacement.Render(logEvent);

            // The base functionality of append is fine.
            base.Append(builder, logEvent);
        }   

        // Called from base.Append()
        protected override string Transform(string text)
        {
            // Changed from 
            // return text.Replace(Environment.NewLine, Replacement);

            // Now just put in the rendered replacement string.
            return text.Replace(Environment.NewLine, m_replacementString);
        }
    }
}

And then, for example, use it as

<target
    ...
    layout=${replace-newlines-withlayout:replacement=${newline}${time}:${message}}
    ... 
    />

In this simplified case, assuming ${message} has line breaks, each new line will be prefixed with a time stamp. Just replace ${time} with the desired prefix layout.

查看更多
登录 后发表回答