1

I have the following class :

[DataContract]
public class CallInformation
{
    [DataMember]
    public string Address { get; set; }
    [DataMember]
    public Boolean IsEmpty { get; set; }
    [DataMember]
    public Boolean IsFaulted { get; set; }
    [DataMember]
    public string Action { get; set; }
    [DataMember]
    public CallOrder CallDirection { get; set; }
    [DataMember]
    public DateTime EventTime { get; set; }
    [DataMember]
    public TimeSpan Duration { get; set; }
    [DataMember]
    public Boolean IsCallback { get; set; }
    [DataMember]
    public string LogSource { get; set; } = "Unknown";

    [DataMember]
    public string Soap { get; set; }
    public string EventTimeDisplay
    {
        get { return EventTime.ToString("HH:mm:ss.fffffff"); }
        set { }
    }
}

This is filled with data about communication in a client server application and den sent to NLog :

public void LogCommunication(CallInformation callInfo)
        {
            var logEvent = new LogEventInfo(LogLevel.Trace, "CommunicationLogger", "CommunicationLogger is logging");
            logEvent.Properties["CallInformation"] = callInfo;
            _comLogger.Log(logEvent);
        }

If it correct to put it in the properties or should it be put in the Parameters?

I need NLog to log it to file in a way that it later can be picked up and searched by Filebeat, ElasticSearch and Kibana. I have tried this NLog config :

<logger name="CommunicationLogger" minlevel="Trace" writeto="f"></logger>

<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${event-properties:item=CallInformation} ${message}" />

But all it prints is CommunicationLogger is logging? I suspect that I need it to serialize the whole object in some way?

Regards

Edit 1 I have tried to change the layout like this :

<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${event-properties:item=CallInformation:jsonEncode=true} ${message}" />

This does still not work, but if I change this code it workes :

logEvent.Properties["CallInformation"] = "test"; //callInfo;

test is written to file as it should so there is probably a problem to pars the simple class CallInformation to file.

I have checked the internal-log for NLog but I can´t finde any errors there.

Edit 2

I tried to change the code like this :

//var logEvent = new LogEventInfo(LogLevel.Trace, "CommunicationLogger", "ComLog writing.");
            //logEvent.Properties["CallInformation"] = callInfo;
            _comLogger.Log(LogLevel.Trace, "Test to log {@CallInformation}", callInfo);

nlog.config

<target xsi:type="File" 
            name="communicationFileLog" 
            fileName="${basedir}/logs/${shortdate}.log"
            maxArchiveDays="5" 
            maxArchiveFiles="10"
            layout="${event-properties:item=CallInformation:format@} ${message}"  />

The result in the log file is this :

 Test to log {@CallInformation}
 Test to log {@CallInformation}
 Test to log {@CallInformation}
 Test to log {@CallInformation}
...
Banshee
  • 15,376
  • 38
  • 128
  • 219
  • Does this answer your question? [Why do I get FormatException with NLog?](https://stackoverflow.com/questions/61713960/why-do-i-get-formatexception-with-nlog) – Julian May 12 '20 at 13:34
  • 1
    "If it correct to put it in the properties or should it be put in the Parameters?" this is answered here? https://stackoverflow.com/questions/61713960/why-do-i-get-formatexception-with-nlog. It depends if you need it in your message. I would choose properties. – Julian May 12 '20 at 13:35
  • also you need something like this `${event-properties:item=LogData:jsonEncode=true}`, also in https://stackoverflow.com/questions/61713960/why-do-i-get-formatexception-with-nlog – Julian May 12 '20 at 13:35
  • Thanks, I Tried this : layout="${event-properties:item=CallInformation:jsonEncode=true} ${message}" /> but I do still only get the message in the log file. The result from _comLogger.Log(logEvent) will be configurated with both FileTarget and a CustomTarget. The CustomTarget unpack the objekt and send it to a vewier in the app, the File target will try to express the object in text on file. – Banshee May 12 '20 at 13:55
  • If I change the logEvent.Properties["CallInformation"] to logEvent.Properties["CallInformation"] = "test" it works fine so there is a problem to pars the simple class to file it seems. I can´t see any error in the nlog-internal log file. – Banshee May 12 '20 at 14:31
  • Looks like you're still using `${event-properties:item=CallInformation}`. Could you double check you're editing the correct nlog.config? (check your bin folder for example) – Julian May 12 '20 at 15:52
  • The NLog.config is set to copy always and I have double checked the bin folder. When doing other changes it is applied. It seems like it can´t pars the CallInformation to text. I could create my own ToString on this class but then I lose the NLog ability to decide exacly how the layout should look like in runtime. The current layout looks like this : layout="${event-properties:item=CallInformation:jsonEncode=true:truncate=1000} ${message}" If I change my C# code so the CallInformation property only holds a string "TEST" then "TEST" is written to the log file. – Banshee May 12 '20 at 16:38

1 Answers1

3

NLog needs to be told, that a LogEvent property is safe for reflection and serialization. The normal way of doing this is like this:

_compLogger.Trace("CommunicationLogger is logging {@CallInformation}", callInfo);

Then NLog will know that the LogEvent property "CallInformation" is safe to to serialize, because of @.

If you are in an exotic mood, and don't want the property included in the LogEvent message, then you can do like this:

var logEvent = new LogEventInfo(LogLevel.Trace, _comLogger.Name, "CommunicationLogger is logging");
logEvent.Properties["CallInformation"] = callInfo;
_comLogger.Log(logEvent);

And then specific the special attribute in the Format-option:

<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${event-properties:item=CallInformation:format=@}" />

See also: https://github.com/NLog/NLog/wiki/How-to-use-structured-logging#output-captured-properties

P.S. Instead of using @ (Activates NLog own serializer) then you could also just override ToString() for your callInfo-object and then perform custom serialization there. Yet another alternative is making the callInfo-object implement IFormattable and provide a custom IFormatProvider with the LogEventInfo.

Rolf Kristensen
  • 17,785
  • 1
  • 51
  • 70
  • 1
    I changed the layout to layout="${event-properties:item=CallInformation:format=@}" but I get no output at all. There is also no errors in the nlog-internal.log. – Banshee May 13 '20 at 07:04
  • Yes, I also thought the ToString solution as a last resort but thia would mean that I can´t control by the nlog.config what to show, it will be all or nothing from the CallInformation object. From what I unstrand I will otherwize be able to point out what part of the CallInformation I want to print to the target. – Banshee May 13 '20 at 07:05
  • Pleas, see Edit 2. It do not want to serialize this simple object at all it seems. – Banshee May 13 '20 at 07:14
  • 1
    @Banshee Looks like you have disabled message-template-parsing. Also notice you have written `${event-properties:item=CallInformation:format@}` and not the specified `${event-properties:item=CallInformation:format=@}` (Missing `=`) – Rolf Kristensen May 13 '20 at 17:17
  • 1
    @Banshee This log-statement works for me: `logger.Log(NLog.LogLevel.Warn, "Test to log {@CallInformation}", new { Hello = "World" });` So I guess you have overridden the IValueFormatter or IJsonConverter with something that throws exceptions (Or disabled MessageTemplateParsing) – Rolf Kristensen May 13 '20 at 17:23