-
Notifications
You must be signed in to change notification settings - Fork 0
/
logger.go
173 lines (143 loc) · 4.67 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
package logger
import (
"encoding/json"
"errors"
"fmt"
coreLog "log"
"os"
"time"
"github.com/valyala/fasthttp"
)
type severity string
const (
criticalSeverity severity = "CRITICAL"
debugSeverity severity = "DEBUG"
errorSeverity severity = "ERROR"
infoSeverity severity = "INFO"
warningSeverity severity = "WARNING"
)
type logEntry struct {
Severity severity `json:"severity"`
Tags []string `json:"tags"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// Options is the config that is used for bootstrapping the logger.
// Default is posting logs to remote server but omitting host will
// write local logs instead.
type Options struct {
Host string `json:"host"` // When omitting, logs will be written locally
System string `json:"system"` // Required
Token string `json:"token"` // Required if Host is set
Local bool `json:"local"` // Default false - If you want to force local logs in addition to the remote ones
Timeout int `json:"timeout"` // Default 10 - How long communication with server is allowed to take before giving up and writing a local log
}
type logger struct {
options Options
}
const format = "2006-01-02 15:04:05"
const timeout = 10
var logTags = []string{"logging"}
var logr *logger
// Init bootstraps the config to the logger instance
func Init(o Options) error {
if o.Timeout < 1 {
o.Timeout = timeout
}
if logr != nil {
err := errors.New("Trying to instantiate an already instantiated logger")
Error(logTags, err.Error(), nil)
return err
}
logr = &logger{o}
if o.Host == "" {
Warning(logTags, "Host is not set", nil)
}
return nil
}
// Critical creates a log for critical error messages.
// Is synchronous and if you need concurrency run it as a goroutine.
func Critical(tags []string, message string, data interface{}) {
log(newEntry(criticalSeverity, tags, message, data))
}
// Debug creates a log for debug messages.
// Is synchronous and if you need concurrency run it as a goroutine.
func Debug(tags []string, message string, data interface{}) {
log(newEntry(debugSeverity, tags, message, data))
}
// Error creates a log for error messages.
// Is synchronous and if you need concurrency run it as a goroutine.
func Error(tags []string, message string, data interface{}) {
log(newEntry(errorSeverity, tags, message, data))
}
// Fatal creates a log for critical error messages and shuts down the server.
// Is synchronous and should not be ran concurrently as it would defeat the
// purpose of being a fatal action.
func Fatal(tags []string, message string, data interface{}) {
e := newEntry(criticalSeverity, tags, message, data)
if err := log(e); err == nil && !logr.options.Local {
// If an error didnt occur here, it wont write a local log so we do it here
writeLocalLog(e)
}
os.Exit(1)
}
// Info creates a log for informational messages.
// Is synchronous and if you need concurrency run it as a goroutine.
func Info(tags []string, message string, data interface{}) {
log(newEntry(infoSeverity, tags, message, data))
}
// Warning creates a log for warning messages.
// Is synchronous and if you need concurrency run it as a goroutine.
func Warning(tags []string, message string, data interface{}) {
log(newEntry(warningSeverity, tags, message, data))
}
func newEntry(severity severity, tags []string, message string, data interface{}) logEntry {
if logr == nil {
coreLog.Fatal("You need to instantiate the logger first")
}
return logEntry{
severity,
append([]string{logr.options.System}, tags...),
message,
data,
}
}
func log(e logEntry) error {
body, err := json.Marshal(e)
if err != nil {
writeLocalLog(e)
Error(logTags, fmt.Sprintf("Could not post to log due to \"data\" wasn't encodable - See local log"), "")
}
if err == nil {
err = postLog(body)
if err != nil || logr.options.Local {
writeLocalLog(e)
}
return err
}
return nil
}
func postLog(body []byte) error {
if logr.options.Host == "" {
return errors.New("Host is not set")
}
req := fasthttp.AcquireRequest()
req.SetRequestURI(logr.options.Host)
req.Header.SetMethod("POST")
req.Header.SetContentType("application/json")
req.Header.Add("billes-log-token", logr.options.Token)
req.SetBody(body)
res := fasthttp.AcquireResponse()
client := &fasthttp.Client{}
err := client.DoTimeout(req, res, time.Duration(logr.options.Timeout)*time.Second)
if err != nil && logr.options.Host != "" {
entry := newEntry(warningSeverity, logTags, fmt.Sprintf("Failed while sending log entry request: %s", err.Error()), nil)
writeLocalLog(entry)
}
return err
}
func writeLocalLog(e logEntry) {
t := time.Now()
ts := t.Format(format)
fmt.Printf("%v %v - %v - %v\n", ts, e.Severity, e.Tags, e.Message)
}