mirror of
https://github.com/ergochat/ergo.git
synced 2025-12-20 02:00:11 -08:00
logger: Move to separate package, make *much* nicer
This commit is contained in:
parent
ef9acf53f8
commit
439331cfb8
6 changed files with 160 additions and 86 deletions
234
irc/logger/logger.go
Normal file
234
irc/logger/logger.go
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
|
||||
// released under the MIT license
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"strings"
|
||||
|
||||
"sync"
|
||||
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/mgutz/ansi"
|
||||
)
|
||||
|
||||
// Level represents the level to log messages at.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
// LogDebug represents debug messages.
|
||||
LogDebug Level = iota
|
||||
// LogInfo represents informational messages.
|
||||
LogInfo
|
||||
// LogWarning represents warnings.
|
||||
LogWarning
|
||||
// LogError represents errors.
|
||||
LogError
|
||||
)
|
||||
|
||||
var (
|
||||
LogLevelNames = map[string]Level{
|
||||
"debug": LogDebug,
|
||||
"info": LogInfo,
|
||||
"warn": LogWarning,
|
||||
"warning": LogWarning,
|
||||
"warnings": LogWarning,
|
||||
"error": LogError,
|
||||
"errors": LogError,
|
||||
}
|
||||
LogLevelDisplayNames = map[Level]string{
|
||||
LogDebug: "debug",
|
||||
LogInfo: "info",
|
||||
LogWarning: "warning",
|
||||
LogError: "error",
|
||||
}
|
||||
)
|
||||
|
||||
// Manager is the main interface used to log debug/info/error messages.
|
||||
type Manager struct {
|
||||
loggers []singleLogger
|
||||
stderrWriteLock sync.Mutex
|
||||
DumpingRawInOut bool
|
||||
}
|
||||
|
||||
// Config represents the configuration of a single logger.
|
||||
type Config struct {
|
||||
// logging methods
|
||||
MethodStderr bool
|
||||
MethodFile bool
|
||||
Filename string
|
||||
// logging level
|
||||
Level Level
|
||||
// logging types
|
||||
Types []string
|
||||
ExcludedTypes []string
|
||||
}
|
||||
|
||||
// NewManager returns a new log manager.
|
||||
func NewManager(config ...Config) (*Manager, error) {
|
||||
var logger Manager
|
||||
|
||||
for _, logConfig := range config {
|
||||
typeMap := make(map[string]bool)
|
||||
for _, name := range logConfig.Types {
|
||||
typeMap[name] = true
|
||||
}
|
||||
excludedTypeMap := make(map[string]bool)
|
||||
for _, name := range logConfig.ExcludedTypes {
|
||||
excludedTypeMap[name] = true
|
||||
}
|
||||
|
||||
sLogger := singleLogger{
|
||||
MethodSTDERR: logConfig.MethodStderr,
|
||||
MethodFile: fileMethod{
|
||||
Enabled: logConfig.MethodFile,
|
||||
Filename: logConfig.Filename,
|
||||
},
|
||||
Level: logConfig.Level,
|
||||
Types: typeMap,
|
||||
ExcludedTypes: excludedTypeMap,
|
||||
stderrWriteLock: &logger.stderrWriteLock,
|
||||
}
|
||||
if typeMap["userinput"] || typeMap["useroutput"] || (typeMap["*"] && !(excludedTypeMap["userinput"] && excludedTypeMap["useroutput"])) {
|
||||
logger.DumpingRawInOut = true
|
||||
}
|
||||
if sLogger.MethodFile.Enabled {
|
||||
file, err := os.OpenFile(sLogger.MethodFile.Filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not open log file %s [%s]", sLogger.MethodFile.Filename, err.Error())
|
||||
}
|
||||
writer := bufio.NewWriter(file)
|
||||
sLogger.MethodFile.File = file
|
||||
sLogger.MethodFile.Writer = writer
|
||||
}
|
||||
logger.loggers = append(logger.loggers, sLogger)
|
||||
}
|
||||
|
||||
return &logger, nil
|
||||
}
|
||||
|
||||
// Log logs the given message with the given details.
|
||||
func (logger *Manager) Log(level Level, logType string, messageParts ...string) {
|
||||
for _, singleLogger := range logger.loggers {
|
||||
singleLogger.Log(level, logType, messageParts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs the given message as a debug message.
|
||||
func (logger *Manager) Debug(logType string, messageParts ...string) {
|
||||
for _, singleLogger := range logger.loggers {
|
||||
singleLogger.Log(LogDebug, logType, messageParts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs the given message as an info message.
|
||||
func (logger *Manager) Info(logType string, messageParts ...string) {
|
||||
for _, singleLogger := range logger.loggers {
|
||||
singleLogger.Log(LogInfo, logType, messageParts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warning logs the given message as a warning message.
|
||||
func (logger *Manager) Warning(logType string, messageParts ...string) {
|
||||
for _, singleLogger := range logger.loggers {
|
||||
singleLogger.Log(LogWarning, logType, messageParts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs the given message as an error message.
|
||||
func (logger *Manager) Error(logType string, messageParts ...string) {
|
||||
for _, singleLogger := range logger.loggers {
|
||||
singleLogger.Log(LogError, logType, messageParts...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal logs the given message as an error message, then exits.
|
||||
func (logger *Manager) Fatal(logType string, messageParts ...string) {
|
||||
logger.Error(logType, messageParts...)
|
||||
logger.Error("FATAL", "Fatal error encountered, application exiting")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type fileMethod struct {
|
||||
Enabled bool
|
||||
Filename string
|
||||
File *os.File
|
||||
Writer *bufio.Writer
|
||||
}
|
||||
|
||||
// singleLogger represents a single logger instance.
|
||||
type singleLogger struct {
|
||||
stderrWriteLock *sync.Mutex
|
||||
MethodSTDERR bool
|
||||
MethodFile fileMethod
|
||||
Level Level
|
||||
Types map[string]bool
|
||||
ExcludedTypes map[string]bool
|
||||
}
|
||||
|
||||
// Log logs the given message with the given details.
|
||||
func (logger *singleLogger) Log(level Level, logType string, messageParts ...string) {
|
||||
// no logging enabled
|
||||
if !(logger.MethodSTDERR || logger.MethodFile.Enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
// ensure we're logging to the given level
|
||||
if level < logger.Level {
|
||||
return
|
||||
}
|
||||
|
||||
// ensure we're capturing this logType
|
||||
logTypeCleaned := strings.ToLower(strings.TrimSpace(logType))
|
||||
capturing := (logger.Types["*"] || logger.Types[logTypeCleaned]) && !logger.ExcludedTypes["*"] && !logger.ExcludedTypes[logTypeCleaned]
|
||||
if !capturing {
|
||||
return
|
||||
}
|
||||
|
||||
// assemble full line
|
||||
timeGrey := ansi.ColorFunc("243")
|
||||
grey := ansi.ColorFunc("8")
|
||||
alert := ansi.ColorFunc("232+b:red")
|
||||
warn := ansi.ColorFunc("black:214")
|
||||
info := ansi.ColorFunc("117")
|
||||
debug := ansi.ColorFunc("78")
|
||||
section := ansi.ColorFunc("229")
|
||||
|
||||
levelDisplay := LogLevelDisplayNames[level]
|
||||
if level == LogError {
|
||||
levelDisplay = alert(levelDisplay)
|
||||
} else if level == LogWarning {
|
||||
levelDisplay = warn(levelDisplay)
|
||||
} else if level == LogInfo {
|
||||
levelDisplay = info(levelDisplay)
|
||||
} else if level == LogDebug {
|
||||
levelDisplay = debug(levelDisplay)
|
||||
}
|
||||
|
||||
sep := grey(":")
|
||||
fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05Z")), sep, levelDisplay, sep, section(logType), sep)
|
||||
fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], section(logType))
|
||||
for i, p := range messageParts {
|
||||
fullStringFormatted += p
|
||||
fullStringRaw += p
|
||||
if i != len(messageParts)-1 {
|
||||
fullStringFormatted += " " + sep + " "
|
||||
fullStringRaw += " : "
|
||||
}
|
||||
}
|
||||
|
||||
// output
|
||||
if logger.MethodSTDERR {
|
||||
logger.stderrWriteLock.Lock()
|
||||
fmt.Fprintln(colorable.NewColorableStderr(), fullStringFormatted)
|
||||
logger.stderrWriteLock.Unlock()
|
||||
}
|
||||
if logger.MethodFile.Enabled {
|
||||
logger.MethodFile.Writer.WriteString(fullStringRaw + "\n")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue