-
Notifications
You must be signed in to change notification settings - Fork 19
/
mlog.go
282 lines (223 loc) · 5.93 KB
/
mlog.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
// Package mlog is simple logging module for go, with a rotating file feature
// and console logging.
package mlog
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"sync/atomic"
)
// LogLevel type
type LogLevel int32
const (
// LevelTrace logs everything
LevelTrace LogLevel = (1 << iota)
// LevelInfo logs Info, Warnings and Errors
LevelInfo
// LevelWarn logs Warning and Errors
LevelWarn
// LevelError logs just Errors
LevelError
)
const MaxBytes int = 10 * 1024 * 1024
const BackupCount int = 10
type mlog struct {
LogLevel int32
Trace *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
Fatal *log.Logger
LogFile *RotatingFileHandler
}
var Logger mlog
// DefaultFlags used by created loggers
var DefaultFlags = log.Ldate | log.Ltime | log.Lshortfile
//RotatingFileHandler writes log a file, if file size exceeds maxBytes,
//it will backup current file and open a new one.
//
//max backup file number is set by backupCount, it will delete oldest if backups too many.
type RotatingFileHandler struct {
fd *os.File
fileName string
maxBytes int
backupCount int
}
// NewRotatingFileHandler creates dirs and opens the logfile
func NewRotatingFileHandler(fileName string, maxBytes int, backupCount int) (*RotatingFileHandler, error) {
dir := path.Dir(fileName)
os.Mkdir(dir, 0777)
h := new(RotatingFileHandler)
if maxBytes <= 0 {
return nil, fmt.Errorf("invalid max bytes")
}
h.fileName = fileName
h.maxBytes = maxBytes
h.backupCount = backupCount
var err error
h.fd, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, err
}
return h, nil
}
func (h *RotatingFileHandler) Write(p []byte) (n int, err error) {
h.doRollover()
return h.fd.Write(p)
}
// Close simply closes the File
func (h *RotatingFileHandler) Close() error {
if h.fd != nil {
return h.fd.Close()
}
return nil
}
func (h *RotatingFileHandler) doRollover() {
f, err := h.fd.Stat()
if err != nil {
return
}
// log.Println("size: ", f.Size())
if h.maxBytes <= 0 {
return
} else if f.Size() < int64(h.maxBytes) {
return
}
if h.backupCount > 0 {
h.fd.Close()
for i := h.backupCount - 1; i > 0; i-- {
sfn := fmt.Sprintf("%s.%d", h.fileName, i)
dfn := fmt.Sprintf("%s.%d", h.fileName, i+1)
os.Rename(sfn, dfn)
}
dfn := fmt.Sprintf("%s.1", h.fileName)
os.Rename(h.fileName, dfn)
h.fd, _ = os.OpenFile(h.fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
}
}
// Start starts the logging
func Start(level LogLevel, path string) {
doLogging(level, path, MaxBytes, BackupCount)
}
func StartEx(level LogLevel, path string, maxBytes, backupCount int) {
doLogging(level, path, maxBytes, backupCount)
}
// Stop stops the logging
func Stop() error {
if Logger.LogFile != nil {
return Logger.LogFile.Close()
}
return nil
}
//Sync commits the current contents of the file to stable storage.
//Typically, this means flushing the file system's in-memory copy
//of recently written data to disk.
func Sync() {
if Logger.LogFile != nil {
Logger.LogFile.fd.Sync()
}
}
func doLogging(logLevel LogLevel, fileName string, maxBytes, backupCount int) {
traceHandle := ioutil.Discard
infoHandle := ioutil.Discard
warnHandle := ioutil.Discard
errorHandle := ioutil.Discard
fatalHandle := ioutil.Discard
var fileHandle *RotatingFileHandler
switch logLevel {
case LevelTrace:
traceHandle = os.Stdout
fallthrough
case LevelInfo:
infoHandle = os.Stdout
fallthrough
case LevelWarn:
warnHandle = os.Stdout
fallthrough
case LevelError:
errorHandle = os.Stderr
fatalHandle = os.Stderr
}
if fileName != "" {
var err error
fileHandle, err = NewRotatingFileHandler(fileName, maxBytes, backupCount)
if err != nil {
log.Fatal("mlog: unable to create RotatingFileHandler: ", err)
}
if traceHandle == os.Stdout {
traceHandle = io.MultiWriter(fileHandle, traceHandle)
}
if infoHandle == os.Stdout {
infoHandle = io.MultiWriter(fileHandle, infoHandle)
}
if warnHandle == os.Stdout {
warnHandle = io.MultiWriter(fileHandle, warnHandle)
}
if errorHandle == os.Stderr {
errorHandle = io.MultiWriter(fileHandle, errorHandle)
}
if fatalHandle == os.Stderr {
fatalHandle = io.MultiWriter(fileHandle, fatalHandle)
}
}
Logger = mlog{
Trace: log.New(traceHandle, "T: ", DefaultFlags),
Info: log.New(infoHandle, "I: ", DefaultFlags),
Warning: log.New(warnHandle, "W: ", DefaultFlags),
Error: log.New(errorHandle, "E: ", DefaultFlags),
Fatal: log.New(errorHandle, "F: ", DefaultFlags),
LogFile: fileHandle,
}
atomic.StoreInt32(&Logger.LogLevel, int32(logLevel))
}
//** TRACE
// Trace writes to the Trace destination
func Trace(format string, a ...interface{}) {
Logger.Trace.Output(2, fmt.Sprintf(format, a...))
}
//** INFO
// Info writes to the Info destination
func Info(format string, a ...interface{}) {
Logger.Info.Output(2, fmt.Sprintf(format, a...))
}
//** WARNING
// Warning writes to the Warning destination
func Warning(format string, a ...interface{}) {
Logger.Warning.Output(2, fmt.Sprintf(format, a...))
}
//** ERROR
// Error writes to the Error destination and accepts an err
func Error(err error) {
Logger.Error.Output(2, fmt.Sprintf("%s\n", err))
}
// IfError is a shortcut function for log.Error if error
func IfError(err error) {
if err != nil {
Logger.Error.Output(2, fmt.Sprintf("%s\n", err))
}
}
//** FATAL
// Fatal writes to the Fatal destination and exits with an error 255 code
func Fatal(a ...interface{}) {
Logger.Fatal.Output(2, fmt.Sprint(a...))
Sync()
os.Exit(255)
}
// Fatalf writes to the Fatal destination and exits with an error 255 code
func Fatalf(format string, a ...interface{}) {
Logger.Fatal.Output(2, fmt.Sprintf(format, a...))
Sync()
os.Exit(255)
}
// FatalIfError is a shortcut function for log.Fatalf if error and
// exits with an error 255 code
func FatalIfError(err error) {
if err != nil {
Logger.Fatal.Output(2, fmt.Sprintf("%s\n", err))
Sync()
os.Exit(255)
}
}