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...) +}