Skip to content

Instantly share code, notes, and snippets.

@rasmuskl
Last active March 16, 2021 21:57
Show Gist options
  • Save rasmuskl/5b0eab54aa56edbfec57c35c5fa100ab to your computer and use it in GitHub Desktop.
Save rasmuskl/5b0eab54aa56edbfec57c35c5fa100ab to your computer and use it in GitHub Desktop.
Json.NET JsonConverters for deserializing Serilog's LogEvents.
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog.Events;
using Serilog.Parsing;
namespace MyNamespace
{
public class LogEventJsonConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
DateTimeOffset timestamp = serializer.Deserialize<DateTimeOffset>(jObject["Timestamp"].CreateReader());
LogEventLevel level = serializer.Deserialize<LogEventLevel>(jObject["Level"].CreateReader());
Exception exception = serializer.Deserialize<Exception>(jObject["Exception"].CreateReader());
string messageTemplateText = jObject["MessageTemplate"]["Text"].Value<string>();
MessageTemplateParser messageTemplateParser = new MessageTemplateParser();
MessageTemplate messageTemplate = messageTemplateParser.Parse(messageTemplateText);
Dictionary<string, LogEventPropertyValue> logEventPropertyValues = serializer.Deserialize<Dictionary<string, LogEventPropertyValue>>(jObject["Properties"].CreateReader());
return new LogEvent(timestamp, level, exception, messageTemplate, logEventPropertyValues.Select(x => new LogEventProperty(x.Key, x.Value)));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(LogEvent).IsAssignableFrom(objectType);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NUnit.Framework;
using Serilog.Events;
using Serilog.Parsing;
namespace MyNamespace
{
[TestFixture]
public class LogEventJsonConverterTests
{
[Test]
public void Roundtrip_Timestamp()
{
DateTimeOffset timestamp = DateTimeOffset.Now;
LogEvent logEvent = RoundtripLogEvent(new LogEvent(timestamp, LogEventLevel.Fatal, null, new MessageTemplateParser().Parse("Test"), new LogEventProperty[0]));
Assert.AreEqual(timestamp, logEvent.Timestamp);
}
[Test]
public void Roundtrip_Level()
{
var level = LogEventLevel.Fatal;
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, level, null, new MessageTemplateParser().Parse("Test"), new LogEventProperty[0]));
Assert.AreEqual(level, logEvent.Level);
}
[Test]
public void Roundtrip_MessageTemplate_Simple()
{
MessageTemplate messageTemplate = new MessageTemplateParser().Parse("Test");
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Fatal, null, messageTemplate, new LogEventProperty[0]));
Assert.AreEqual(messageTemplate.Text, logEvent.MessageTemplate.Text);
}
[Test]
public void Roundtrip_MessageTemplate_Properties()
{
MessageTemplate messageTemplate = new MessageTemplateParser().Parse("Test {key} and {value}");
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Fatal, null, messageTemplate, new LogEventProperty[0]));
Assert.AreEqual(messageTemplate.Text, logEvent.MessageTemplate.Text);
Assert.AreEqual(2, messageTemplate.Tokens.OfType<TextToken>().Count());
Assert.AreEqual(2, messageTemplate.Tokens.OfType<PropertyToken>().Count());
}
[Test]
public void Roundtrip_LogEventProperties_Scalar()
{
var logEventProperties = new List<LogEventProperty>();
logEventProperties.Add(new LogEventProperty("scalar", new ScalarValue(42)));
logEventProperties.Add(new LogEventProperty("dict", new DictionaryValue(new List<KeyValuePair<ScalarValue, LogEventPropertyValue>> { new KeyValuePair<ScalarValue, LogEventPropertyValue>(new ScalarValue(42), new ScalarValue(43)) })));
logEventProperties.Add(new LogEventProperty("struct", new StructureValue(new[] { new LogEventProperty("struct-1", new ScalarValue(5)) })));
logEventProperties.Add(new LogEventProperty("seq", new SequenceValue(new[] { new ScalarValue(37) })));
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, new MessageTemplateParser().Parse("Test"), logEventProperties));
LogEventPropertyValue value;
Assert.IsTrue(logEvent.Properties.TryGetValue("scalar", out value));
Assert.IsAssignableFrom<ScalarValue>(value);
Assert.AreEqual(42, ((ScalarValue)value).Value);
}
[Test]
public void Roundtrip_LogEventProperties_Dictionary()
{
var logEventProperties = new List<LogEventProperty>();
logEventProperties.Add(new LogEventProperty("dict", new DictionaryValue(new List<KeyValuePair<ScalarValue, LogEventPropertyValue>> { new KeyValuePair<ScalarValue, LogEventPropertyValue>(new ScalarValue(92), new ScalarValue("value")) })));
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, new MessageTemplateParser().Parse("Test"), logEventProperties));
LogEventPropertyValue value;
Assert.IsTrue(logEvent.Properties.TryGetValue("dict", out value));
Assert.IsAssignableFrom<DictionaryValue>(value);
Assert.AreEqual("92", ((DictionaryValue)value).Elements.Keys.First().Value);
Assert.AreEqual("value", ((DictionaryValue)value).Elements.Values.OfType<ScalarValue>().First().Value);
}
[Test]
public void Roundtrip_LogEventProperties_Structure()
{
var logEventProperties = new List<LogEventProperty>();
logEventProperties.Add(new LogEventProperty("struct", new StructureValue(new[] { new LogEventProperty("struct-1", new ScalarValue(5)) }, "typeTag")));
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, new MessageTemplateParser().Parse("Test"), logEventProperties));
LogEventPropertyValue value;
Assert.IsTrue(logEvent.Properties.TryGetValue("struct", out value));
Assert.IsAssignableFrom<StructureValue>(value);
var structureValue = (StructureValue)value;
Assert.AreEqual("typeTag", structureValue.TypeTag);
LogEventProperty logEventProperty = structureValue.Properties.First();
Assert.AreEqual("struct-1", logEventProperty.Name);
Assert.AreEqual(5, ((ScalarValue)logEventProperty.Value).Value);
}
[Test]
public void Roundtrip_LogEventProperties_Structure_NoTypeTag()
{
var logEventProperties = new List<LogEventProperty>();
logEventProperties.Add(new LogEventProperty("struct", new StructureValue(new[] { new LogEventProperty("struct-1", new ScalarValue(5)) })));
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, new MessageTemplateParser().Parse("Test"), logEventProperties));
LogEventPropertyValue value;
Assert.IsTrue(logEvent.Properties.TryGetValue("struct", out value));
Assert.IsAssignableFrom<StructureValue>(value);
var structureValue = (StructureValue)value;
LogEventProperty logEventProperty = structureValue.Properties.First();
Assert.AreEqual("struct-1", logEventProperty.Name);
Assert.AreEqual(5, ((ScalarValue)logEventProperty.Value).Value);
}
[Test]
public void Roundtrip_LogEventProperties_Sequence()
{
var logEventProperties = new List<LogEventProperty>();
logEventProperties.Add(new LogEventProperty("seq", new SequenceValue(new[] { new ScalarValue(37) })));
LogEvent logEvent = RoundtripLogEvent(new LogEvent(DateTimeOffset.Now, LogEventLevel.Debug, null, new MessageTemplateParser().Parse("Test"), logEventProperties));
LogEventPropertyValue value;
Assert.IsTrue(logEvent.Properties.TryGetValue("seq", out value));
Assert.IsAssignableFrom<SequenceValue>(value);
Assert.AreEqual(37, ((SequenceValue)value).Elements.OfType<ScalarValue>().First().Value);
}
private LogEvent RoundtripLogEvent(LogEvent logEvent)
{
string json = JsonConvert.SerializeObject(logEvent);
return JsonConvert.DeserializeObject<LogEvent>(json, new LogEventJsonConverter(), new LogEventPropertyValueConverter());
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog.Events;
namespace MyNamespace
{
public class LogEventPropertyValueConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
if (FieldExists("Elements", jObject) && jObject["Elements"] is JArray)
{
var logEventPropertyValues = serializer.Deserialize<LogEventPropertyValue[]>(jObject["Elements"].CreateReader());
return new SequenceValue(logEventPropertyValues);
}
if (FieldExists("Elements", jObject) && jObject["Elements"] is JObject)
{
var dictionary = serializer.Deserialize<Dictionary<string, LogEventPropertyValue>>(jObject["Elements"].CreateReader());
return new DictionaryValue(dictionary.Select(x => new KeyValuePair<ScalarValue, LogEventPropertyValue>(new ScalarValue(x.Key), x.Value)));
}
if (FieldExists("Value", jObject))
{
return new ScalarValue(serializer.Deserialize(jObject["Value"].CreateReader()));
}
if (FieldExists("Properties", jObject))
{
var logEventProperties = serializer.Deserialize<LogEventProperty[]>(jObject["Properties"].CreateReader());
var typeTag = serializer.Deserialize<string>(jObject["TypeTag"].CreateReader());
return new StructureValue(logEventProperties, typeTag);
}
throw new NotSupportedException("Unknown LogEventPropertyValue: " + jObject);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(LogEventPropertyValue).IsAssignableFrom(objectType);
}
private bool FieldExists(string fieldName, JObject jObject)
{
return jObject[fieldName] != null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment