forked from afocus/log
-
Notifications
You must be signed in to change notification settings - Fork 0
/
log.go
342 lines (292 loc) · 7.32 KB
/
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
package log
import (
"crypto/rand"
"errors"
"fmt"
"io"
"runtime"
"sync"
"time"
"github.com/getsentry/raven-go"
)
// Level 日志级别类型
type Level int32
const (
// 日志级别 兼容Log4j
// 它假设级别是有序的。对于标准级别,其顺序为:DEBUG < INFO < WARN < ERROR < FATAL < OFF
// DEBUG 指明细致的事件信息,对调试应用最有用
DEBUG Level = iota
// INFO 指明描述信息,从粗粒度上描述了应用运行过程
INFO
// WARN 指明潜在的有害状况
WARN
// ERROR 指明错误事件,但应用可能还能继续运行
ERROR
// FATAL 指明非常严重的错误事件,可能会导致应用终止执行
FATAL
// OFF 最高级别,用于关闭日志
OFF
)
// String 返回日志等级的描述
func (l Level) String() string {
switch l {
case DEBUG:
return "DBUG"
case INFO:
return "INFO"
case WARN:
return "WARN"
case ERROR:
return "ERRO"
case FATAL:
return "FATA"
default:
return "UNKN"
}
}
// MarshalJSON 保证json编码level是不是显示数字而是string
func (l Level) MarshalJSON() ([]byte, error) {
return []byte(`"` + l.String() + `"`), nil
}
// TimestampLayout 日志时间格式化模板
var TimestampLayout = "2006-01-02 15:04:05"
// FormatPattern 扁平化格式化日志事件
var FormatPattern = func(ev *Event) []byte {
data := fmt.Sprintf(
"%s [%s] %s %s-%s → %s",
ev.Timestamp, ev.Level, ev.File, ev.ID, ev.Action, ev.Message,
)
d := []byte(data)
if length := len(d); d[length-1] == '\n' {
return d
}
return append(d, '\n')
}
// Formater 格式化日志事件到字符串
type Formater interface {
Format(*Event) []byte
}
// FormatWriter 格式化输入
// 用于定制日志输出内容的样式
type FormatWriter interface {
io.Writer
Formater
}
// Event 日志事件 记录了日志的必要信息
// 可以通过Formater接口输入格式化样式后的数据
type Event struct {
// 日志产生时的时间
Timestamp string
// 日志等级
Level Level
// 所在文件行数file:line
// main:20
File string
// 日志id 只有Ctx方式才会使用
// 主要用于上下文关联
ID string
// 日志动作名称 描述干什么的 如 login,callback...
Action string
// 日志内容
Message string
}
// CreateID 简单的返回一个随机字符串id
func CreateID() string {
x := make([]byte, 16)
io.ReadFull(rand.Reader, x)
return fmt.Sprintf("%x", x)
}
// Logger 日志对象
type Logger struct {
// 用于并发安全的锁
mu sync.Mutex
// 实现FormatWriter接口的输出对象
// 可以时多个输出对象 为了效率非必要尽量不要太多
outs []FormatWriter
// 日志等级限制
// 输入的等级>=限制才能输出
lvl Level
tracing *raven.Client
}
func New(lvl Level, raven *raven.Client, outs ...FormatWriter) *Logger {
return &Logger{
outs: outs,
lvl: lvl,
tracing: raven,
}
}
func (o *Logger) lockCall(f func()) {
o.mu.Lock()
f()
o.mu.Unlock()
}
var eventObjPool = &sync.Pool{New: func() interface{} { return new(Event) }}
// Output 输出日志消息
// 核心方法 所有日志输出全部以及此方法
func (o *Logger) Output(calldept int, level Level, acname, id, msg string) error {
// 等级不足以输出
if o.lvl > level {
return nil
}
if level == FATAL {
// 追加调用堆栈
for i := calldept; i < calldept+5; i++ {
_, file, line, ok := runtime.Caller(i)
if !ok {
break
}
msg += fmt.Sprintf("\n%s:%d", file, line)
}
}
// 捕获错误报告给Senty
switch level {
case WARN:
{
o.tracing.Capture(&raven.Packet{Message: msg}, nil)
break
}
case FATAL:
{
o.tracing.CaptureError(errors.New(msg), nil)
break
}
case ERROR:
{
o.tracing.CaptureError(errors.New(msg), nil)
break
}
}
// 从对象池中取出一个style对象并赋值
ev := eventObjPool.Get().(*Event)
ev.Timestamp = time.Now().Format(TimestampLayout)
ev.ID = id
ev.Level = level
ev.Action = acname
ev.Message = msg
// 获取所在文件以及行数
_, file, line, ok := runtime.Caller(calldept)
if !ok {
file = "???"
line = 0
}
length := len(file) - 1
for i := length; i > 0; i-- {
if file[i] == '/' {
file = file[i+1 : length-2]
break
}
}
ev.File = fmt.Sprintf("%s:%d", file, line)
// 保证并发安全
var err error
for _, out := range o.outs {
// Write 方法不要堵塞会
// 如何高性能杜绝或防止这里堵塞呢?todo
o.lockCall(func() {
out.Write(out.Format(ev))
})
if err != nil {
continue
}
}
// 放入对象池中
eventObjPool.Put(ev)
return err
}
// Debug
func (o *Logger) Debug(s ...interface{}) {
o.Output(2, DEBUG, "", "", fmt.Sprint(s...))
}
func (o *Logger) Info(s ...interface{}) {
o.Output(2, INFO, "", "", fmt.Sprint(s...))
}
func (o *Logger) Warn(s ...interface{}) {
o.Output(2, WARN, "", "", fmt.Sprint(s...))
}
func (o *Logger) Error(s ...interface{}) {
o.Output(2, ERROR, "", "", fmt.Sprint(s...))
}
func (o *Logger) Fatal(s ...interface{}) {
o.Output(2, FATAL, "", "", fmt.Sprint(s...))
}
// format
func (o *Logger) Debugf(s string, args ...interface{}) {
o.Output(2, DEBUG, "", "", fmt.Sprintf(s, args...))
}
func (o *Logger) Infof(s string, args ...interface{}) {
o.Output(2, INFO, "", "", fmt.Sprintf(s, args...))
}
func (o *Logger) Warnf(s string, args ...interface{}) {
o.Output(2, WARN, "", "", fmt.Sprintf(s, args...))
}
func (o *Logger) Errorf(s string, args ...interface{}) {
o.Output(2, ERROR, "", "", fmt.Sprintf(s, args...))
}
func (o *Logger) Fatalf(s string, args ...interface{}) {
o.Output(2, FATAL, "", "", fmt.Sprintf(s, args...))
}
// Ctx 携带日志id和事件名的日志对象
// 主要用于通过id串联一些日志 起到查询方便
type Ctx struct {
o *Logger
id, tag string
}
var ctxPool = sync.Pool{New: func() interface{} { return new(Ctx) }}
// Ctx 创建一个包含指定id的ctx对象
func (o *Logger) Ctx(id string) *Ctx {
ctx := ctxPool.Get().(*Ctx)
ctx.id = id
ctx.tag = ""
ctx.o = o
return ctx
}
// Tag 设置标签名
func (ctx *Ctx) Tag(tag string) *Ctx {
ctx.tag = tag
return ctx
}
// Free 释放
func (ctx *Ctx) Free() {
ctxPool.Put(ctx)
}
func (ctx *Ctx) Debug(s ...interface{}) *Ctx {
ctx.o.Output(2, DEBUG, ctx.tag, ctx.id, fmt.Sprint(s...))
return ctx
}
func (ctx *Ctx) Info(s ...interface{}) *Ctx {
ctx.o.Output(2, INFO, ctx.tag, ctx.id, fmt.Sprint(s...))
return ctx
}
func (ctx *Ctx) Warn(s ...interface{}) *Ctx {
ctx.o.Output(2, WARN, ctx.tag, ctx.id, fmt.Sprint(s...))
return ctx
}
func (ctx *Ctx) Error(s ...interface{}) *Ctx {
ctx.o.Output(2, ERROR, ctx.tag, ctx.id, fmt.Sprint(s...))
return ctx
}
func (ctx *Ctx) Fatal(s ...interface{}) *Ctx {
ctx.o.Output(2, FATAL, ctx.tag, ctx.id, fmt.Sprint(s...))
return ctx
}
//
func (ctx *Ctx) Debugf(s string, args ...interface{}) *Ctx {
ctx.o.Output(2, DEBUG, ctx.tag, ctx.id, fmt.Sprintf(s, args...))
return ctx
}
func (ctx *Ctx) Infof(s string, args ...interface{}) *Ctx {
ctx.o.Output(2, INFO, ctx.tag, ctx.id, fmt.Sprintf(s, args...))
return ctx
}
func (ctx *Ctx) Warnf(s string, args ...interface{}) *Ctx {
ctx.o.Output(2, WARN, ctx.tag, ctx.id, fmt.Sprintf(s, args...))
return ctx
}
func (ctx *Ctx) Errorf(s string, args ...interface{}) *Ctx {
ctx.o.Output(2, ERROR, ctx.tag, ctx.id, fmt.Sprintf(s, args...))
return ctx
}
func (ctx *Ctx) Fatalf(s string, args ...interface{}) *Ctx {
ctx.o.Output(2, FATAL, ctx.tag, ctx.id, fmt.Sprintf(s, args...))
return ctx
}