Logging infrastructure for Bee

logger.go implements the logging infrastructure for Bee.
The end user can set the preferred backend (that implements io.Writer).
It supports multiple logging levels, and logs are sent to the output
colored based on each level. Debug logs are treated differently, i.e.
they will be sent to the output unless DEBUG_ENABLED is set to "1".
This commit is contained in:
Faissal Elamraoui 2016-11-13 13:46:32 +01:00
parent 04c0f9d7b4
commit b4e13b5529
1 changed files with 260 additions and 0 deletions

260
logger.go Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2013 bee authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package main
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"sync"
"sync/atomic"
"text/template"
)
var errInvalidLogLevel = errors.New("logger: invalid log level")
const (
levelCritical = iota
levelFatal
levelSuccess
levelHint
levelDebug
levelInfo
levelWarn
levelError
)
var (
sequenceNo uint64
logger *BeeLogger
)
// BeeLogger logs logging records to the specified io.Writer
type BeeLogger struct {
mu sync.Mutex
output io.Writer
}
// LogRecord represents a log record and contains the timestamp when the record
// was created, an increasing id, level and the actual formatted log line.
type LogRecord struct {
ID uint64
Level string
Message string
Filename string
LineNo int
}
var (
logRecordTemplate *template.Template
debugLogRecordTemplate *template.Template
debugLogFormat string
)
func init() {
var (
err error
simpleLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}}{{.ID}} {{.Message}}{{EndLine}}`
debugLogFormat = `{{Now "2006/01/02 15:04:05"}} {{.Level}}{{.ID}} {{.Filename}}:{{.LineNo}} {{.Message}}{{EndLine}}`
)
// Initialize and parse logging templates
funcs := template.FuncMap{
"Now": Now,
"EndLine": EndLine,
}
logRecordTemplate, err = template.New("logRecordTemplate").Funcs(funcs).Parse(simpleLogFormat)
MustCheck(err)
debugLogRecordTemplate, err = template.New("dbgLogRecordTemplate").Funcs(funcs).Parse(debugLogFormat)
MustCheck(err)
// Initialize the logger instance with a NewColorWriter output
logger = &BeeLogger{output: NewColorWriter(os.Stdout)}
}
// SetOutput sets the logger output destination
func (l *BeeLogger) SetOutput(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
l.output = NewColorWriter(w)
}
func (l *BeeLogger) getLevelTag(level int) string {
switch level {
case levelFatal:
return "FATAL "
case levelSuccess:
return "SUCCESS "
case levelHint:
return "HINT "
case levelDebug:
return "DEBUG "
case levelInfo:
return "INFO "
case levelWarn:
return "WARN "
case levelError:
return "ERROR "
case levelCritical:
return "CRITICAL"
default:
panic(errInvalidLogLevel)
}
}
func (l *BeeLogger) getColorLevel(level int) string {
switch level {
case levelCritical:
return RedBold(l.getLevelTag(level))
case levelFatal:
return RedBold(l.getLevelTag(level))
case levelInfo:
return BlueBold(l.getLevelTag(level))
case levelHint:
return CyanBold(l.getLevelTag(level))
case levelDebug:
return YellowBold(l.getLevelTag(level))
case levelError:
return RedBold(l.getLevelTag(level))
case levelWarn:
return YellowBold(l.getLevelTag(level))
case levelSuccess:
return GreenBold(l.getLevelTag(level))
default:
panic(errInvalidLogLevel)
}
}
// mustLog logs the message according to the specified level and arguments.
// It panics in case of an error.
func (l *BeeLogger) mustLog(level int, message string, args ...interface{}) {
// Create the logging record and pass into the output
record := LogRecord{
ID: atomic.AddUint64(&sequenceNo, 1),
Level: l.getColorLevel(level),
Message: fmt.Sprintf(message, args...),
}
err := logRecordTemplate.Execute(l.output, record)
MustCheck(err)
}
// mustLogDebug logs a debug message only if debug mode
// is enabled. i.e. DEBUG_ENABLED="1"
func (l *BeeLogger) mustLogDebug(message string, args ...interface{}) {
if !IsDebugEnabled() {
return
}
// Change the output to Stderr
l.SetOutput(os.Stderr)
// Create the log record and Get the filename
// and the line number of the caller
_, file, line, _ := runtime.Caller(1)
record := LogRecord{
ID: atomic.AddUint64(&sequenceNo, 1),
Level: l.getColorLevel(levelDebug),
Message: fmt.Sprintf(message, args...),
LineNo: line,
Filename: filepath.Base(file),
}
err := debugLogRecordTemplate.Execute(l.output, record)
MustCheck(err)
}
// Debug outputs a debug log message
func (l *BeeLogger) Debug(message string) {
l.mustLogDebug(message)
}
// Debugf outputs a formatted debug log message
func (l *BeeLogger) Debugf(message string, vars ...interface{}) {
l.mustLogDebug(message, vars...)
}
// Info outputs an information log message
func (l *BeeLogger) Info(message string) {
l.mustLog(levelInfo, message)
}
// Infof outputs a formatted information log message
func (l *BeeLogger) Infof(message string, vars ...interface{}) {
l.mustLog(levelInfo, message, vars...)
}
// Warn outputs a warning log message
func (l *BeeLogger) Warn(message string) {
l.mustLog(levelWarn, message)
}
// Warnf outputs a formatted warning log message
func (l *BeeLogger) Warnf(message string, vars ...interface{}) {
l.mustLog(levelWarn, message, vars...)
}
// Error outputs an error log message
func (l *BeeLogger) Error(message string) {
l.mustLog(levelError, message)
}
// Errorf outputs a formatted error log message
func (l *BeeLogger) Errorf(message string, vars ...interface{}) {
l.mustLog(levelError, message, vars...)
}
// Fatal outputs a fatal log message and exists
func (l *BeeLogger) Fatal(message string) {
l.mustLog(levelFatal, message)
os.Exit(255)
}
// Fatalf outputs a formatted log message and exists
func (l *BeeLogger) Fatalf(message string, vars ...interface{}) {
l.mustLog(levelFatal, message, vars...)
os.Exit(255)
}
// Success outputs a success log message
func (l *BeeLogger) Success(message string) {
l.mustLog(levelSuccess, message)
}
// Successf outputs a formatted success log message
func (l *BeeLogger) Successf(message string, vars ...interface{}) {
l.mustLog(levelSuccess, message, vars...)
}
// Hint outputs a hint log message
func (l *BeeLogger) Hint(message string) {
l.mustLog(levelHint, message)
}
// Hintf outputs a formatted hint log message
func (l *BeeLogger) Hintf(message string, vars ...interface{}) {
l.mustLog(levelHint, message, vars...)
}
// Critical outputs a critical log message
func (l *BeeLogger) Critical(message string) {
l.mustLog(levelCritical, message)
}
// Criticalf outputs a formatted critical log message
func (l *BeeLogger) Criticalf(message string, vars ...interface{}) {
l.mustLog(levelCritical, message, vars...)
}