File: log.go

package info (click to toggle)
ntfy 2.11.0-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 19,364 kB
  • sloc: javascript: 16,782; makefile: 282; sh: 105; php: 21; python: 19
file content (206 lines) | stat: -rw-r--r-- 4,928 bytes parent folder | download | duplicates (2)
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
package log

import (
	"io"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

// Defaults for package level variables
var (
	DefaultLevel  = InfoLevel
	DefaultFormat = TextFormat
	DefaultOutput = &peekLogWriter{os.Stderr}
)

var (
	level               = DefaultLevel
	format              = DefaultFormat
	overrides           = make(map[string][]*levelOverride)
	output    io.Writer = DefaultOutput
	filename            = ""
	mu                  = &sync.RWMutex{}
)

// init sets the default log output (including log.SetOutput)
//
// This has to be explicitly called, because DefaultOutput is a peekLogWriter,
// which wraps os.Stderr.
func init() {
	SetOutput(DefaultOutput)
}

// Fatal prints the given message, and exits the program
func Fatal(message string, v ...any) {
	newEvent().Fatal(message, v...)
}

// Error prints the given message, if the current log level is ERROR or lower
func Error(message string, v ...any) {
	newEvent().Error(message, v...)
}

// Warn prints the given message, if the current log level is WARN or lower
func Warn(message string, v ...any) {
	newEvent().Warn(message, v...)
}

// Info prints the given message, if the current log level is INFO or lower
func Info(message string, v ...any) {
	newEvent().Info(message, v...)
}

// Debug prints the given message, if the current log level is DEBUG or lower
func Debug(message string, v ...any) {
	newEvent().Debug(message, v...)
}

// Trace prints the given message, if the current log level is TRACE
func Trace(message string, v ...any) {
	newEvent().Trace(message, v...)
}

// With creates a new log event and adds the fields of the given Contexter structs
func With(contexts ...Contexter) *Event {
	return newEvent().With(contexts...)
}

// Field creates a new log event and adds a custom field and value to it
func Field(key string, value any) *Event {
	return newEvent().Field(key, value)
}

// Fields creates a new log event and adds a map of fields to it
func Fields(fields Context) *Event {
	return newEvent().Fields(fields)
}

// Tag creates a new log event and adds a "tag" field to it
func Tag(tag string) *Event {
	return newEvent().Tag(tag)
}

// Time creates a new log event and sets the time field
func Time(time time.Time) *Event {
	return newEvent().Time(time)
}

// Timing runs f and records the time if took to execute it in "time_taken_ms"
func Timing(f func()) *Event {
	return newEvent().Timing(f)
}

// CurrentLevel returns the current log level
func CurrentLevel() Level {
	mu.RLock()
	defer mu.RUnlock()
	return level
}

// SetLevel sets a new log level
func SetLevel(newLevel Level) {
	mu.Lock()
	defer mu.Unlock()
	level = newLevel
}

// SetLevelOverride adds a log override for the given field
func SetLevelOverride(field string, value string, level Level) {
	mu.Lock()
	defer mu.Unlock()
	if _, ok := overrides[field]; !ok {
		overrides[field] = make([]*levelOverride, 0)
	}
	overrides[field] = append(overrides[field], &levelOverride{value: value, level: level})
}

// ResetLevelOverrides removes all log level overrides
func ResetLevelOverrides() {
	mu.Lock()
	defer mu.Unlock()
	overrides = make(map[string][]*levelOverride)
}

// CurrentFormat returns the current log format
func CurrentFormat() Format {
	mu.RLock()
	defer mu.RUnlock()
	return format
}

// SetFormat sets a new log format
func SetFormat(newFormat Format) {
	mu.Lock()
	defer mu.Unlock()
	format = newFormat
	if newFormat == JSONFormat {
		DisableDates()
	}
}

// SetOutput sets the log output writer
func SetOutput(w io.Writer) {
	mu.Lock()
	defer mu.Unlock()
	output = &peekLogWriter{w}
	if f, ok := w.(*os.File); ok {
		filename = f.Name()
	} else {
		filename = ""
	}
	log.SetOutput(output)
}

// File returns the log file, if any, or an empty string otherwise
func File() string {
	mu.RLock()
	defer mu.RUnlock()
	return filename
}

// IsFile returns true if the output is a non-default file
func IsFile() bool {
	mu.RLock()
	defer mu.RUnlock()
	return filename != ""
}

// DisableDates disables the date/time prefix
func DisableDates() {
	log.SetFlags(0)
}

// Loggable returns true if the given log level is lower or equal to the current log level
func Loggable(l Level) bool {
	return CurrentLevel() <= l
}

// IsTrace returns true if the current log level is TraceLevel
func IsTrace() bool {
	return Loggable(TraceLevel)
}

// IsDebug returns true if the current log level is DebugLevel or below
func IsDebug() bool {
	return Loggable(DebugLevel)
}

// peekLogWriter is an io.Writer which will peek at the rendered log event,
// and ensure that the rendered output is valid JSON. This is a hack!
type peekLogWriter struct {
	w io.Writer
}

func (w *peekLogWriter) Write(p []byte) (n int, err error) {
	if len(p) == 0 || p[0] == '{' || CurrentFormat() == TextFormat {
		return w.w.Write(p)
	}
	m := newEvent().Tag(tagStdLog).Render(InfoLevel, strings.TrimSpace(string(p)))
	if m == "" {
		return 0, nil
	}
	return w.w.Write([]byte(m + "\n"))
}