-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogger.go
executable file
·177 lines (150 loc) · 3.61 KB
/
logger.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
package logger
import (
"context"
"encoding/json"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
)
type tag string // a non-primitive to use as context keys
type internal string
// tags is the key for a []tag, used for contextual logging
const tagsKey = internal("tags")
// DEBUG is controlled by the LOG_LEVEL environment variable.
var DEBUG bool
var mu sync.Mutex
func SetDebug(val bool) {
mu.Lock()
DEBUG = val
mu.Unlock()
}
// Logger is the main type for the logger package.
type Logger struct {
tags tagList
}
// tagList contains metadata for the logger instance.
type tagList map[tag]interface{}
type message struct {
Level string `json:"level"`
EventTime string `json:"event_time"`
Message string `json:"message"`
Trace string `json:"trace,omitempty"`
Tags tagList `json:"tags,omitempty"`
}
func init() {
DEBUG = os.Getenv("DEBUG") == "TRUE"
}
// New instantiates and returns a Logger object
func New() *Logger {
return &Logger{
tags: make(tagList),
}
}
func stripPathPrefix(s string) string {
i := strings.LastIndex(s, "/")
if i > 0 {
s = s[i+1:]
}
return s
}
func stack() string {
names := make([]string, 0, 10)
pc := make([]uintptr, 100)
n := runtime.Callers(4, pc)
frames := runtime.CallersFrames(pc[:n])
for {
frame, more := frames.Next()
if frame.Function == "main.index" {
break
}
if strings.Contains(frame.File, "asm_amd64.s") {
break
}
path := fmt.Sprintf("%s:%s:%d", stripPathPrefix(frame.File), stripPathPrefix(frame.Function), frame.Line)
names = append(names, path)
if !more {
break
}
}
return strings.Join(names, ", ")
}
func (l *Logger) With(ctx context.Context, k string, v interface{}) context.Context {
// Add tag to logger.
l.tags[tag(k)] = v
ctx = context.WithValue(ctx, k, v)
// Add value to context.
tags := make([]tag, 0, len(l.tags))
for k, v := range l.tags {
tags = append(tags, k)
ctx = context.WithValue(ctx, k, v)
}
return context.WithValue(ctx, tagsKey, tags)
}
// FromContext returns a new *Logger, automatically
// adding tags from ctx if ctx contains a
// logger.Tags key with a value of []string.
func FromContext(ctx context.Context) *Logger {
log := New()
tags, ok := ctx.Value(tagsKey).([]tag)
if !ok {
return log
}
for _, t := range tags {
log.tags[t] = ctx.Value(t)
}
return log
}
func (l *Logger) ValueString(key string) string {
v, found := l.tags[tag(key)]
if !found {
return ""
}
return fmt.Sprintf("%s", v)
}
func (l *Logger) log(level, text string) {
msg := message{
Level: level,
EventTime: time.Now().UTC().Format(time.RFC3339),
Message: text,
Tags: l.tags,
}
if level == "ERROR" {
msg.Trace = stack()
}
b, err := json.Marshal(&msg)
if err != nil {
fmt.Printf("logger ERROR: cannot marshal payload: %s", err)
return
}
fmt.Fprintln(os.Stdout, string(b))
}
// Debug prints out a message with DEBUG level.
func (l Logger) Debug(message string) {
if !DEBUG {
return
}
l.log("DEBUG", message)
}
// Debugf prints out a message with DEBUG level.
func (l Logger) Debugf(message string, args ...interface{}) {
l.Debug(fmt.Sprintf(message, args...))
}
// Info prints out a message with INFO level.
func (l Logger) Info(message string) {
l.log("INFO", message)
}
// Infof prints out a message with INFO level.
func (l Logger) Infof(message string, args ...interface{}) {
l.Info(fmt.Sprintf(message, args...))
}
// Error prints out a message with ERROR level.
func (l Logger) Error(message string) {
l.log("ERROR", message)
}
// Errorf prints out a message with INFO level.
func (l Logger) Errorf(message string, args ...interface{}) {
l.Error(fmt.Sprintf(message, args...))
}