1package logging
2
3import "io"
4import "os"
5import "fmt"
6import "strings"
7import "time"
8import "bytes"
9import "net/http"
10import "io/ioutil"
11import "runtime/debug"
12import l "log"
13
14// Log levels
15type LogLevel int16
16
17const (
18	Silent LogLevel = iota
19	Fatal
20	Error
21	Warn
22	Info
23	Verbose
24	Timing
25	Debug
26	Trace
27)
28
29const udtag_begin = "<ud>"
30const udtag_end = "</ud>"
31
32// Logger interface
33type Logger interface {
34	// Warnings, logged by default.
35	Warnf(format string, v ...interface{})
36	// Errors, logged by default.
37	Errorf(format string, v ...interface{})
38	// Fatal errors. Will not terminate execution.
39	Fatalf(format string, v ...interface{})
40	// Informational messages.
41	Infof(format string, v ...interface{})
42	// Verbose messages like request logging
43	Verbosef(format string, v ...interface{})
44	// Get stack trace
45	StackTrace() string
46	// Timing utility
47	Timer(format string, v ...interface{}) Ender
48	// Debugging messages
49	Debugf(format string, v ...interface{})
50	// Program execution
51	Tracef(format string, v ...interface{})
52	// Call and print the stringer if verbose enabled
53	LazyVerbose(fn func() string)
54	// Call and print the stringer if debugging enabled
55	LazyDebug(fn func() string)
56	// Call and print the stringer if tracing enabled
57	LazyTrace(fn func() string)
58}
59
60// Timer interface
61type Ender interface {
62	// Stop and log timing
63	End()
64}
65
66//
67// Implementation
68//
69
70// Messages administrator should eventually see.
71func (t LogLevel) String() string {
72	switch t {
73	case Silent:
74		return "Silent"
75	case Fatal:
76		return "Fatal"
77	case Error:
78		return "Error"
79	case Warn:
80		return "Warn"
81	case Info:
82		return "Info"
83	case Verbose:
84		return "Verbose"
85	case Timing:
86		return "Timing"
87	case Debug:
88		return "Debug"
89	case Trace:
90		return "Trace"
91	default:
92		return "Info"
93	}
94}
95
96func Level(s string) LogLevel {
97	switch strings.ToUpper(s) {
98	case "SILENT":
99		return Silent
100	case "FATAL":
101		return Fatal
102	case "ERROR":
103		return Error
104	case "WARN":
105		return Warn
106	case "INFO":
107		return Info
108	case "VERBOSE":
109		return Verbose
110	case "TIMING":
111		return Timing
112	case "DEBUG":
113		return Debug
114	case "TRACE":
115		return Trace
116	default:
117		return Info
118	}
119}
120
121type destination struct {
122	baselevel LogLevel
123	target    *l.Logger
124}
125
126func (log *destination) Warnf(format string, v ...interface{}) {
127	log.printf(Warn, format, v...)
128}
129
130// Errors that caused problems in execution logic.
131func (log *destination) Errorf(format string, v ...interface{}) {
132	log.printf(Error, format, v...)
133}
134
135// Fatal messages are to be logged prior to exiting due to errors.
136func (log *destination) Fatalf(format string, v ...interface{}) {
137	log.printf(Fatal, format, v...)
138}
139
140// Info messages are those that are logged but not expected to be read.
141func (log *destination) Infof(format string, v ...interface{}) {
142	log.printf(Info, format, v...)
143}
144
145// Used for logging additional information that are not logged by info level
146// This may slightly impact performance
147func (log *destination) Verbosef(format string, v ...interface{}) {
148	log.printf(Verbose, format, v...)
149}
150
151// Debug messages to help analyze problem. Default off.
152func (log *destination) Debugf(format string, v ...interface{}) {
153	log.printf(Debug, format, v...)
154}
155
156// Execution trace showing the program flow. Default off.
157func (log *destination) Tracef(format string, v ...interface{}) {
158	log.printf(Trace, format, v...)
159}
160
161// Set the base log level
162func (log *destination) SetLogLevel(to LogLevel) {
163	log.baselevel = to
164}
165
166// Get stack trace
167func (log *destination) StackTrace() string {
168	return log.getStackTrace(2, debug.Stack())
169}
170
171// Get profiling info
172func Profile(port string, endpoints ...string) string {
173	if strings.HasPrefix(port, ":") {
174		port = port[1:]
175	}
176	var buf bytes.Buffer
177	for _, endpoint := range endpoints {
178		addr := fmt.Sprintf("http://localhost:%s/debug/pprof/%s?debug=1", port, endpoint)
179		resp, err := http.Get(addr)
180		if err != nil {
181			continue
182		}
183		defer resp.Body.Close()
184		body, err := ioutil.ReadAll(resp.Body)
185		if err != nil {
186			continue
187		}
188		buf.Write(body)
189	}
190	return buf.String()
191}
192
193// Dump profiling info periodically
194func PeriodicProfile(level LogLevel, port string, endpoints ...string) {
195	tick := time.NewTicker(5 * time.Minute)
196	go func() {
197		for {
198			select {
199			case <-tick.C:
200				if SystemLogger.IsEnabled(level) {
201					SystemLogger.printf(level, "%v", Profile(port, endpoints...))
202				}
203			}
204		}
205	}()
206}
207
208// Run function only if output will be logged at debug level
209func (log *destination) LazyDebug(fn func() string) {
210	if log.IsEnabled(Debug) {
211		log.printf(Debug, "%s", fn())
212	}
213}
214
215// Run function only if output will be logged at verbose level
216func (log *destination) LazyVerbose(fn func() string) {
217	if log.IsEnabled(Verbose) {
218		log.printf(Verbose, "%s", fn())
219	}
220}
221
222// Run function only if output will be logged at trace level
223func (log *destination) LazyTrace(fn func() string) {
224	if log.IsEnabled(Trace) {
225		log.printf(Trace, "%s", fn())
226	}
227}
228
229// Check if enabled
230func (log *destination) IsEnabled(at LogLevel) bool {
231	return log.baselevel >= at
232}
233
234func (log *destination) printf(at LogLevel, format string, v ...interface{}) {
235	if log.IsEnabled(at) {
236		ts := time.Now().Format("2006-01-02T15:04:05.000-07:00")
237		log.target.Printf(ts+" ["+at.String()+"] "+format, v...)
238	}
239}
240
241func (log *destination) getStackTrace(skip int, stack []byte) string {
242	var buf bytes.Buffer
243	lines := strings.Split(string(stack), "\n")
244	for _, call := range lines[skip*2:] {
245		buf.WriteString(fmt.Sprintf("%s\n", call))
246	}
247	return buf.String()
248}
249
250// The default logger
251var SystemLogger destination
252
253func init() {
254	dest := l.New(os.Stdout, "", 0)
255	SystemLogger = destination{baselevel: Info, target: dest}
256}
257
258// SetLogWriter sets a new default destination
259func SetLogWriter(w io.Writer) {
260	dest := l.New(w, "", 0)
261	SystemLogger = destination{baselevel: Info, target: dest}
262}
263
264//
265// A set of convenience methods to log to default logger
266// See correspond methods on destination for details
267//
268func Warnf(format string, v ...interface{}) {
269	SystemLogger.printf(Warn, format, v...)
270}
271
272// Errorf to log message and warning messages will be logged.
273func Errorf(format string, v ...interface{}) {
274	SystemLogger.printf(Error, format, v...)
275}
276
277// Fatalf to log message and warning messages will be logged.
278func Fatalf(format string, v ...interface{}) {
279	SystemLogger.printf(Fatal, format, v...)
280}
281
282// Infof to log message at info level.
283func Infof(format string, v ...interface{}) {
284	SystemLogger.printf(Info, format, v...)
285}
286
287// Verbosef to log message at verbose level.
288func Verbosef(format string, v ...interface{}) {
289	SystemLogger.printf(Verbose, format, v...)
290}
291
292// Debugf to log message at info level.
293func Debugf(format string, v ...interface{}) {
294	SystemLogger.printf(Debug, format, v...)
295}
296
297// Tracef to log message at info level.
298func Tracef(format string, v ...interface{}) {
299	SystemLogger.printf(Trace, format, v...)
300}
301
302// StackTrace prints current stack at specified log level
303func StackTrace() string {
304	return SystemLogger.getStackTrace(2, debug.Stack())
305}
306
307// Set the base log level
308func SetLogLevel(to LogLevel) {
309	SystemLogger.SetLogLevel(to)
310}
311
312// Check if logging is enabled
313func IsEnabled(lvl LogLevel) bool {
314	return SystemLogger.IsEnabled(lvl)
315}
316
317// Run function only if output will be logged at verbose level
318func LazyVerbose(fn func() string) {
319	if SystemLogger.IsEnabled(Verbose) {
320		SystemLogger.printf(Verbose, "%s", fn())
321	}
322}
323
324// Run function only if output will be logged at debug level
325func LazyDebug(fn func() string) {
326	if SystemLogger.IsEnabled(Debug) {
327		SystemLogger.printf(Debug, "%s", fn())
328	}
329}
330
331// Run function only if output will be logged at trace level
332func LazyTrace(fn func() string) {
333	if SystemLogger.IsEnabled(Trace) {
334		SystemLogger.printf(Trace, "%s", fn())
335	}
336}
337
338// Run function only if output will be logged at verbose level
339// Only %v is allowable in format string
340func LazyVerbosef(fmt string, fns ...func() string) {
341	if SystemLogger.IsEnabled(Verbose) {
342		snippets := make([]interface{}, len(fns))
343		for i, fn := range fns {
344			snippets[i] = fn()
345		}
346		SystemLogger.printf(Verbose, fmt, snippets...)
347	}
348}
349
350// Run function only if output will be logged at debug level
351// Only %v is allowable in format string
352func LazyDebugf(fmt string, fns ...func() string) {
353	if SystemLogger.IsEnabled(Debug) {
354		snippets := make([]interface{}, len(fns))
355		for i, fn := range fns {
356			snippets[i] = fn()
357		}
358		SystemLogger.printf(Debug, fmt, snippets...)
359	}
360}
361
362// Run function only if output will be logged at trace level
363// Only %v is allowable in format string
364func LazyTracef(fmt string, fns ...func() string) {
365	if SystemLogger.IsEnabled(Trace) {
366		snippets := make([]interface{}, len(fns))
367		for i, fn := range fns {
368			snippets[i] = fn()
369		}
370		SystemLogger.printf(Trace, fmt, snippets...)
371	}
372}
373
374// Tag user data in default format
375func TagUD(arg interface{}) interface{} {
376	return fmt.Sprintf("%s(%v)%s", udtag_begin, arg, udtag_end)
377}
378
379// Tag user data in string format
380func TagStrUD(arg interface{}) interface{} {
381	return fmt.Sprintf("%s(%s)%s", udtag_begin, arg, udtag_end)
382}
383