-
Notifications
You must be signed in to change notification settings - Fork 0
/
log.go
179 lines (158 loc) · 3.54 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
// Copyright © 2021-2023 The Gomon Project.
package gocore
import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"syscall"
"time"
)
type (
// LogLevel indexes the literal log levels.
LogLevel int
// LogMessage is custom logging error type.
LogMessage struct {
Source string
E error
File string
Line int
Detail map[string]string
}
)
const (
// enum of logging levels corresponding to log levels.
LevelTrace LogLevel = iota - 2
LevelDebug
LevelInfo // default
LevelWarn
LevelError
LevelFatal
)
var (
// logLevels map logging level enums to log levels.
logLevels = map[LogLevel]string{
LevelTrace: "TRACE",
LevelDebug: "DEBUG",
LevelInfo: "INFO",
LevelWarn: "WARN",
LevelError: "ERROR",
LevelFatal: "FATAL",
}
// loggingLevel maps requested LOG_LEVEL to index.
LoggingLevel = func() LogLevel {
switch strings.ToUpper(os.Getenv("LOG_LEVEL")) {
case "TRACE":
return LevelTrace
case "DEBUG":
return LevelDebug
case "WARN":
return LevelWarn
case "ERROR":
return LevelError
case "FATAL":
return LevelFatal
}
return LevelInfo
}()
// Log is the default log message formatter and writer.
Log = func(msg LogMessage, level LogLevel) {
if level >= LoggingLevel {
if msg.E == nil && level > LevelInfo {
level = LevelInfo
}
log.Printf("%s %-5s %s", time.Now().Format(RFC3339Milli), logLevels[level], msg.Error())
}
}
)
// Unsupported reports that a specific OS does not support a function
func Unsupported() error {
return Error("Unsupported", errors.New(runtime.GOOS))
}
// Error method to comply with error interface.
func (msg LogMessage) Error() string {
var detail string
var keys []string
for key := range msg.Detail {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
val := msg.Detail[key]
if val == "" {
continue
}
if strings.ContainsAny(val, "\t\n\v\f\r ") { // quote value with embedded whitespace
val = "\"" + val + "\""
}
detail += " " + key + "=" + val
}
var e string
if msg.E != nil {
e = "err=\"" + msg.E.Error() + "\" "
}
return fmt.Sprintf("[%s:%d] %ssource=%q%s",
msg.File,
msg.Line,
e,
msg.Source,
detail,
)
}
// Unwrap method to comply with error interface.
func (msg LogMessage) Unwrap() error {
return msg.E
}
// Error records the function source, error message, code location, and any
// details of initial error, preserving the initial error for percolation.
func Error(source string, err error, details ...map[string]string) LogMessage {
e := LogMessage{}
if errors.As(err, &e) {
return e // percolate original Err
}
_, file, line, _ := runtime.Caller(1)
file = filepath.Join(filepath.Base(filepath.Dir(file)), filepath.Base(file))
detail := map[string]string{}
var errno syscall.Errno
if errors.As(err, &errno) {
detail["errno"] = strconv.Itoa(int(errno))
}
// if for some reason, multiple detail maps, merge into first one.
for _, det := range details {
for key, val := range det {
detail[key] = val
}
}
return LogMessage{
Source: source,
E: err,
File: file,
Line: line,
Detail: detail,
}
}
// Trace log trace message.
func (msg LogMessage) Trace() {
Log(msg, LevelTrace)
}
// Debug log debug message.
func (msg LogMessage) Debug() {
Log(msg, LevelDebug)
}
// Info log info message (default logging level).
func (msg LogMessage) Info() {
Log(msg, LevelInfo)
}
// Warn log warning message.
func (msg LogMessage) Warn() {
Log(msg, LevelWarn)
}
// Error log error message.
func (msg LogMessage) Err() {
Log(msg, LevelError)
}