serialize objects or collections to log
/**
* class used to optimize loggers
*
* Logger.Trace("info "+bigData.ToString());
* Logger.Trace("info {0}",bigData.ToString());
* both creates and parses bigData even if Trace is disabled
*
* Logger.Trace("info {0}", LazyJsonizer.Create(bigData));
* Logger.Trace(LazyJsonizer.Instance, "info {0}", bigData);
* creates string only if Trace is enabled
*
* http://stackoverflow.com/questions/23007377/nlog-serialize-objects-or-collections-to-log
*/
public class LazyJsonizer<T>
{
T Value;
public LazyJsonizer(T value)
{
Value = value;
}
override public string ToString()
{
return LazyJsonizer.Instance.Format(null, Value, null);
}
}
public class LazyJsonizer : IFormatProvider, ICustomFormatter
{
static public readonly LazyJsonizer Instance = new LazyJsonizer();
static public LazyJsonizer<T> Create<T>(T value)
{
return new LazyJsonizer<T>(value);
}
public object GetFormat(Type formatType)
{
return this;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
try
{
return JsonConvert.SerializeObject(arg);
}
catch (Exception ex)
{
return ex.Message;
}
}
}
No, there is nothing built-in for serializing objects. When you use formatted methods like Debug<T>(string message, T argument)
internally (you can see class NLog.LogEventInfo
) simple String.Format
is used for creating formatted message (i.e. just ToString()
is called on every parameter).
I use Json.NET for serializing objects and collections to JSON. It's easy to create extension method like
public static string ToJson(this object value)
{
var settings = new JsonSerializerSettings {
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
return JsonConvert.SerializeObject(value, Formatting.Indented, settings);
}
And then use it during logging:
Logger.Debug("Saving person {0}", person.ToJson());
This simplified example shows what I came to after playing with NLog. In my solution I use code-based configuration in order to mitigate duplicate xml nlog.config files for every asp.net project. Works with NLog v4.4.1.
Logger static helper:
private static readonly Logger DefaultLogger = LogManager.GetLogger("Application");
public static void Debug(Exception exception = null, string message = null, object data = null)
=> Write(DefaultLogger, LogLevel.Debug, message, exception, data);
private static void Write(
Logger logger,
LogLevel level,
string message = null,
Exception exception = null,
object data = null)
{
var eventInfo = new LogEventInfo()
{
Level = level,
Message = message,
Exception = exception,
Parameters = new[] { data, tag }
};
if (data != null) eventInfo.Properties["data"] = data.ToJson();
eventInfo.Properties["level"] = eventInfo.GetLevelCode(); // custom level to int conversion
logger.Log(eventInfo);
}
FileTarget configuration:
var jsonFileTarget = new FileTarget()
{
Name = "file_json",
Layout = new JsonLayout()
{
Attributes =
{
new JsonAttribute("level", "${event-context:item=level}"),
new JsonAttribute("time", "${longdate}"),
new JsonAttribute("msg", "${message}"),
new JsonAttribute("error", "${exception:format=tostring}"),
new JsonAttribute("data", "${event-context:item=data}", false),
},
RenderEmptyObject = false,
},
FileName = $"{LogFile.Directory}/json_{LogFile.Suffix}", // use settings from static LogFile class
ArchiveFileName = $"{LogFile.Directory}/json_{LogFile.ArchiveSuffix}",
ArchiveAboveSize = LogFile.MaxSize
};
Output for custom object:
{ "level": "10", "time": "2017-02-02 16:24:52.3078", "data":{"method":"get","url":"http://localhost:44311/"}}