/
heka_log.go
206 lines (184 loc) · 5.28 KB
/
heka_log.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package util
import (
"code.google.com/p/go-uuid/uuid"
"github.com/mozilla-services/heka/client"
"github.com/mozilla-services/heka/message"
"fmt"
"log"
"os"
"runtime"
"runtime/debug"
"strconv"
"strings"
"time"
)
type HekaLogger struct {
client client.Client
encoder client.Encoder
sender client.Sender
logname string
pid int32
hostname string
conf *MzConfig
tracer bool
filter int64
}
// Message levels
const (
CRITICAL = iota
ERROR
WARNING
INFO
DEBUG
)
// The fields to relay. NOTE: object reflection is VERY CPU expensive.
// I specify strings here to reduce that as much as possible. Please do
// not change this to something like map[string]interface{} since that
// can dramatically increase server load.
type Fields map[string]string
// Create a new Heka logging interface.
func NewHekaLogger(conf *MzConfig) *HekaLogger {
//Preflight
var encoder client.Encoder = nil
var sender client.Sender = nil
var logname string = ""
var err error
var tracer bool = false
var filter int64
pid := int32(os.Getpid())
dhost, _ := os.Hostname()
conf.SetDefaultFlag("heka.show_caller", false)
conf.SetDefault("logger.filter", "10")
filter, _ = strconv.ParseInt(conf.Get("logger.filter", "10"), 0, 0)
if conf.GetFlag("heka.use") {
encoder = client.NewProtobufEncoder(nil)
sender, err = client.NewNetworkSender(conf.Get("heka.sender", "tcp"),
conf.Get("heka.server_addr", "127.0.0.1:5565"))
if err != nil {
log.Panic("Could not create sender ", err)
}
logname = conf.Get("heka.logger_name", "package")
}
return &HekaLogger{encoder: encoder,
sender: sender,
logname: logname,
pid: pid,
hostname: conf.Get("heka.current_host", dhost),
conf: conf,
tracer: tracer,
filter: filter}
}
// Fields are additional logging data passed to Heka. They are technically
// undefined, but searchable and actionable.
func addFields(msg *message.Message, fields Fields) (err error) {
for key, ival := range fields {
var field *message.Field
if ival == "" {
ival = "*empty*"
}
if key == "" {
continue
}
field, err = message.NewField(key, ival, ival)
if err != nil {
return err
}
msg.AddField(field)
}
return err
}
// Logging workhorse function. Chances are you're not going to call this
// directly, but via one of the helper methods. of Info() .. Critical()
// level - One of the defined logging CONST values
// mtype - Message type, Short class identifier for the message
// payload - Main error message
// fields - additional optional key/value data associated with the message.
func (self HekaLogger) Log(level int32, mtype, payload string, fields Fields) (err error) {
var caller Fields
// add in go language tracing. (Also CPU intensive, but REALLY helpful
// when dev/debugging)
if self.tracer {
if pc, file, line, ok := runtime.Caller(2); ok {
funk := runtime.FuncForPC(pc)
caller = Fields{
"file": file,
// defaults don't appear to work.: file,
"line": strconv.FormatInt(int64(line), 0),
"name": funk.Name()}
}
}
// Only print out the debug message if it's less than the filter.
if int64(level) < self.filter {
dump := fmt.Sprintf("[%d]% 7s: %s", level, mtype, payload)
if len(fields) > 0 {
var fld []string
for key, val := range fields {
fld = append(fld, key+": "+val)
}
dump += " {" + strings.Join(fld, ", ") + "}"
}
if len(caller) > 0 {
dump += fmt.Sprintf(" [%s:%d %s]", caller["file"],
caller["line"], caller["name"])
}
log.Printf(dump)
// Don't send an error if there's nothing to do
if self.sender == nil {
return nil
}
var stream []byte
msg := &message.Message{}
msg.SetTimestamp(time.Now().UnixNano())
msg.SetUuid(uuid.NewRandom())
msg.SetLogger(self.logname)
msg.SetType(mtype)
msg.SetPid(self.pid)
msg.SetSeverity(level)
msg.SetHostname(self.hostname)
if len(payload) > 0 {
msg.SetPayload(payload)
}
err = addFields(msg, fields)
if err != nil {
return err
}
err = addFields(msg, caller)
if err != nil {
return err
}
err = self.encoder.EncodeMessageStream(msg, &stream)
if err != nil {
log.Fatal("ERROR: Could not encode log message (%s)", err)
return err
}
err = self.sender.SendMessage(stream)
if err != nil {
log.Fatal("ERROR: Could not send message (%s)", err)
return err
}
}
return nil
}
// record the lowest priority message
func (self HekaLogger) Info(mtype, msg string, fields Fields) (err error) {
return self.Log(INFO, mtype, msg, fields)
}
func (self HekaLogger) Debug(mtype, msg string, fields Fields) (err error) {
return self.Log(DEBUG, mtype, msg, fields)
}
func (self HekaLogger) Warn(mtype, msg string, fields Fields) (err error) {
return self.Log(WARNING, mtype, msg, fields)
}
func (self HekaLogger) Error(mtype, msg string, fields Fields) (err error) {
return self.Log(ERROR, mtype, msg, fields)
}
// record the Highest priority message, and include a printstack to STDERR
func (self HekaLogger) Critical(mtype, msg string, fields Fields) (err error) {
debug.PrintStack()
return self.Log(CRITICAL, mtype, msg, fields)
}
// o4fs
// vim: set tabstab=4 softtabstop=4 shiftwidth=4 noexpandtab