-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
MetadataTextFormatter.cs
155 lines (126 loc) · 5.2 KB
/
MetadataTextFormatter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Serilog.Debugging;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Json;
using Serilog.Parsing;
namespace SerilogExample
{
public class MetadataTextFormatter : ITextFormatter
{
private static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter();
/// <summary>
/// Format the log event into the output.
/// </summary>
/// <param name="logEvent">The event to format.</param>
/// <param name="output">The output.</param>
public void Format(LogEvent logEvent, TextWriter output)
{
try
{
var buffer = new StringWriter();
FormatContent(logEvent, buffer);
// If formatting was successful, write to output
output.WriteLine(buffer.ToString());
}
catch (Exception e)
{
LogNonFormattableEvent(logEvent, e);
}
}
private void FormatContent(LogEvent logEvent, TextWriter output)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
if (output == null) throw new ArgumentNullException(nameof(output));
output.Write("{\"Timestamp\":\"");
output.Write(logEvent.Timestamp.ToString("o"));
output.Write("\",\"Level\":\"");
output.Write(logEvent.Level);
output.Write("\",\"MessageTemplate\":");
JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Text, output);
output.Write(",\"RenderedMessage\":");
var message = logEvent.MessageTemplate.Render(logEvent.Properties);
JsonValueFormatter.WriteQuotedJsonString(message, output);
if (logEvent.Exception != null)
{
output.Write(",\"Exception\":");
JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output);
}
if (logEvent.Properties.Count != 0)
{
WriteProperties(logEvent.Properties, output);
}
// Better not to allocate an array in the 99.9% of cases where this is false
var tokensWithFormat = logEvent.MessageTemplate.Tokens
.OfType<PropertyToken>()
.Where(pt => pt.Format != null);
// ReSharper disable once PossibleMultipleEnumeration
if (tokensWithFormat.Any())
{
// ReSharper disable once PossibleMultipleEnumeration
WriteRenderings(tokensWithFormat.GroupBy(pt => pt.PropertyName), logEvent.Properties, output);
}
// Add a @metadata property that will be used by Logstash to separate logs from
// multiple applications
output.Write(", \"@metadata\":{\"app\":\"my-app\",\"version\":\"1.2.3\"}");
output.Write('}');
}
private static void WriteProperties(
IReadOnlyDictionary<string, LogEventPropertyValue> properties,
TextWriter output)
{
output.Write(",\"Properties\":{");
var precedingDelimiter = "";
foreach (var property in properties)
{
output.Write(precedingDelimiter);
precedingDelimiter = ",";
JsonValueFormatter.WriteQuotedJsonString(property.Key, output);
output.Write(':');
ValueFormatter.Format(property.Value, output);
}
output.Write('}');
}
private static void WriteRenderings(
IEnumerable<IGrouping<string, PropertyToken>> tokensWithFormat,
IReadOnlyDictionary<string, LogEventPropertyValue> properties,
TextWriter output)
{
output.Write(",\"Renderings\":{");
var rdelim = "";
foreach (var ptoken in tokensWithFormat)
{
output.Write(rdelim);
rdelim = ",";
JsonValueFormatter.WriteQuotedJsonString(ptoken.Key, output);
output.Write(":[");
var fdelim = "";
foreach (var format in ptoken)
{
output.Write(fdelim);
fdelim = ",";
output.Write("{\"Format\":");
JsonValueFormatter.WriteQuotedJsonString(format.Format, output);
output.Write(",\"Rendering\":");
var sw = new StringWriter();
format.Render(properties, sw);
JsonValueFormatter.WriteQuotedJsonString(sw.ToString(), output);
output.Write('}');
}
output.Write(']');
}
output.Write('}');
}
private static void LogNonFormattableEvent(LogEvent logEvent, Exception e)
{
SelfLog.WriteLine(
"Event at {0} with message template {1} could not be formatted into JSON and will be dropped: {2}",
logEvent.Timestamp.ToString("o"),
logEvent.MessageTemplate.Text,
e);
}
}
}