1
+ // Copyright 2016 Serilog Contributors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ using Serilog . Events ;
16
+ using Serilog . Formatting ;
17
+ using Serilog . Formatting . Json ;
18
+ using Serilog . Parsing ;
19
+ using System ;
20
+ using System . Globalization ;
21
+ using System . IO ;
22
+ using System . Linq ;
23
+
24
+ namespace Serilog . Sinks . Splunk
25
+ {
26
+ /// <summary>
27
+ /// Renders log events into a Compact JSON format for consumption by Splunk.
28
+ /// </summary>
29
+ public class CompactSplunkJsonFormatter : ITextFormatter
30
+ {
31
+ private static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter ( typeTagName : "$type" ) ;
32
+ private readonly string _suffix ;
33
+ private readonly bool _renderTemplate ;
34
+
35
+ /// <summary>
36
+ /// Construct a <see cref="CompactSplunkJsonFormatter"/>.
37
+ /// </summary>
38
+ /// <param name="source">The source of the event</param>
39
+ /// <param name="sourceType">The source type of the event</param>
40
+ /// <param name="host">The host of the event</param>
41
+ /// <param name="index">The Splunk index to log to</param>
42
+ /// <param name="renderTemplate">If true, the template used will be rendered and written to the output as a property named MessageTemplate</param>
43
+ public CompactSplunkJsonFormatter ( bool renderTemplate = false , string source = null , string sourceType = null , string host = null , string index = null )
44
+ {
45
+ _renderTemplate = renderTemplate ;
46
+ var suffixWriter = new StringWriter ( ) ;
47
+ suffixWriter . Write ( "}" ) ; // Terminates "event"
48
+
49
+ if ( ! string . IsNullOrWhiteSpace ( source ) )
50
+ {
51
+ suffixWriter . Write ( ",\" source\" :" ) ;
52
+ JsonValueFormatter . WriteQuotedJsonString ( source , suffixWriter ) ;
53
+ }
54
+
55
+ if ( ! string . IsNullOrWhiteSpace ( sourceType ) )
56
+ {
57
+ suffixWriter . Write ( ",\" sourcetype\" :" ) ;
58
+ JsonValueFormatter . WriteQuotedJsonString ( sourceType , suffixWriter ) ;
59
+ }
60
+
61
+ if ( ! string . IsNullOrWhiteSpace ( host ) )
62
+ {
63
+ suffixWriter . Write ( ",\" host\" :" ) ;
64
+ JsonValueFormatter . WriteQuotedJsonString ( host , suffixWriter ) ;
65
+ }
66
+
67
+ if ( ! string . IsNullOrWhiteSpace ( index ) )
68
+ {
69
+ suffixWriter . Write ( ",\" index\" :" ) ;
70
+ JsonValueFormatter . WriteQuotedJsonString ( index , suffixWriter ) ;
71
+ }
72
+ suffixWriter . Write ( '}' ) ; // Terminates the payload
73
+ _suffix = suffixWriter . ToString ( ) ;
74
+ }
75
+
76
+ /// <inheritdoc/>
77
+ public void Format ( LogEvent logEvent , TextWriter output )
78
+ {
79
+ if ( logEvent == null ) throw new ArgumentNullException ( nameof ( logEvent ) ) ;
80
+ if ( output == null ) throw new ArgumentNullException ( nameof ( output ) ) ;
81
+
82
+ output . Write ( "{\" time\" :\" " ) ;
83
+ output . Write ( logEvent . Timestamp . ToEpoch ( ) . ToString ( CultureInfo . InvariantCulture ) ) ;
84
+ output . Write ( "\" ,\" event\" :{\" @l\" :\" " ) ;
85
+ output . Write ( logEvent . Level ) ;
86
+ output . Write ( '"' ) ;
87
+
88
+ if ( _renderTemplate )
89
+ {
90
+ output . Write ( ",\" @mt\" :" ) ;
91
+ JsonValueFormatter . WriteQuotedJsonString ( logEvent . MessageTemplate . Text , output ) ;
92
+
93
+ var tokensWithFormat = logEvent . MessageTemplate . Tokens
94
+ . OfType < PropertyToken > ( )
95
+ . Where ( pt => pt . Format != null ) ;
96
+
97
+ // Better not to allocate an array in the 99.9% of cases where this is false
98
+ // ReSharper disable once PossibleMultipleEnumeration
99
+ if ( tokensWithFormat . Any ( ) )
100
+ {
101
+ output . Write ( ",\" @r\" :[" ) ;
102
+ var delim = "" ;
103
+ foreach ( var r in tokensWithFormat )
104
+ {
105
+ output . Write ( delim ) ;
106
+ delim = "," ;
107
+ var space = new StringWriter ( ) ;
108
+ r . Render ( logEvent . Properties , space ) ;
109
+ JsonValueFormatter . WriteQuotedJsonString ( space . ToString ( ) , output ) ;
110
+ }
111
+ output . Write ( ']' ) ;
112
+ }
113
+ }
114
+ if ( logEvent . Exception != null )
115
+ {
116
+ output . Write ( ",\" @x\" :" ) ;
117
+ JsonValueFormatter . WriteQuotedJsonString ( logEvent . Exception . ToString ( ) , output ) ;
118
+ }
119
+
120
+ foreach ( var property in logEvent . Properties )
121
+ {
122
+ var name = property . Key ;
123
+ if ( name . Length > 0 && name [ 0 ] == '@' )
124
+ {
125
+ // Escape first '@' by doubling
126
+ name = '@' + name ;
127
+ }
128
+
129
+ output . Write ( ',' ) ;
130
+ JsonValueFormatter . WriteQuotedJsonString ( name , output ) ;
131
+ output . Write ( ':' ) ;
132
+ ValueFormatter . Format ( property . Value , output ) ;
133
+ }
134
+ output . WriteLine ( _suffix ) ;
135
+ }
136
+ }
137
+ }
0 commit comments