From b4e13b55298ac2ca65e93c12ef718a93b9f6854a Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 13 Nov 2016 13:46:32 +0100 Subject: [PATCH] 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". --- logger.go | 260 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 logger.go diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..567b073 --- /dev/null +++ b/logger.go @@ -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...) +}