-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogger.go
205 lines (174 loc) · 5.29 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
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
/*
The realize of logger make other file easy to use.
There is initialization of logger instance.
It save the logs into local when connection to influxdb fail. when connection of influx recover, it will write back all logs.
Supply all function to log including debug, info, warn, error, fatal, panic.
*/
package utils
import (
"bufio"
"context"
"github.com/influxdata/influxdb-client-go/v2"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"path/filepath"
"strings"
"time"
)
var logger zerolog.Logger
// InitLogger is to initialize the logger make log be available
func InitLogger() {
// setting of the logger about size, backup, store days etc.
logFile := &lumberjack.Logger{
Filename: "logs/server.log",
MaxSize: 10,
MaxBackups: 3,
MaxAge: 30,
Compress: true,
}
// new a local logger instance
localLogger := zerolog.New(logFile).With().Timestamp().Logger()
// make logger equal to local logger at first
logger = localLogger
// set using influxdb mode to false at first
isUsingInfluxDB := false
influxHook := NewInfluxDBHook()
// start to try to connect influxdb
go func() {
// get the context and close when program turn off
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// make an infinite loop check and make connection to influxdb
for {
select {
case <-ctx.Done():
return
default:
// try to connect influxdb. if success to connect then replace logger else then turn back to local logger.
if result, err := influxHook.Client.Ping(ctx); result == true {
// if it is a new connection, it will write all formal logs to the influxdb.
if !isUsingInfluxDB {
logger = zerolog.New(nil).Hook(&influxHook)
isUsingInfluxDB = true
go uploadRotatedLogsToInfluxDB(influxHook.Client)
}
} else {
// turn back to local logger and log error to connect influxdb
if isUsingInfluxDB {
logger = localLogger
isUsingInfluxDB = false
}
LogError(err.Error())
influxHook = NewInfluxDBHook()
}
// sleep a minute and then try to connect again.
time.Sleep(time.Minute)
}
}
}()
}
// the function is to upload all local logs into influx db
func uploadRotatedLogsToInfluxDB(influxClient influxdb2.Client) {
// get the organization and bucket from environment
org := EnvInfluxDbOrg()
bucket := EnvInfluxDbBucket()
// make a write api to write the logs
writeAPI := influxClient.WriteAPIBlocking(org, bucket)
// get all local log files
files, err := filepath.Glob("logs/server.log*")
if err != nil {
LogError(err.Error())
}
// enumerate all files
for _, filename := range files {
// try to open the file. when open file fail, log error and continue.
file, err := os.Open(filename)
if err != nil {
LogError(err.Error())
continue
}
// new a scanner to read file
scanner := bufio.NewScanner(file)
for scanner.Scan() {
// get the line of content and get the level, time and message
line := scanner.Text()
level := getField(line, "level")
logTime := getField(line, "time")
logMessage := getField(line, "message")
// write the data back to the influxdb and it will log error when write error
point := influxdb2.NewPointWithMeasurement("logs").
AddTag("level", level).
AddField("message", logMessage).
SetTime(parseTime(logTime))
err := writeAPI.WritePoint(context.Background(), point)
if err != nil {
LogError(err.Error())
}
}
// close the log file. when fail to close it, program will log error.
err = file.Close()
if err != nil {
LogError(err.Error())
continue
}
// remove the log file. when fail to remove it, program will log error.
err = os.Remove(filename)
if err != nil {
LogError(err.Error())
}
}
}
// get the specific content in the log file's content
func getField(line, field string) string {
// when field not exist
if !strings.Contains(line, field) {
return ""
}
// get the start of the specific tag
start := strings.Index(line, `"`+field+`":"`) + len(field) + 4
// to process special tag case (the end of the line).
if field == "message" {
return line[start : len(line)-2]
}
// get the end of the tag
end := strings.Index(line[start:], `","`)
return line[start : start+end]
}
// parse the time in the log file's content
func parseTime(logTime string) time.Time {
// parse the time when fail then return time in now
parsedTime, err := time.Parse(time.RFC3339, logTime)
if err != nil {
return GetNow()
}
return parsedTime
}
/*
The following functions make an interface to log the information.
It will be different by the importance of the message using different function.
*/
// LogDebug is to log information when development
func LogDebug(msg string) {
logger.Debug().Msg(msg)
}
// LogInfo is to log basic information
func LogInfo(msg string) {
logger.Info().Msg(msg)
}
// LogWarn is to log warning information need to focus
func LogWarn(msg string) {
logger.Warn().Msg(msg)
}
// LogError is to log error information when unknown error happened
func LogError(msg string) {
logger.Error().Msg(msg)
}
// LogFatal is to log fatal information when the error will influence server work
func LogFatal(msg string) {
logger.Fatal().Msg(msg)
}
// LogPanic is to log panic information when the error is more serious than fatal
func LogPanic(msg string) {
logger.Panic().Msg(msg)
}