16
16
using System . Diagnostics ;
17
17
using System . Globalization ;
18
18
using System . IO ;
19
- using Newtonsoft . Json ;
20
- using Newtonsoft . Json . Linq ;
19
+ using System . Text . Json ;
21
20
using Serilog . Events ;
22
21
using Serilog . Parsing ;
23
22
using System . Linq ;
@@ -35,19 +34,16 @@ public class LogEventReader : IDisposable
35
34
static readonly MessageTemplateParser Parser = new MessageTemplateParser ( ) ;
36
35
static readonly Rendering [ ] NoRenderings = Array . Empty < Rendering > ( ) ;
37
36
readonly TextReader _text ;
38
- readonly JsonSerializer _serializer ;
39
37
40
38
int _lineNumber ;
41
39
42
40
/// <summary>
43
41
/// Construct a <see cref="LogEventReader"/>.
44
42
/// </summary>
45
43
/// <param name="text">Text to read from.</param>
46
- /// <param name="serializer">If specified, a JSON serializer used when converting event documents.</param>
47
- public LogEventReader ( TextReader text , JsonSerializer serializer = null )
44
+ public LogEventReader ( TextReader text )
48
45
{
49
46
_text = text ?? throw new ArgumentNullException ( nameof ( text ) ) ;
50
- _serializer = serializer ?? CreateSerializer ( ) ;
51
47
}
52
48
53
49
/// <inheritdoc/>
@@ -77,42 +73,60 @@ public bool TryRead(out LogEvent evt)
77
73
_lineNumber ++ ;
78
74
}
79
75
80
- var data = _serializer . Deserialize ( new JsonTextReader ( new StringReader ( line ) ) ) ;
81
- if ( ! ( data is JObject fields ) )
82
- throw new InvalidDataException ( $ "The data on line { _lineNumber } is not a complete JSON object.") ;
83
-
84
- evt = ReadFromJObject ( _lineNumber , fields ) ;
76
+ JsonDocument data = null ;
77
+ try
78
+ {
79
+ data = JsonDocument . Parse ( line ) ;
80
+ }
81
+ catch ( JsonException )
82
+ {
83
+ }
84
+ using ( data )
85
+ {
86
+ if ( data == null || data . RootElement . ValueKind != JsonValueKind . Object )
87
+ throw new InvalidDataException ( $ "The data on line { _lineNumber } is not a complete JSON object.") ;
88
+ evt = ReadFromJObject ( _lineNumber , data . RootElement ) ;
89
+ }
85
90
return true ;
86
91
}
87
92
93
+
88
94
/// <summary>
89
95
/// Read a single log event from a JSON-encoded document.
90
96
/// </summary>
91
97
/// <param name="document">The event in compact-JSON.</param>
92
- /// <param name="serializer">If specified, a JSON serializer used when converting event documents.</param>
93
98
/// <returns>The log event.</returns>
94
- public static LogEvent ReadFromString ( string document , JsonSerializer serializer = null )
99
+ public static LogEvent ReadFromString ( string document )
95
100
{
96
101
if ( document == null ) throw new ArgumentNullException ( nameof ( document ) ) ;
97
-
98
- serializer ??= CreateSerializer ( ) ;
99
- var jObject = serializer . Deserialize < JObject > ( new JsonTextReader ( new StringReader ( document ) ) ) ;
100
- return ReadFromJObject ( jObject ) ;
101
-
102
+ JsonDocument data = null ;
103
+ try
104
+ {
105
+ data = JsonDocument . Parse ( document ) ;
106
+ }
107
+ catch ( JsonException )
108
+ {
109
+ }
110
+ using ( data )
111
+ {
112
+ if ( data == null || data . RootElement . ValueKind != JsonValueKind . Object )
113
+ throw new ArgumentException ( $ "The document is not a complete JSON object.", nameof ( document ) ) ;
114
+ return ReadFromJObject ( data . RootElement ) ;
115
+ }
102
116
}
103
117
104
118
/// <summary>
105
119
/// Read a single log event from an already-deserialized JSON object.
106
120
/// </summary>
107
121
/// <param name="jObject">The deserialized compact-JSON event.</param>
108
122
/// <returns>The log event.</returns>
109
- public static LogEvent ReadFromJObject ( JObject jObject )
123
+ public static LogEvent ReadFromJObject ( in JsonElement jObject )
110
124
{
111
- if ( jObject == null ) throw new ArgumentNullException ( nameof ( jObject ) ) ;
125
+ if ( jObject . ValueKind != JsonValueKind . Object ) throw new ArgumentException ( nameof ( jObject ) ) ;
112
126
return ReadFromJObject ( 1 , jObject ) ;
113
127
}
114
128
115
- static LogEvent ReadFromJObject ( int lineNumber , JObject jObject )
129
+ static LogEvent ReadFromJObject ( int lineNumber , in JsonElement jObject )
116
130
{
117
131
var timestamp = GetRequiredTimestampField ( lineNumber , jObject , ClefFields . Timestamp ) ;
118
132
@@ -146,20 +160,20 @@ static LogEvent ReadFromJObject(int lineNumber, JObject jObject)
146
160
147
161
var renderings = NoRenderings ;
148
162
149
- if ( jObject . TryGetValue ( ClefFields . Renderings , out var r ) )
150
- {
151
- if ( ! ( r is JArray renderedByIndex ) )
163
+ if ( jObject . TryGetProperty ( ClefFields . Renderings , out var r ) )
164
+ {
165
+ if ( ! ( r . ValueKind == JsonValueKind . Array ) )
152
166
throw new InvalidDataException ( $ "The `{ ClefFields . Renderings } ` value on line { lineNumber } is not an array as expected.") ;
153
167
154
168
renderings = parsedTemplate . Tokens
155
169
. OfType < PropertyToken > ( )
156
170
. Where ( t => t . Format != null )
157
- . Zip ( renderedByIndex , ( t , rd ) => new Rendering ( t . PropertyName , t . Format , rd . Value < string > ( ) ) )
171
+ . Zip ( r . EnumerateArray ( ) , ( t , rd ) => new Rendering ( t . PropertyName , t . Format , rd . GetString ( ) ) )
158
172
. ToArray ( ) ;
159
173
}
160
174
161
175
var properties = jObject
162
- . Properties ( )
176
+ . EnumerateObject ( )
163
177
. Where ( f => ! ClefFields . All . Contains ( f . Name ) )
164
178
. Select ( f =>
165
179
{
@@ -177,71 +191,62 @@ static LogEvent ReadFromJObject(int lineNumber, JObject jObject)
177
191
return new LogEvent ( timestamp , level , exception , parsedTemplate , properties , traceId , spanId ) ;
178
192
}
179
193
180
- static bool TryGetOptionalField ( int lineNumber , JObject data , string field , out string value )
194
+ static bool TryGetOptionalField ( int lineNumber , in JsonElement data , string field , out string value )
181
195
{
182
- if ( ! data . TryGetValue ( field , out var token ) || token . Type == JTokenType . Null )
196
+ if ( ! data . TryGetProperty ( field , out var prop ) || prop . ValueKind == JsonValueKind . Null )
183
197
{
184
198
value = null ;
185
199
return false ;
186
200
}
187
201
188
- if ( token . Type != JTokenType . String )
202
+ if ( prop . ValueKind != JsonValueKind . String )
189
203
throw new InvalidDataException ( $ "The value of `{ field } ` on line { lineNumber } is not in a supported format.") ;
190
204
191
- value = token . Value < string > ( ) ;
205
+ value = prop . GetString ( ) ;
192
206
return true ;
193
207
}
194
208
195
- static bool TryGetOptionalEventId ( int lineNumber , JObject data , string field , out object eventId )
209
+ static bool TryGetOptionalEventId ( int lineNumber , in JsonElement data , string field , out object eventId )
196
210
{
197
- if ( ! data . TryGetValue ( field , out var token ) || token . Type == JTokenType . Null )
211
+ if ( ! data . TryGetProperty ( field , out var prop ) || prop . ValueKind == JsonValueKind . Null )
198
212
{
199
213
eventId = null ;
200
214
return false ;
201
215
}
202
216
203
- switch ( token . Type )
217
+ switch ( prop . ValueKind )
204
218
{
205
- case JTokenType . String :
206
- eventId = token . Value < string > ( ) ;
219
+ case JsonValueKind . String :
220
+ eventId = prop . GetString ( ) ;
207
221
return true ;
208
- case JTokenType . Integer :
209
- eventId = token . Value < uint > ( ) ;
210
- return true ;
211
- default :
212
- throw new InvalidDataException (
213
- $ "The value of `{ field } ` on line { lineNumber } is not in a supported format.") ;
222
+ case JsonValueKind . Number :
223
+ if ( prop . TryGetUInt32 ( out var v ) )
224
+ {
225
+ eventId = v ;
226
+ return true ;
227
+ }
228
+ break ;
214
229
}
230
+
231
+ throw new InvalidDataException (
232
+ $ "The value of `{ field } ` on line { lineNumber } is not in a supported format.") ;
215
233
}
216
234
217
- static DateTimeOffset GetRequiredTimestampField ( int lineNumber , JObject data , string field )
235
+ static DateTimeOffset GetRequiredTimestampField ( int lineNumber , in JsonElement data , string field )
218
236
{
219
- if ( ! data . TryGetValue ( field , out var token ) || token . Type == JTokenType . Null )
237
+ if ( ! data . TryGetProperty ( field , out var prop ) || prop . ValueKind == JsonValueKind . Null )
220
238
throw new InvalidDataException ( $ "The data on line { lineNumber } does not include the required `{ field } ` field.") ;
221
239
222
- if ( token . Type == JTokenType . Date )
223
- {
224
- var dt = token . Value < JValue > ( ) . Value ;
225
- if ( dt is DateTimeOffset offset )
226
- return offset ;
240
+ if ( prop . TryGetDateTimeOffset ( out var ret ) )
241
+ return ret ;
242
+ if ( prop . TryGetDateTime ( out var dt ) )
243
+ return dt ;
227
244
228
- return ( DateTime ) dt ! ;
229
- }
230
-
231
- if ( token . Type != JTokenType . String )
245
+ if ( prop . ValueKind != JsonValueKind . String )
232
246
throw new InvalidDataException ( $ "The value of `{ field } ` on line { lineNumber } is not in a supported format.") ;
233
247
234
- var text = token . Value < string > ( ) ;
248
+ var text = prop . GetString ( ) ;
235
249
return DateTimeOffset . Parse ( text ) ;
236
250
}
237
-
238
- static JsonSerializer CreateSerializer ( )
239
- {
240
- return JsonSerializer . Create ( new JsonSerializerSettings
241
- {
242
- DateParseHandling = DateParseHandling . None ,
243
- Culture = CultureInfo . InvariantCulture
244
- } ) ;
245
- }
246
251
}
247
252
}
0 commit comments