diff options
Diffstat (limited to 'vendor/github.com/mattermost/logr/v2/logrec.go')
-rw-r--r-- | vendor/github.com/mattermost/logr/v2/logrec.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/vendor/github.com/mattermost/logr/v2/logrec.go b/vendor/github.com/mattermost/logr/v2/logrec.go new file mode 100644 index 00000000..76d51b9e --- /dev/null +++ b/vendor/github.com/mattermost/logr/v2/logrec.go @@ -0,0 +1,182 @@ +package logr + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" + "sync" + "time" +) + +// 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 + + msg string + newline bool + fields []Field + + stackPC []uintptr + stackCount int + + // flushes Logr and target queues when not nil. + flush chan struct{} + + // remaining fields calculated by `prep` + frames []runtime.Frame + fieldsAll []Field + caller string +} + +// NewLogRec creates a new LogRec with the current time and optional stack trace. +func NewLogRec(lvl Level, logger Logger, msg string, fields []Field, incStacktrace bool) *LogRec { + rec := &LogRec{time: time.Now(), logger: logger, level: lvl, msg: msg, fields: fields} + 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 stack trace to frames. +func (rec *LogRec) prep() { + rec.mux.Lock() + defer rec.mux.Unlock() + + // include log rec fields and logger fields added via "With" + rec.fieldsAll = make([]Field, 0, len(rec.fields)+len(rec.logger.fields)) + rec.fieldsAll = append(rec.fieldsAll, rec.logger.fields...) + rec.fieldsAll = append(rec.fieldsAll, rec.fields...) + + filter := rec.logger.lgr.options.stackFilter + + // resolve stack trace + if rec.stackCount > 0 { + rec.frames = make([]runtime.Frame, 0, rec.stackCount) + frames := runtime.CallersFrames(rec.stackPC[:rec.stackCount]) + for { + frame, more := frames.Next() + + // remove all package entries that are in filter. + pkg := ResolvePackageName(frame.Function) + if _, ok := filter[pkg]; !ok && pkg != "" { + rec.frames = append(rec.frames, frame) + } + + if !more { + break + } + } + } + + // calc caller if stack trace provided + if len(rec.frames) > 0 { + rec.caller = calcCaller(rec.frames) + } +} + +// 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, + msg: rec.msg, + newline: rec.newline, + fields: rec.fields, + 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() []Field { + // no locking needed as this field is not mutated. + return rec.fieldsAll +} + +// 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 +} + +// Caller returns this log record's caller info, meaning the file and line +// number where this log record was emitted. Returns empty string if no +// stack trace was provided. +func (rec *LogRec) Caller() string { + rec.mux.RLock() + defer rec.mux.RUnlock() + return rec.caller +} + +// 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.lgr.BorrowBuffer() + defer rec.logger.lgr.ReleaseBuffer(buf) + buf, _ = f.Format(rec, rec.Level(), buf) + return strings.TrimSpace(buf.String()) +} + +func calcCaller(frames []runtime.Frame) string { + for _, frame := range frames { + if frame.File == "" { + continue + } + + dir, file := filepath.Split(frame.File) + base := filepath.Base(dir) + + return fmt.Sprintf("%s/%s:%d", base, file, frame.Line) + } + return "" +} |