forked from heroku/rollrus
/
rollrus.go
119 lines (103 loc) · 2.99 KB
/
rollrus.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
package rollrus
import (
"fmt"
"os"
"time"
log "github.com/Sirupsen/logrus"
"github.com/stvp/roll"
)
var defaultTriggerLevels = []log.Level{
log.ErrorLevel,
log.FatalLevel,
log.PanicLevel,
}
// Hook wrapper for the rollbar Client
// May be used as a rollbar client itself
type Hook struct {
roll.Client
triggers []log.Level
}
// SetupLogging sets up logging. If token is not an empty string a rollbar
// hook is added with the environment set to env. The log formatter is set to a
// TextFormatter with timestamps disabled, which is suitable for use on Heroku.
func SetupLogging(token, env string) {
setupLogging(token, env, defaultTriggerLevels)
}
// SetupLoggingForLevels works like SetupLogging, but allows you to
// set the levels on which to trigger this hook.
func SetupLoggingForLevels(token, env string, levels []log.Level) {
setupLogging(token, env, levels)
}
func setupLogging(token, env string, levels []log.Level) {
log.SetFormatter(&log.TextFormatter{DisableTimestamp: true})
if token != "" {
log.AddHook(&Hook{Client: roll.New(token, env), triggers: levels})
}
}
// ReportPanic attempts to report the panic to rollbar using the provided
// client and then re-panic. If it can't report the panic it will print an
// error to stderr.
func (r *Hook) ReportPanic() {
if p := recover(); p != nil {
if _, err := r.Client.Critical(fmt.Errorf("panic: %q", p), nil); err != nil {
fmt.Fprintf(os.Stderr, "reporting_panic=false err=%q\n", err)
}
panic(p)
}
}
// ReportPanic attempts to report the panic to rollbar if the token is set
func ReportPanic(token, env string) {
if token != "" {
h := &Hook{Client: roll.New(token, env)}
h.ReportPanic()
}
}
// Fire the hook. This is called by Logrus for entries that match the levels
// returned by Levels(). See below.
func (r *Hook) Fire(entry *log.Entry) (err error) {
e := fmt.Errorf(entry.Message)
m := convertFields(entry.Data)
if _, exists := m["time"]; !exists {
m["time"] = entry.Time.Format(time.RFC3339)
}
switch entry.Level {
case log.FatalLevel, log.PanicLevel:
_, err = r.Client.Critical(e, m)
case log.ErrorLevel:
_, err = r.Client.Error(e, m)
case log.WarnLevel:
_, err = r.Client.Warning(e, m)
case log.InfoLevel:
_, err = r.Client.Info(entry.Message, m)
case log.DebugLevel:
_, err = r.Client.Debug(entry.Message, m)
default:
return fmt.Errorf("Unknown level: %s", entry.Level)
}
return err
}
// Levels returns the logrus log levels that this hook handles
func (r *Hook) Levels() []log.Level {
if r.triggers == nil {
return defaultTriggerLevels
}
return r.triggers
}
// convertFields converts from log.Fields to map[string]string so that we can
// report extra fields to Rollbar
func convertFields(fields log.Fields) map[string]string {
m := make(map[string]string)
for k, v := range fields {
switch t := v.(type) {
case time.Time:
m[k] = t.Format(time.RFC3339)
default:
if s, ok := v.(fmt.Stringer); ok {
m[k] = s.String()
} else {
m[k] = fmt.Sprintf("%+v", t)
}
}
}
return m
}