mirror of
1
Fork 0
forgejo/vendor/github.com/couchbase/goutils/logging/logger.go

362 lines
8.0 KiB
Go

// Copyright (c) 2016 Couchbase, Inc.
// 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 logging
import (
"os"
"runtime"
"strings"
"sync"
)
type Level int
const (
NONE = Level(iota) // Disable all logging
FATAL // System is in severe error state and has to abort
SEVERE // System is in severe error state and cannot recover reliably
ERROR // System is in error state but can recover and continue reliably
WARN // System approaching error state, or is in a correct but undesirable state
INFO // System-level events and status, in correct states
REQUEST // Request-level events, with request-specific rlevel
TRACE // Trace detailed system execution, e.g. function entry / exit
DEBUG // Debug
)
type LogEntryFormatter int
const (
TEXTFORMATTER = LogEntryFormatter(iota)
JSONFORMATTER
KVFORMATTER
UNIFORMFORMATTER
)
func (level Level) String() string {
return _LEVEL_NAMES[level]
}
var _LEVEL_NAMES = []string{
DEBUG: "DEBUG",
TRACE: "TRACE",
REQUEST: "REQUEST",
INFO: "INFO",
WARN: "WARN",
ERROR: "ERROR",
SEVERE: "SEVERE",
FATAL: "FATAL",
NONE: "NONE",
}
var _LEVEL_MAP = map[string]Level{
"debug": DEBUG,
"trace": TRACE,
"request": REQUEST,
"info": INFO,
"warn": WARN,
"error": ERROR,
"severe": SEVERE,
"fatal": FATAL,
"none": NONE,
}
// cache logging enablement to improve runtime performance (reduces from multiple tests to a single test on each call)
var (
cachedDebug bool
cachedTrace bool
cachedRequest bool
cachedInfo bool
cachedWarn bool
cachedError bool
cachedSevere bool
cachedFatal bool
)
// maintain the cached logging state
func cacheLoggingChange() {
cachedDebug = !skipLogging(DEBUG)
cachedTrace = !skipLogging(TRACE)
cachedRequest = !skipLogging(REQUEST)
cachedInfo = !skipLogging(INFO)
cachedWarn = !skipLogging(WARN)
cachedError = !skipLogging(ERROR)
cachedSevere = !skipLogging(SEVERE)
cachedFatal = !skipLogging(FATAL)
}
func ParseLevel(name string) (level Level, ok bool) {
level, ok = _LEVEL_MAP[strings.ToLower(name)]
return
}
// Logger provides a common interface for logging libraries
type Logger interface {
// Higher performance
Loga(level Level, f func() string)
Debuga(f func() string)
Tracea(f func() string)
Requesta(rlevel Level, f func() string)
Infoa(f func() string)
Warna(f func() string)
Errora(f func() string)
Severea(f func() string)
Fatala(f func() string)
// Printf style
Logf(level Level, fmt string, args ...interface{})
Debugf(fmt string, args ...interface{})
Tracef(fmt string, args ...interface{})
Requestf(rlevel Level, fmt string, args ...interface{})
Infof(fmt string, args ...interface{})
Warnf(fmt string, args ...interface{})
Errorf(fmt string, args ...interface{})
Severef(fmt string, args ...interface{})
Fatalf(fmt string, args ...interface{})
/*
These APIs control the logging level
*/
SetLevel(Level) // Set the logging level
Level() Level // Get the current logging level
}
var logger Logger = nil
var curLevel Level = DEBUG // initially set to never skip
var loggerMutex sync.RWMutex
// All the methods below first acquire the mutex (mostly in exclusive mode)
// and only then check if logging at the current level is enabled.
// This introduces a fair bottleneck for those log entries that should be
// skipped (the majority, at INFO or below levels)
// We try to predict here if we should lock the mutex at all by caching
// the current log level: while dynamically changing logger, there might
// be the odd entry skipped as the new level is cached.
// Since we seem to never change the logger, this is not an issue.
func skipLogging(level Level) bool {
if logger == nil {
return true
}
return level > curLevel
}
func SetLogger(newLogger Logger) {
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger = newLogger
if logger == nil {
curLevel = NONE
} else {
curLevel = newLogger.Level()
}
cacheLoggingChange()
}
// we are using deferred unlocking here throughout as we have to do this
// for the anonymous function variants even though it would be more efficient
// to not do this for the printf style variants
// anonymous function variants
func Loga(level Level, f func() string) {
if skipLogging(level) {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Loga(level, f)
}
func Debuga(f func() string) {
if !cachedDebug {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Debuga(f)
}
func Tracea(f func() string) {
if !cachedTrace {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Tracea(f)
}
func Requesta(rlevel Level, f func() string) {
if !cachedRequest {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Requesta(rlevel, f)
}
func Infoa(f func() string) {
if !cachedInfo {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Infoa(f)
}
func Warna(f func() string) {
if !cachedWarn {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Warna(f)
}
func Errora(f func() string) {
if !cachedError {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Errora(f)
}
func Severea(f func() string) {
if !cachedSevere {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Severea(f)
}
func Fatala(f func() string) {
if !cachedFatal {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Fatala(f)
}
// printf-style variants
func Logf(level Level, fmt string, args ...interface{}) {
if skipLogging(level) {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Logf(level, fmt, args...)
}
func Debugf(fmt string, args ...interface{}) {
if !cachedDebug {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Debugf(fmt, args...)
}
func Tracef(fmt string, args ...interface{}) {
if !cachedTrace {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Tracef(fmt, args...)
}
func Requestf(rlevel Level, fmt string, args ...interface{}) {
if !cachedRequest {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Requestf(rlevel, fmt, args...)
}
func Infof(fmt string, args ...interface{}) {
if !cachedInfo {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Infof(fmt, args...)
}
func Warnf(fmt string, args ...interface{}) {
if !cachedWarn {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Warnf(fmt, args...)
}
func Errorf(fmt string, args ...interface{}) {
if !cachedError {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Errorf(fmt, args...)
}
func Severef(fmt string, args ...interface{}) {
if !cachedSevere {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Severef(fmt, args...)
}
func Fatalf(fmt string, args ...interface{}) {
if !cachedFatal {
return
}
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Fatalf(fmt, args...)
}
func SetLevel(level Level) {
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.SetLevel(level)
curLevel = level
cacheLoggingChange()
}
func LogLevel() Level {
loggerMutex.RLock()
defer loggerMutex.RUnlock()
return logger.Level()
}
func Stackf(level Level, fmt string, args ...interface{}) {
if skipLogging(level) {
return
}
buf := make([]byte, 1<<16)
n := runtime.Stack(buf, false)
s := string(buf[0:n])
loggerMutex.Lock()
defer loggerMutex.Unlock()
logger.Logf(level, fmt, args...)
logger.Logf(level, s)
}
func init() {
logger := NewLogger(os.Stderr, INFO, TEXTFORMATTER)
SetLogger(logger)
}