forked from golang/glog
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathglog_json.go
151 lines (137 loc) · 4.28 KB
/
glog_json.go
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
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
//
// Modifications copyright 2013 Ernest Micklei. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package glog
import (
"bytes"
"encoding/json"
"io"
)
/*
{
"@source_host":"test.here.com",
"@timestamp":"2013-10-24T09:30:46.947024155+02:00",
"@fields":{
"level":"INFO",
"threadid":"400004",
"file":"file.go",
"line":10
},
"@message":"hello"
}
*/
// glogJSON can decode a data slice generated by glog and encode it in logstash json format.
// https://gist.github.com/jordansissel/2996677
type glogJSON struct {
writer *bytes.Buffer // for the composition of one message.
encoder *json.Encoder // used to encode string parameters.
}
// WriteWithStack decodes the data and writes a logstash json event
func (d glogJSON) WriteWithStack(data []byte, stack []byte) {
d.openEvent()
// peek for normal logline
sev := data[0]
switch sev {
case 73, 87, 69, 70: // IWEF
d.iwef(sev, data, stack)
default:
d.message(string(data))
}
d.closeHash()
}
// openEvent writes the "header" part of the JSON message.
func (d glogJSON) openEvent() {
io.WriteString(d.writer, `{"@source_host":`)
d.encoder.Encode(host) // uses glog package var
io.WriteString(d.writer, `,"@timestamp":`)
// ignore time information given, take new snapshot
d.encoder.Encode(timeNow()) // use testable function stored in var
}
// closeAll writes the closing brackets for the main and fields hash.
func (d glogJSON) closeHash() {
io.WriteString(d.writer, "}\n")
}
// message adds a JSON field with the JSON encoded message.
func (d glogJSON) message(msg string) {
io.WriteString(d.writer, `,"@message":`)
d.encoder.Encode(msg)
}
// stack adds a JSON field with the JSON encoded stack trace of all goroutines.
func (d glogJSON) stacktrace(stacktrace []byte) {
io.WriteString(d.writer, `,"stack":`)
d.encoder.Encode(string(stacktrace))
}
// iwef decodes a glog data packet and write the JSON representation.
// [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
func (d glogJSON) iwef(sev byte, data []byte, trace []byte) {
io.WriteString(d.writer, `,"@fields":{"level":"`)
switch sev {
case 73:
io.WriteString(d.writer, "INFO")
case 87:
io.WriteString(d.writer, "WARNING")
case 69:
io.WriteString(d.writer, "ERROR")
case 70:
io.WriteString(d.writer, "FATAL")
}
r := &iwefreader{data, 22} // past last u
io.WriteString(d.writer, `","threadid":"`)
io.WriteString(d.writer, r.stringUpTo(32)) // space
r.skip() // space
io.WriteString(d.writer, `","file":"`)
io.WriteString(d.writer, r.stringUpTo(58)) // :
r.skip() // :
io.WriteString(d.writer, `","line":`)
io.WriteString(d.writer, r.stringUpTo(93)) // ]
// ]
r.skip()
// space
r.skip()
if trace != nil && len(trace) > 0 {
d.stacktrace(trace)
}
// extras?
for k, v := range ExtraFields {
io.WriteString(d.writer, `,"`)
io.WriteString(d.writer, k)
io.WriteString(d.writer, `":`)
d.encoder.Encode(v)
}
// fields
d.closeHash()
d.message(r.stringUpToLineEnd())
}
// iwefreader is a small helper object to parse a glog IWEF entry
type iwefreader struct {
data []byte
position int // read offset in data
}
// skip advances the position in data
func (i *iwefreader) skip() {
i.position++
}
// stringUpToLineEnd returns the string part from the data up to not-including the line end.
func (i iwefreader) stringUpToLineEnd() string {
return string(i.data[i.position : len(i.data)-1]) // without the line delimiter
}
// stringUpTo returns the string part from the data up to not-including a delimiter.
func (i *iwefreader) stringUpTo(delim byte) string {
start := i.position
for i.data[i.position] != delim {
i.position++
}
return string(i.data[start:i.position])
}