Files
mostlymatter/vendor/github.com/mattermost/logr/logrec.go
Doug Lauder 90ff87a77f MM-25731 Advanced Logging (#14888)
Adds Advanced Logging to server. Advanced Logging is an optional logging capability that allows customers to send log records to any number of destinations.

Supported destinations:
- file
- syslog (with out without TLS)
- raw TCP socket (with out without TLS)

Allows developers to specify discrete log levels as well as the standard trace, debug, info, ... panic. Existing code and logging API usage is unchanged.

Log records are emitted asynchronously to reduce latency to the caller. Supports hot-reloading of logger config, including adding removing targets.

Advanced Logging is configured within config.json via "LogSettings.AdvancedLoggingConfig" which can contain a filespec to another config file, a database DSN, or JSON.
2020-07-15 14:40:36 -04:00

190 строки
4.4 KiB
Go

package logr
import (
"fmt"
"runtime"
"strings"
"sync"
"time"
)
var (
logrPkg string
)
func init() {
// Calc current package name
pcs := make([]uintptr, 2)
_ = runtime.Callers(0, pcs)
tmp := runtime.FuncForPC(pcs[1]).Name()
logrPkg = getPackageName(tmp)
}
// LogRec collects raw, unformatted data to be logged.
// TODO: pool these? how to reliably know when targets are done with them? Copy for each target?
type LogRec struct {
mux sync.RWMutex
time time.Time
level Level
logger Logger
template string
newline bool
args []interface{}
stackPC []uintptr
stackCount int
// flushes Logr and target queues when not nil.
flush chan struct{}
// remaining fields calculated by `prep`
msg string
frames []runtime.Frame
}
// NewLogRec creates a new LogRec with the current time and optional stack trace.
func NewLogRec(lvl Level, logger Logger, template string, args []interface{}, incStacktrace bool) *LogRec {
rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args}
if incStacktrace {
rec.stackPC = make([]uintptr, DefaultMaxStackFrames)
rec.stackCount = runtime.Callers(2, rec.stackPC)
}
return rec
}
// newFlushLogRec creates a LogRec that flushes the Logr queue and
// any target queues that support flushing.
func newFlushLogRec(logger Logger) *LogRec {
return &LogRec{logger: logger, flush: make(chan struct{})}
}
// prep resolves all args and field values to strings, and
// resolves stack trace to frames.
func (rec *LogRec) prep() {
rec.mux.Lock()
defer rec.mux.Unlock()
// resolve args
if rec.template == "" {
if rec.newline {
rec.msg = fmt.Sprintln(rec.args...)
} else {
rec.msg = fmt.Sprint(rec.args...)
}
} else {
rec.msg = fmt.Sprintf(rec.template, rec.args...)
}
// resolve stack trace
if rec.stackCount > 0 {
frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount])
for {
f, more := frames.Next()
rec.frames = append(rec.frames, f)
if !more {
break
}
}
// remove leading logr package entries.
var start int
for i, frame := range rec.frames {
pkg := getPackageName(frame.Function)
if pkg != "" && pkg != logrPkg {
start = i
break
}
}
rec.frames = rec.frames[start:]
}
}
// WithTime returns a shallow copy of the log record while replacing
// the time. This can be used by targets and formatters to adjust
// the time, or take ownership of the log record.
func (rec *LogRec) WithTime(time time.Time) *LogRec {
rec.mux.RLock()
defer rec.mux.RUnlock()
return &LogRec{
time: time,
level: rec.level,
logger: rec.logger,
template: rec.template,
newline: rec.newline,
args: rec.args,
msg: rec.msg,
stackPC: rec.stackPC,
stackCount: rec.stackCount,
frames: rec.frames,
}
}
// Logger returns the `Logger` that created this `LogRec`.
func (rec *LogRec) Logger() Logger {
return rec.logger
}
// Time returns this log record's time stamp.
func (rec *LogRec) Time() time.Time {
// no locking needed as this field is not mutated.
return rec.time
}
// Level returns this log record's Level.
func (rec *LogRec) Level() Level {
// no locking needed as this field is not mutated.
return rec.level
}
// Fields returns this log record's Fields.
func (rec *LogRec) Fields() Fields {
// no locking needed as this field is not mutated.
return rec.logger.fields
}
// Msg returns this log record's message text.
func (rec *LogRec) Msg() string {
rec.mux.RLock()
defer rec.mux.RUnlock()
return rec.msg
}
// StackFrames returns this log record's stack frames or
// nil if no stack trace was required.
func (rec *LogRec) StackFrames() []runtime.Frame {
rec.mux.RLock()
defer rec.mux.RUnlock()
return rec.frames
}
// String returns a string representation of this log record.
func (rec *LogRec) String() string {
if rec.flush != nil {
return "[flusher]"
}
f := &DefaultFormatter{}
buf := rec.logger.logr.BorrowBuffer()
defer rec.logger.logr.ReleaseBuffer(buf)
buf, _ = f.Format(rec, true, buf)
return strings.TrimSpace(buf.String())
}
// getPackageName reduces a fully qualified function name to the package name
// By sirupsen: https://github.com/sirupsen/logrus/blob/master/entry.go
func getPackageName(f string) string {
for {
lastPeriod := strings.LastIndex(f, ".")
lastSlash := strings.LastIndex(f, "/")
if lastPeriod > lastSlash {
f = f[:lastPeriod]
} else {
break
}
}
return f
}