10

I'm logging my messages to a database field with a max size of 1000 characters. Currently if I try to log a message (which often contains exception information with stack trace, HTTP request content, etc.) that's larger than 1000 characters, the insert fails and NLog (as it should) silently ignores that and keeps going.

Is there something I can put in my NLog.config to declare that the message length should always be truncated so it's no greater than 1000 characters?

Bonus points if you can tell me how to gracefully mark truncated messages by replacing the last few characters before the 1000 character limit with something like "[...Truncated]".

Can't believe I can't readily find this with some googling. Hopefully I don't have to write my own renderer?

mo.
  • 4,165
  • 3
  • 34
  • 45

4 Answers4

9

NLog 4.6.3 supports this:

${message:truncate=1000}

Older versions of NLog can do this:

${trim-whitespace:inner=${message:padding=-1000:fixedLength=true}}
Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70
5

I don't know of a built in way to do it. Instead, I would write a LayoutRenderer (actually, a WrapperLayoutRenderer). It's not hard.

Something like this (untested) ought to do it:

[LayoutRenderer("truncate")]
[ThreadAgnostic]
public sealed class TruncateLayoutRendererWrapper : WrapperLayoutRendererBase
{
    public TruncateLayoutRendererWrapper()
    {
        this.Truncate = true;
        this.Ellipsis = true;
        this.Limit = 1000;
    }

    [DefaultValue(true)]
    public bool Truncate { get; set; }

    [DefaultValue(true)]
    public bool Ellipsis { get; set; }

    [DefaultValue(1000)]
    public bool Limit { get; set; }

    /// <summary>
    /// Post-processes the rendered message. 
    /// </summary>
    /// <param name="text">The text to be post-processed.</param>
    /// <returns>Trimmed string.</returns>
    protected override string Transform(string text)
    {
        if (!Truncate || Limit <= 0) return text;

        var truncated = text.Substring(0, Ellipsis ? Limit - 3 : Limit);
        if (Ellipsis) truncated += "...";

        return truncated;
    }
}
wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • 1
    Thanks. It certainly does look easy, but I was hoping to avoid it mainly because I'd end up with this annoying little class that all my projects that use this logging system would need to have a dependency on, and it doesn't seem like it's worth the maintenance. – mo. Oct 14 '13 at 21:20
5

One way to do this is by using regular expression replacement of the message, which you can define right in the nlog.config. I used the following to truncate to 500 characters:

<variable name="truncated_message" value="${replace:replaceWith=...TRUNCATED:regex=true:inner=${message}:searchFor=(?&lt;\=.\{500\}).+}"/>

<target name="filelog" xsi:type="File" fileName="${basedir}/../logs/jobs/${shortdate}.log" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff}|${level:uppercase=true}|${truncated_message}"/>
user1411430
  • 153
  • 2
  • 8
4

In log4net I'd specify left(@msg, 1000) in the insert statement to ensure the message fits the database column. You can probably do something similar in nlog.

In sql server you can construct an insert into .. select from-statement utilizing this fragment:

case when len(@msg) > 1000 then left(@msg, 988)+' [truncated]' else @msg end
EventHorizon
  • 2,916
  • 22
  • 30
  • 1
    Thanks. That `left(@msg, 1000)` thing in log4net would be perfect :/ I ended up doing pretty much what you suggested, SQL side, in a stored procedure that wraps the insert. I wish I could mark both as the accepted answer but, even though I ended up doing what you suggested, I feel like wageoghe's answer is more useful in the general case. – mo. Oct 14 '13 at 21:19
  • That's actually a pretty good idea, wrapping the insert in a stored procedure and doing the truncate there! – wageoghe Oct 14 '13 at 21:25
  • you can do `left(@msg, 1000)` in NLog – Turch Oct 08 '15 at 19:30