mirror of
https://github.com/safing/portbase
synced 2025-09-01 18:19:57 +00:00
Cleanup log package, remove dependency on taskmanager package
This commit is contained in:
parent
0b6582dd15
commit
c1cc409558
9 changed files with 255 additions and 210 deletions
|
@ -9,5 +9,5 @@ var (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.StringVar(&logLevelFlag, "log", "info", "set log level to [trace|debug|info|warning|error|critical]")
|
flag.StringVar(&logLevelFlag, "log", "info", "set log level to [trace|debug|info|warning|error|critical]")
|
||||||
flag.StringVar(&pkgLogLevelsFlag, "plog", "", "set log level of packages: database=trace,firewall=debug")
|
flag.StringVar(&pkgLogLevelsFlag, "plog", "", "set log level of packages: database=trace,notifications=debug")
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ var counter uint16
|
||||||
|
|
||||||
const maxCount uint16 = 999
|
const maxCount uint16 = 999
|
||||||
|
|
||||||
func (s severity) String() string {
|
func (s Severity) String() string {
|
||||||
switch s {
|
switch s {
|
||||||
case TraceLevel:
|
case TraceLevel:
|
||||||
return "TRAC"
|
return "TRAC"
|
||||||
|
@ -51,15 +51,15 @@ func formatLine(line *logLine, duplicates uint64, useColor bool) string {
|
||||||
fLine = fmt.Sprintf("%s%s %s:%03d %s %s %03d%s%s %s", colorStart, line.timestamp.Format("060102 15:04:05.000"), line.file[fPartStart:], line.line, rightArrow, line.level.String(), counter, formatDuplicates(duplicates), colorEnd, line.msg)
|
fLine = fmt.Sprintf("%s%s %s:%03d %s %s %03d%s%s %s", colorStart, line.timestamp.Format("060102 15:04:05.000"), line.file[fPartStart:], line.line, rightArrow, line.level.String(), counter, formatDuplicates(duplicates), colorEnd, line.msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.trace != nil {
|
if line.tracer != nil {
|
||||||
// append full trace time
|
// append full trace time
|
||||||
if len(line.trace.actions) > 0 {
|
if len(line.tracer.logs) > 0 {
|
||||||
fLine += fmt.Sprintf(" Σ=%s", line.timestamp.Sub(line.trace.actions[0].timestamp))
|
fLine += fmt.Sprintf(" Σ=%s", line.timestamp.Sub(line.tracer.logs[0].timestamp))
|
||||||
}
|
}
|
||||||
|
|
||||||
// append all trace actions
|
// append all trace actions
|
||||||
var d time.Duration
|
var d time.Duration
|
||||||
for i, action := range line.trace.actions {
|
for i, action := range line.tracer.logs {
|
||||||
// set color
|
// set color
|
||||||
if useColor {
|
if useColor {
|
||||||
colorStart = action.level.color()
|
colorStart = action.level.color()
|
||||||
|
@ -71,10 +71,10 @@ func formatLine(line *logLine, duplicates uint64, useColor bool) string {
|
||||||
fPartStart = 0
|
fPartStart = 0
|
||||||
}
|
}
|
||||||
// format
|
// format
|
||||||
if i == len(line.trace.actions)-1 { // last
|
if i == len(line.tracer.logs)-1 { // last
|
||||||
d = line.timestamp.Sub(action.timestamp)
|
d = line.timestamp.Sub(action.timestamp)
|
||||||
} else {
|
} else {
|
||||||
d = line.trace.actions[i+1].timestamp.Sub(action.timestamp)
|
d = line.tracer.logs[i+1].timestamp.Sub(action.timestamp)
|
||||||
}
|
}
|
||||||
fLine += fmt.Sprintf("\n%s%19s %s:%03d %s %s%s %s", colorStart, d, action.file[fPartStart:], action.line, rightArrow, action.level.String(), colorEnd, action.msg)
|
fLine += fmt.Sprintf("\n%s%19s %s:%03d %s %s%s %s", colorStart, d, action.file[fPartStart:], action.line, rightArrow, action.level.String(), colorEnd, action.msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ const (
|
||||||
// colorWhite = "\033[37m"
|
// colorWhite = "\033[37m"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s severity) color() string {
|
func (s Severity) color() string {
|
||||||
switch s {
|
switch s {
|
||||||
case DebugLevel:
|
case DebugLevel:
|
||||||
return colorCyan
|
return colorCyan
|
||||||
|
|
|
@ -28,7 +28,7 @@ func init() {
|
||||||
colorsSupported = osdetail.EnableColorSupport()
|
colorsSupported = osdetail.EnableColorSupport()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s severity) color() string {
|
func (s Severity) color() string {
|
||||||
if colorsSupported {
|
if colorsSupported {
|
||||||
switch s {
|
switch s {
|
||||||
case DebugLevel:
|
case DebugLevel:
|
||||||
|
|
78
log/input.go
78
log/input.go
|
@ -8,24 +8,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fastcheck(level severity) bool {
|
func log(level Severity, msg string, tracer *ContextTracer) {
|
||||||
if pkgLevelsActive.IsSet() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if uint32(level) < atomic.LoadUint32(logLevel) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func log(level severity, msg string, trace *ContextTracer) {
|
|
||||||
|
|
||||||
if !started.IsSet() {
|
if !started.IsSet() {
|
||||||
// a bit resouce intense, but keeps logs before logging started.
|
// a bit resouce intense, but keeps logs before logging started.
|
||||||
// FIXME: create option to disable logging
|
// FIXME: create option to disable logging
|
||||||
go func() {
|
go func() {
|
||||||
<-startedSignal
|
<-startedSignal
|
||||||
log(level, msg, trace)
|
log(level, msg, tracer)
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -72,7 +62,7 @@ func log(level severity, msg string, trace *ContextTracer) {
|
||||||
// create log object
|
// create log object
|
||||||
log := &logLine{
|
log := &logLine{
|
||||||
msg: msg,
|
msg: msg,
|
||||||
trace: trace,
|
tracer: tracer,
|
||||||
level: level,
|
level: level,
|
||||||
timestamp: now,
|
timestamp: now,
|
||||||
file: file,
|
file: file,
|
||||||
|
@ -94,82 +84,96 @@ func log(level severity, msg string, trace *ContextTracer) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Tracef(things ...interface{}) {
|
func fastcheck(level Severity) bool {
|
||||||
if fastcheck(TraceLevel) {
|
if pkgLevelsActive.IsSet() {
|
||||||
log(TraceLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
return true
|
||||||
}
|
}
|
||||||
|
if uint32(level) >= atomic.LoadUint32(logLevel) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trace is used to log tiny steps. Log traces to context if you can!
|
||||||
func Trace(msg string) {
|
func Trace(msg string) {
|
||||||
if fastcheck(TraceLevel) {
|
if fastcheck(TraceLevel) {
|
||||||
log(TraceLevel, msg, nil)
|
log(TraceLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Debugf(things ...interface{}) {
|
// Tracef is used to log tiny steps. Log traces to context if you can!
|
||||||
if fastcheck(DebugLevel) {
|
func Tracef(format string, things ...interface{}) {
|
||||||
log(DebugLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
if fastcheck(TraceLevel) {
|
||||||
|
log(TraceLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug is used to log minor errors or unexpected events. These occurences are usually not worth mentioning in itself, but they might hint at a bigger problem.
|
||||||
func Debug(msg string) {
|
func Debug(msg string) {
|
||||||
if fastcheck(DebugLevel) {
|
if fastcheck(DebugLevel) {
|
||||||
log(DebugLevel, msg, nil)
|
log(DebugLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Infof(things ...interface{}) {
|
// Debugf is used to log minor errors or unexpected events. These occurences are usually not worth mentioning in itself, but they might hint at a bigger problem.
|
||||||
if fastcheck(InfoLevel) {
|
func Debugf(format string, things ...interface{}) {
|
||||||
log(InfoLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
if fastcheck(DebugLevel) {
|
||||||
|
log(DebugLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Info is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
|
||||||
func Info(msg string) {
|
func Info(msg string) {
|
||||||
if fastcheck(InfoLevel) {
|
if fastcheck(InfoLevel) {
|
||||||
log(InfoLevel, msg, nil)
|
log(InfoLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Warningf(things ...interface{}) {
|
// Infof is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
|
||||||
if fastcheck(WarningLevel) {
|
func Infof(format string, things ...interface{}) {
|
||||||
log(WarningLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
if fastcheck(InfoLevel) {
|
||||||
|
log(InfoLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warning is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
|
||||||
func Warning(msg string) {
|
func Warning(msg string) {
|
||||||
if fastcheck(WarningLevel) {
|
if fastcheck(WarningLevel) {
|
||||||
log(WarningLevel, msg, nil)
|
log(WarningLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Errorf(things ...interface{}) {
|
// Warningf is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
|
||||||
if fastcheck(ErrorLevel) {
|
func Warningf(format string, things ...interface{}) {
|
||||||
log(ErrorLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
if fastcheck(WarningLevel) {
|
||||||
|
log(WarningLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. Maybe User/Admin should be informed.
|
||||||
func Error(msg string) {
|
func Error(msg string) {
|
||||||
if fastcheck(ErrorLevel) {
|
if fastcheck(ErrorLevel) {
|
||||||
log(ErrorLevel, msg, nil)
|
log(ErrorLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Criticalf(things ...interface{}) {
|
// Errorf is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational.
|
||||||
if fastcheck(CriticalLevel) {
|
func Errorf(format string, things ...interface{}) {
|
||||||
log(CriticalLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
if fastcheck(ErrorLevel) {
|
||||||
|
log(ErrorLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Critical is used to log events that completely break the system. Operation connot continue. User/Admin must be informed.
|
||||||
func Critical(msg string) {
|
func Critical(msg string) {
|
||||||
if fastcheck(CriticalLevel) {
|
if fastcheck(CriticalLevel) {
|
||||||
log(CriticalLevel, msg, nil)
|
log(CriticalLevel, msg, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Testf(things ...interface{}) {
|
// Criticalf is used to log events that completely break the system. Operation connot continue. User/Admin must be informed.
|
||||||
fmt.Printf(things[0].(string), things[1:]...)
|
func Criticalf(format string, things ...interface{}) {
|
||||||
}
|
if fastcheck(CriticalLevel) {
|
||||||
|
log(CriticalLevel, fmt.Sprintf(format, things...), nil)
|
||||||
func Test(msg string) {
|
}
|
||||||
fmt.Println(msg)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,13 @@ import (
|
||||||
- logging is configured by main module and is supplied access to configuration and taskmanager
|
- logging is configured by main module and is supplied access to configuration and taskmanager
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type severity uint32
|
// Severity describes a log level.
|
||||||
|
type Severity uint32
|
||||||
|
|
||||||
type logLine struct {
|
type logLine struct {
|
||||||
msg string
|
msg string
|
||||||
trace *ContextTracer
|
tracer *ContextTracer
|
||||||
level severity
|
level Severity
|
||||||
timestamp time.Time
|
timestamp time.Time
|
||||||
file string
|
file string
|
||||||
line int
|
line int
|
||||||
|
@ -45,7 +46,7 @@ func (ll *logLine) Equal(ol *logLine) bool {
|
||||||
switch {
|
switch {
|
||||||
case ll.msg != ol.msg:
|
case ll.msg != ol.msg:
|
||||||
return false
|
return false
|
||||||
case ll.trace != nil || ol.trace != nil:
|
case ll.tracer != nil || ol.tracer != nil:
|
||||||
return false
|
return false
|
||||||
case ll.file != ol.file:
|
case ll.file != ol.file:
|
||||||
return false
|
return false
|
||||||
|
@ -57,13 +58,14 @@ func (ll *logLine) Equal(ol *logLine) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log Levels
|
||||||
const (
|
const (
|
||||||
TraceLevel severity = 1
|
TraceLevel Severity = 1
|
||||||
DebugLevel severity = 2
|
DebugLevel Severity = 2
|
||||||
InfoLevel severity = 3
|
InfoLevel Severity = 3
|
||||||
WarningLevel severity = 4
|
WarningLevel Severity = 4
|
||||||
ErrorLevel severity = 5
|
ErrorLevel Severity = 5
|
||||||
CriticalLevel severity = 6
|
CriticalLevel Severity = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -74,7 +76,7 @@ var (
|
||||||
logLevel = &logLevelInt
|
logLevel = &logLevelInt
|
||||||
|
|
||||||
pkgLevelsActive = abool.NewBool(false)
|
pkgLevelsActive = abool.NewBool(false)
|
||||||
pkgLevels = make(map[string]severity)
|
pkgLevels = make(map[string]Severity)
|
||||||
pkgLevelsLock sync.Mutex
|
pkgLevelsLock sync.Mutex
|
||||||
|
|
||||||
logsWaiting = make(chan bool, 1)
|
logsWaiting = make(chan bool, 1)
|
||||||
|
@ -90,22 +92,26 @@ var (
|
||||||
testErrors = abool.NewBool(false)
|
testErrors = abool.NewBool(false)
|
||||||
)
|
)
|
||||||
|
|
||||||
func SetPkgLevels(levels map[string]severity) {
|
// SetPkgLevels sets individual log levels for packages.
|
||||||
|
func SetPkgLevels(levels map[string]Severity) {
|
||||||
pkgLevelsLock.Lock()
|
pkgLevelsLock.Lock()
|
||||||
pkgLevels = levels
|
pkgLevels = levels
|
||||||
pkgLevelsLock.Unlock()
|
pkgLevelsLock.Unlock()
|
||||||
pkgLevelsActive.Set()
|
pkgLevelsActive.Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnSetPkgLevels removes all individual log levels for packages.
|
||||||
func UnSetPkgLevels() {
|
func UnSetPkgLevels() {
|
||||||
pkgLevelsActive.UnSet()
|
pkgLevelsActive.UnSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetLogLevel(level severity) {
|
// SetLogLevel sets a new log level.
|
||||||
|
func SetLogLevel(level Severity) {
|
||||||
atomic.StoreUint32(logLevel, uint32(level))
|
atomic.StoreUint32(logLevel, uint32(level))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseLevel(level string) severity {
|
// ParseLevel returns the level severity of a log level name.
|
||||||
|
func ParseLevel(level string) Severity {
|
||||||
switch strings.ToLower(level) {
|
switch strings.ToLower(level) {
|
||||||
case "trace":
|
case "trace":
|
||||||
return 1
|
return 1
|
||||||
|
@ -123,6 +129,7 @@ func ParseLevel(level string) severity {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start starts the logging system. Must be called in order to see logs.
|
||||||
func Start() (err error) {
|
func Start() (err error) {
|
||||||
|
|
||||||
if !initializing.SetToIf(false, true) {
|
if !initializing.SetToIf(false, true) {
|
||||||
|
@ -143,7 +150,7 @@ func Start() (err error) {
|
||||||
// get and set file loglevels
|
// get and set file loglevels
|
||||||
pkgLogLevels := pkgLogLevelsFlag
|
pkgLogLevels := pkgLogLevelsFlag
|
||||||
if len(pkgLogLevels) > 0 {
|
if len(pkgLogLevels) > 0 {
|
||||||
newPkgLevels := make(map[string]severity)
|
newPkgLevels := make(map[string]Severity)
|
||||||
for _, pair := range strings.Split(pkgLogLevels, ",") {
|
for _, pair := range strings.Split(pkgLogLevels, ",") {
|
||||||
splitted := strings.Split(pair, "=")
|
splitted := strings.Split(pair, "=")
|
||||||
if len(splitted) != 2 {
|
if len(splitted) != 2 {
|
||||||
|
@ -170,7 +177,7 @@ func Start() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown writes remaining log lines and then stops the logger.
|
// Shutdown writes remaining log lines and then stops the log system.
|
||||||
func Shutdown() {
|
func Shutdown() {
|
||||||
close(shutdownSignal)
|
close(shutdownSignal)
|
||||||
shutdownWaitGroup.Wait()
|
shutdownWaitGroup.Wait()
|
||||||
|
|
|
@ -3,10 +3,29 @@ package log
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/safing/portbase/taskmanager"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
schedulingEnabled = false
|
||||||
|
writeTrigger = make(chan struct{})
|
||||||
|
)
|
||||||
|
|
||||||
|
// EnableScheduling enables external scheduling of the logger. This will require to manually trigger writes via TriggerWrite whenevery logs should be written. Please note that full buffers will also trigger writing. Must be called before Start() to have an effect.
|
||||||
|
func EnableScheduling() {
|
||||||
|
if !initializing.IsSet() {
|
||||||
|
schedulingEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerWriter triggers log output writing.
|
||||||
|
func TriggerWriter() {
|
||||||
|
if started.IsSet() && schedulingEnabled {
|
||||||
|
select {
|
||||||
|
case writeTrigger <- struct{}{}:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func writeLine(line *logLine, duplicates uint64) {
|
func writeLine(line *logLine, duplicates uint64) {
|
||||||
fmt.Println(formatLine(line, duplicates, true))
|
fmt.Println(formatLine(line, duplicates, true))
|
||||||
// TODO: implement file logging and setting console/file logging
|
// TODO: implement file logging and setting console/file logging
|
||||||
|
@ -23,7 +42,6 @@ func writer() {
|
||||||
var line *logLine
|
var line *logLine
|
||||||
var lastLine *logLine
|
var lastLine *logLine
|
||||||
var duplicates uint64
|
var duplicates uint64
|
||||||
startedTask := false
|
|
||||||
defer shutdownWaitGroup.Done()
|
defer shutdownWaitGroup.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -41,8 +59,7 @@ func writer() {
|
||||||
|
|
||||||
// wait for timeslot to log, or when buffer is full
|
// wait for timeslot to log, or when buffer is full
|
||||||
select {
|
select {
|
||||||
case <-taskmanager.StartVeryLowPriorityMicroTask():
|
case <-writeTrigger:
|
||||||
startedTask = true
|
|
||||||
case <-forceEmptyingOfBuffer:
|
case <-forceEmptyingOfBuffer:
|
||||||
case <-shutdownSignal:
|
case <-shutdownSignal:
|
||||||
for {
|
for {
|
||||||
|
@ -89,10 +106,6 @@ func writer() {
|
||||||
writeLine(line, duplicates)
|
writeLine(line, duplicates)
|
||||||
duplicates = 0
|
duplicates = 0
|
||||||
default:
|
default:
|
||||||
if startedTask {
|
|
||||||
taskmanager.EndMicroTask()
|
|
||||||
startedTask = false
|
|
||||||
}
|
|
||||||
break writeLoop
|
break writeLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
279
log/trace.go
279
log/trace.go
|
@ -5,41 +5,35 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Key for context value
|
// ContextTracerKey is the key used for the context key/value storage.
|
||||||
type ContextTracerKey struct{}
|
type ContextTracerKey struct{}
|
||||||
|
|
||||||
|
// ContextTracer is attached to a context in order bind logs to a context.
|
||||||
type ContextTracer struct {
|
type ContextTracer struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
actions []*Action
|
logs []*logLine
|
||||||
}
|
|
||||||
|
|
||||||
type Action struct {
|
|
||||||
timestamp time.Time
|
|
||||||
level severity
|
|
||||||
msg string
|
|
||||||
file string
|
|
||||||
line int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key = ContextTracerKey{}
|
key = ContextTracerKey{}
|
||||||
nilTracer *ContextTracer
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddTracer(ctx context.Context) context.Context {
|
// AddTracer adds a ContextTracer to the returned Context. Will return a nil ContextTracer if one already exists. Will return a nil context if nil.
|
||||||
if ctx != nil && fastcheckLevel(TraceLevel) {
|
func AddTracer(ctx context.Context) (context.Context, *ContextTracer) {
|
||||||
|
if ctx != nil && fastcheck(TraceLevel) {
|
||||||
_, ok := ctx.Value(key).(*ContextTracer)
|
_, ok := ctx.Value(key).(*ContextTracer)
|
||||||
if !ok {
|
if !ok {
|
||||||
return context.WithValue(ctx, key, &ContextTracer{})
|
tracer := &ContextTracer{}
|
||||||
|
return context.WithValue(ctx, key, tracer), tracer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ctx
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tracer returns the ContextTracer previously added to the given Context.
|
||||||
func Tracer(ctx context.Context) *ContextTracer {
|
func Tracer(ctx context.Context) *ContextTracer {
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
tracer, ok := ctx.Value(key).(*ContextTracer)
|
||||||
|
@ -47,10 +41,59 @@ func Tracer(ctx context.Context) *ContextTracer {
|
||||||
return tracer
|
return tracer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nilTracer
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) logTrace(level severity, msg string) {
|
// Submit collected logs on the context for further processing/outputting. Does nothing if called on a nil ContextTracer.
|
||||||
|
func (tracer *ContextTracer) Submit() {
|
||||||
|
if tracer != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !started.IsSet() {
|
||||||
|
// a bit resouce intense, but keeps logs before logging started.
|
||||||
|
// FIXME: create option to disable logging
|
||||||
|
go func() {
|
||||||
|
<-startedSignal
|
||||||
|
tracer.Submit()
|
||||||
|
}()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tracer.logs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract last line as main line
|
||||||
|
mainLine := tracer.logs[len(tracer.logs)-1]
|
||||||
|
tracer.logs = tracer.logs[:len(tracer.logs)-1]
|
||||||
|
|
||||||
|
// create log object
|
||||||
|
log := &logLine{
|
||||||
|
msg: mainLine.msg,
|
||||||
|
tracer: tracer,
|
||||||
|
level: mainLine.level,
|
||||||
|
timestamp: mainLine.timestamp,
|
||||||
|
file: mainLine.file,
|
||||||
|
line: mainLine.line,
|
||||||
|
}
|
||||||
|
|
||||||
|
// send log to processing
|
||||||
|
select {
|
||||||
|
case logBuffer <- log:
|
||||||
|
default:
|
||||||
|
forceEmptyingOfBuffer <- true
|
||||||
|
logBuffer <- log
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake up writer if necessary
|
||||||
|
if logsWaitingFlag.SetToIf(false, true) {
|
||||||
|
logsWaiting <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracer *ContextTracer) log(level Severity, msg string) {
|
||||||
// get file and line
|
// get file and line
|
||||||
_, file, line, ok := runtime.Caller(2)
|
_, file, line, ok := runtime.Caller(2)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -64,9 +107,9 @@ func (ct *ContextTracer) logTrace(level severity, msg string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ct.Lock()
|
tracer.Lock()
|
||||||
defer ct.Unlock()
|
defer tracer.Unlock()
|
||||||
ct.actions = append(ct.actions, &Action{
|
tracer.logs = append(tracer.logs, &logLine{
|
||||||
timestamp: time.Now(),
|
timestamp: time.Now(),
|
||||||
level: level,
|
level: level,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
|
@ -75,146 +118,122 @@ func (ct *ContextTracer) logTrace(level severity, msg string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Tracef(things ...interface{}) (ok bool) {
|
// Trace is used to log tiny steps. Log traces to context if you can!
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Trace(msg string) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(TraceLevel, fmt.Sprintf(things[0].(string), things[1:]...))
|
case tracer != nil:
|
||||||
}
|
tracer.log(TraceLevel, msg)
|
||||||
return true
|
case fastcheck(TraceLevel):
|
||||||
|
log(TraceLevel, msg, nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Trace(msg string) (ok bool) {
|
// Tracef is used to log tiny steps. Log traces to context if you can!
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Tracef(format string, things ...interface{}) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(TraceLevel, msg)
|
case tracer != nil:
|
||||||
}
|
tracer.log(TraceLevel, fmt.Sprintf(format, things...))
|
||||||
return true
|
case fastcheck(TraceLevel):
|
||||||
|
log(TraceLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Warningf(things ...interface{}) (ok bool) {
|
// Debug is used to log minor errors or unexpected events. These occurences are usually not worth mentioning in itself, but they might hint at a bigger problem.
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Debug(msg string) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(WarningLevel, fmt.Sprintf(things[0].(string), things[1:]...))
|
case tracer != nil:
|
||||||
}
|
tracer.log(DebugLevel, msg)
|
||||||
return true
|
case fastcheck(DebugLevel):
|
||||||
|
log(DebugLevel, msg, nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Warning(msg string) (ok bool) {
|
// Debugf is used to log minor errors or unexpected events. These occurences are usually not worth mentioning in itself, but they might hint at a bigger problem.
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Debugf(format string, things ...interface{}) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(WarningLevel, msg)
|
case tracer != nil:
|
||||||
}
|
tracer.log(DebugLevel, fmt.Sprintf(format, things...))
|
||||||
return true
|
case fastcheck(DebugLevel):
|
||||||
|
log(DebugLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Errorf(things ...interface{}) (ok bool) {
|
// Info is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Info(msg string) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(ErrorLevel, fmt.Sprintf(things[0].(string), things[1:]...))
|
case tracer != nil:
|
||||||
}
|
tracer.log(InfoLevel, msg)
|
||||||
return true
|
case fastcheck(InfoLevel):
|
||||||
|
log(InfoLevel, msg, nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *ContextTracer) Error(msg string) (ok bool) {
|
// Infof is used to log mildly significant events. Should be used to inform about somewhat bigger or user affecting events that happen.
|
||||||
if ct != nil {
|
func (tracer *ContextTracer) Infof(format string, things ...interface{}) {
|
||||||
if fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
ct.logTrace(ErrorLevel, msg)
|
case tracer != nil:
|
||||||
}
|
tracer.log(InfoLevel, fmt.Sprintf(format, things...))
|
||||||
return true
|
case fastcheck(InfoLevel):
|
||||||
|
log(InfoLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DebugTrace(ctx context.Context, msg string) (ok bool) {
|
// Warning is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Warning(msg string) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(DebugLevel, msg, tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(WarningLevel, msg)
|
||||||
|
case fastcheck(WarningLevel):
|
||||||
|
log(WarningLevel, msg, nil)
|
||||||
}
|
}
|
||||||
log(DebugLevel, msg, nil)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DebugTracef(ctx context.Context, things ...interface{}) (ok bool) {
|
// Warningf is used to log (potentially) bad events, but nothing broke (even a little) and there is no need to panic yet.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Warningf(format string, things ...interface{}) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(DebugLevel, fmt.Sprintf(things[0].(string), things[1:]...), tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(WarningLevel, fmt.Sprintf(format, things...))
|
||||||
|
case fastcheck(WarningLevel):
|
||||||
|
log(WarningLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
log(DebugLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoTrace(ctx context.Context, msg string) (ok bool) {
|
// Error is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational. Maybe User/Admin should be informed.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Error(msg string) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(InfoLevel, msg, tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(ErrorLevel, msg)
|
||||||
|
case fastcheck(ErrorLevel):
|
||||||
|
log(ErrorLevel, msg, nil)
|
||||||
}
|
}
|
||||||
log(InfoLevel, msg, nil)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoTracef(ctx context.Context, things ...interface{}) (ok bool) {
|
// Errorf is used to log errors that break or impair functionality. The task/process may have to be aborted and tried again later. The system is still operational.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Errorf(format string, things ...interface{}) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(InfoLevel, fmt.Sprintf(things[0].(string), things[1:]...), tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(ErrorLevel, fmt.Sprintf(format, things...))
|
||||||
|
case fastcheck(ErrorLevel):
|
||||||
|
log(ErrorLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
log(InfoLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WarningTrace(ctx context.Context, msg string) (ok bool) {
|
// Critical is used to log events that completely break the system. Operation connot continue. User/Admin must be informed.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Critical(msg string) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(WarningLevel, msg, tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(CriticalLevel, msg)
|
||||||
|
case fastcheck(CriticalLevel):
|
||||||
|
log(CriticalLevel, msg, nil)
|
||||||
}
|
}
|
||||||
log(WarningLevel, msg, nil)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WarningTracef(ctx context.Context, things ...interface{}) (ok bool) {
|
// Criticalf is used to log events that completely break the system. Operation connot continue. User/Admin must be informed.
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
func (tracer *ContextTracer) Criticalf(format string, things ...interface{}) {
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
switch {
|
||||||
log(WarningLevel, fmt.Sprintf(things[0].(string), things[1:]...), tracer)
|
case tracer != nil:
|
||||||
return true
|
tracer.log(CriticalLevel, fmt.Sprintf(format, things...))
|
||||||
|
case fastcheck(CriticalLevel):
|
||||||
|
log(CriticalLevel, fmt.Sprintf(format, things...), nil)
|
||||||
}
|
}
|
||||||
log(WarningLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorTrace(ctx context.Context, msg string) (ok bool) {
|
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
|
||||||
log(ErrorLevel, msg, tracer)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
log(ErrorLevel, msg, nil)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorTracef(ctx context.Context, things ...interface{}) (ok bool) {
|
|
||||||
tracer, ok := ctx.Value(key).(*ContextTracer)
|
|
||||||
if ok && fastcheckLevel(TraceLevel) {
|
|
||||||
log(ErrorLevel, fmt.Sprintf(things[0].(string), things[1:]...), tracer)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
log(ErrorLevel, fmt.Sprintf(things[0].(string), things[1:]...), nil)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func fastcheckLevel(level severity) bool {
|
|
||||||
return uint32(level) >= atomic.LoadUint32(logLevel)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,20 +12,22 @@ func TestContextTracer(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := AddTracer(context.Background())
|
ctx, tracer := AddTracer(context.Background())
|
||||||
|
_ = Tracer(ctx)
|
||||||
|
|
||||||
Tracer(ctx).Trace("api: request received, checking security")
|
tracer.Trace("api: request received, checking security")
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
Tracer(ctx).Trace("login: logging in user")
|
tracer.Trace("login: logging in user")
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
Tracer(ctx).Trace("database: fetching requested resources")
|
tracer.Trace("database: fetching requested resources")
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
Tracer(ctx).Warning("database: partial failure")
|
tracer.Warning("database: partial failure")
|
||||||
time.Sleep(10 * time.Microsecond)
|
time.Sleep(10 * time.Microsecond)
|
||||||
Tracer(ctx).Trace("renderer: rendering output")
|
tracer.Trace("renderer: rendering output")
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
Tracer(ctx).Trace("api: returning request")
|
tracer.Trace("api: returning request")
|
||||||
|
|
||||||
DebugTrace(ctx, "api: completed request")
|
tracer.Trace("api: completed request")
|
||||||
|
tracer.Submit()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue