MM-36764 mlog refactor (#18118)
Refactor mlog - simplify mlog by removing redundant code - remove Zap dependency - update unit test helpers - update logging config - update auditing
Этот коммит содержится в:
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
11
vendor/github.com/mattermost/logr/config.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wiggin77/cfg"
|
||||
)
|
||||
|
||||
func ConfigLogger(config *cfg.Config) error {
|
||||
return fmt.Errorf("Not implemented yet")
|
||||
}
|
||||
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
26
vendor/github.com/mattermost/logr/filter.go
сгенерированный
поставляемый
@@ -1,26 +0,0 @@
|
||||
package logr
|
||||
|
||||
// LevelID is the unique id of each level.
|
||||
type LevelID uint
|
||||
|
||||
// Level provides a mechanism to enable/disable specific log lines.
|
||||
type Level struct {
|
||||
ID LevelID
|
||||
Name string
|
||||
Stacktrace bool
|
||||
}
|
||||
|
||||
// String returns the name of this level.
|
||||
func (level Level) String() string {
|
||||
return level.Name
|
||||
}
|
||||
|
||||
// Filter allows targets to determine which Level(s) are active
|
||||
// for logging and which Level(s) require a stack trace to be output.
|
||||
// A default implementation using "panic, fatal..." is provided, and
|
||||
// a more flexible alternative implementation is also provided that
|
||||
// allows any number of custom levels.
|
||||
type Filter interface {
|
||||
IsEnabled(Level) bool
|
||||
IsStacktraceEnabled(Level) bool
|
||||
}
|
||||
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
273
vendor/github.com/mattermost/logr/format/json.go
сгенерированный
поставляемый
@@ -1,273 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// ContextField is a name/value pair within the context fields.
|
||||
type ContextField struct {
|
||||
Key string
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
// JSON formats log records as JSON.
|
||||
type JSON struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool
|
||||
// DisableContext disables output of all context fields.
|
||||
DisableContext bool
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string
|
||||
|
||||
// Deprecated: this has no effect.
|
||||
Indent string
|
||||
|
||||
// EscapeHTML determines if certain characters (e.g. `<`, `>`, `&`)
|
||||
// are escaped.
|
||||
EscapeHTML bool
|
||||
|
||||
// KeyTimestamp overrides the timestamp field key name.
|
||||
KeyTimestamp string
|
||||
|
||||
// KeyLevel overrides the level field key name.
|
||||
KeyLevel string
|
||||
|
||||
// KeyMsg overrides the msg field key name.
|
||||
KeyMsg string
|
||||
|
||||
// KeyContextFields when not empty will group all context fields
|
||||
// under this key.
|
||||
KeyContextFields string
|
||||
|
||||
// KeyStacktrace overrides the stacktrace field key name.
|
||||
KeyStacktrace string
|
||||
|
||||
// ContextSorter allows custom sorting for the context fields.
|
||||
ContextSorter func(fields logr.Fields) []ContextField
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in JSON format.
|
||||
func (j *JSON) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
j.once.Do(j.applyDefaultKeyNames)
|
||||
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
sorter := j.ContextSorter
|
||||
if sorter == nil {
|
||||
sorter = j.defaultContextSorter
|
||||
}
|
||||
|
||||
jlr := JSONLogRec{
|
||||
LogRec: rec,
|
||||
JSON: j,
|
||||
stacktrace: stacktrace,
|
||||
sorter: sorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(jlr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (j *JSON) applyDefaultKeyNames() {
|
||||
if j.KeyTimestamp == "" {
|
||||
j.KeyTimestamp = "timestamp"
|
||||
}
|
||||
if j.KeyLevel == "" {
|
||||
j.KeyLevel = "level"
|
||||
}
|
||||
if j.KeyMsg == "" {
|
||||
j.KeyMsg = "msg"
|
||||
}
|
||||
if j.KeyStacktrace == "" {
|
||||
j.KeyStacktrace = "stacktrace"
|
||||
}
|
||||
}
|
||||
|
||||
// defaultContextSorter sorts the context fields alphabetically by key.
|
||||
func (j *JSON) defaultContextSorter(fields logr.Fields) []ContextField {
|
||||
keys := make([]string, 0, len(fields))
|
||||
for k := range fields {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
cf := make([]ContextField, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
cf = append(cf, ContextField{Key: k, Val: fields[k]})
|
||||
}
|
||||
return cf
|
||||
}
|
||||
|
||||
// JSONLogRec decorates a LogRec adding JSON encoding.
|
||||
type JSONLogRec struct {
|
||||
*logr.LogRec
|
||||
*JSON
|
||||
stacktrace bool
|
||||
sorter func(fields logr.Fields) []ContextField
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (rec JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
if !rec.DisableTimestamp {
|
||||
timestampFmt := rec.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
time := rec.Time()
|
||||
enc.AddTimeKey(rec.KeyTimestamp, &time, timestampFmt)
|
||||
}
|
||||
if !rec.DisableLevel {
|
||||
enc.AddStringKey(rec.KeyLevel, rec.Level().Name)
|
||||
}
|
||||
if !rec.DisableMsg {
|
||||
enc.AddStringKey(rec.KeyMsg, rec.Msg())
|
||||
}
|
||||
if !rec.DisableContext {
|
||||
ctxFields := rec.sorter(rec.Fields())
|
||||
if rec.KeyContextFields != "" {
|
||||
enc.AddObjectKey(rec.KeyContextFields, jsonFields(ctxFields))
|
||||
} else {
|
||||
if len(ctxFields) > 0 {
|
||||
for _, cf := range ctxFields {
|
||||
key := rec.prefixCollision(cf.Key)
|
||||
encodeField(enc, key, cf.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if rec.stacktrace && !rec.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
enc.AddArrayKey(rec.KeyStacktrace, stackFrames(frames))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// IsNil returns true if the LogRec pointer is nil.
|
||||
func (rec JSONLogRec) IsNil() bool {
|
||||
return rec.LogRec == nil
|
||||
}
|
||||
|
||||
func (rec JSONLogRec) prefixCollision(key string) string {
|
||||
switch key {
|
||||
case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace:
|
||||
return rec.prefixCollision("_" + key)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
type stackFrames []runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrames slice as JSON.
|
||||
func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) {
|
||||
for _, frame := range s {
|
||||
enc.AddObject(stackFrame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if stackFrames is empty slice.
|
||||
func (s stackFrames) IsNil() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
type stackFrame runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrame as JSON.
|
||||
func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey("Function", f.Function)
|
||||
enc.AddStringKey("File", f.File)
|
||||
enc.AddIntKey("Line", f.Line)
|
||||
}
|
||||
|
||||
func (f stackFrame) IsNil() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type jsonFields []ContextField
|
||||
|
||||
// MarshalJSONObject encodes Fields map to JSON.
|
||||
func (f jsonFields) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
for _, ctxField := range f {
|
||||
encodeField(enc, ctxField.Key, ctxField.Val)
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if map is nil.
|
||||
func (f jsonFields) IsNil() bool {
|
||||
return f == nil
|
||||
}
|
||||
|
||||
func encodeField(enc *gojay.Encoder, key string, val interface{}) {
|
||||
switch vt := val.(type) {
|
||||
case gojay.MarshalerJSONObject:
|
||||
enc.AddObjectKey(key, vt)
|
||||
case gojay.MarshalerJSONArray:
|
||||
enc.AddArrayKey(key, vt)
|
||||
case string:
|
||||
enc.AddStringKey(key, vt)
|
||||
case error:
|
||||
enc.AddStringKey(key, vt.Error())
|
||||
case bool:
|
||||
enc.AddBoolKey(key, vt)
|
||||
case int:
|
||||
enc.AddIntKey(key, vt)
|
||||
case int64:
|
||||
enc.AddInt64Key(key, vt)
|
||||
case int32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case int8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint64:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint32:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint16:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case uint8:
|
||||
enc.AddIntKey(key, int(vt))
|
||||
case float64:
|
||||
enc.AddFloatKey(key, vt)
|
||||
case float32:
|
||||
enc.AddFloat32Key(key, vt)
|
||||
case *gojay.EmbeddedJSON:
|
||||
enc.AddEmbeddedJSONKey(key, vt)
|
||||
case time.Time:
|
||||
enc.AddTimeKey(key, &vt, logr.DefTimestampFormat)
|
||||
case *time.Time:
|
||||
enc.AddTimeKey(key, vt, logr.DefTimestampFormat)
|
||||
default:
|
||||
s := fmt.Sprintf("%v", vt)
|
||||
enc.AddStringKey(key, s)
|
||||
}
|
||||
}
|
||||
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
75
vendor/github.com/mattermost/logr/format/plain.go
сгенерированный
поставляемый
@@ -1,75 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// Plain is the simplest formatter, outputting only text with
|
||||
// no colors.
|
||||
type Plain struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool
|
||||
// DisableContext disables output of all context fields.
|
||||
DisableContext bool
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool
|
||||
|
||||
// Delim is an optional delimiter output between each log field.
|
||||
// Defaults to a single space.
|
||||
Delim string
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *Plain) Format(rec *logr.LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
delim := p.Delim
|
||||
if delim == "" {
|
||||
delim = " "
|
||||
}
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
timestampFmt := p.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
|
||||
if !p.DisableTimestamp {
|
||||
var arr [128]byte
|
||||
tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt)
|
||||
buf.Write(tbuf)
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
if !p.DisableLevel {
|
||||
fmt.Fprintf(buf, "%v%s", rec.Level().Name, delim)
|
||||
}
|
||||
if !p.DisableMsg {
|
||||
fmt.Fprint(buf, rec.Msg(), delim)
|
||||
}
|
||||
if !p.DisableContext {
|
||||
ctx := rec.Fields()
|
||||
if len(ctx) > 0 {
|
||||
logr.WriteFields(buf, ctx, " ")
|
||||
}
|
||||
}
|
||||
if stacktrace && !p.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
logr.WriteStacktrace(buf, rec.StackFrames())
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
return buf, nil
|
||||
}
|
||||
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
119
vendor/github.com/mattermost/logr/formatter.go
сгенерированный
поставляемый
@@ -1,119 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Formatter turns a LogRec into a formatted string.
|
||||
type Formatter interface {
|
||||
// Format converts a log record to bytes. If buf is not nil then it will be
|
||||
// be filled with the formatted results, otherwise a new buffer will be allocated.
|
||||
Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefTimestampFormat is the default time stamp format used by
|
||||
// Plain formatter and others.
|
||||
DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
|
||||
)
|
||||
|
||||
// DefaultFormatter is the default formatter, outputting only text with
|
||||
// no colors and a space delimiter. Use `format.Plain` instead.
|
||||
type DefaultFormatter struct {
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *DefaultFormatter) Format(rec *LogRec, stacktrace bool, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
delim := " "
|
||||
timestampFmt := DefTimestampFormat
|
||||
|
||||
fmt.Fprintf(buf, "%s%s", rec.Time().Format(timestampFmt), delim)
|
||||
fmt.Fprintf(buf, "%v%s", rec.Level(), delim)
|
||||
fmt.Fprint(buf, rec.Msg(), delim)
|
||||
|
||||
ctx := rec.Fields()
|
||||
if len(ctx) > 0 {
|
||||
WriteFields(buf, ctx, " ")
|
||||
}
|
||||
|
||||
if stacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
WriteStacktrace(buf, rec.StackFrames())
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// WriteFields writes zero or more name value pairs to the io.Writer.
|
||||
// The pairs are sorted by key name and output in key=value format
|
||||
// with optional separator between fields.
|
||||
func WriteFields(w io.Writer, flds Fields, separator string) {
|
||||
keys := make([]string, 0, len(flds))
|
||||
for k := range flds {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
sep := ""
|
||||
for _, key := range keys {
|
||||
writeField(w, key, flds[key], sep)
|
||||
sep = separator
|
||||
}
|
||||
}
|
||||
|
||||
func writeField(w io.Writer, key string, val interface{}, sep string) {
|
||||
var template string
|
||||
switch v := val.(type) {
|
||||
case error:
|
||||
val := v.Error()
|
||||
if shouldQuote(val) {
|
||||
template = "%s%s=%q"
|
||||
} else {
|
||||
template = "%s%s=%s"
|
||||
}
|
||||
case string:
|
||||
if shouldQuote(v) {
|
||||
template = "%s%s=%q"
|
||||
} else {
|
||||
template = "%s%s=%s"
|
||||
}
|
||||
default:
|
||||
template = "%s%s=%v"
|
||||
}
|
||||
fmt.Fprintf(w, template, sep, key, val)
|
||||
}
|
||||
|
||||
// shouldQuote returns true if val contains any characters that might be unsafe
|
||||
// when injecting log output into an aggregator, viewer or report.
|
||||
func shouldQuote(val string) bool {
|
||||
for _, c := range val {
|
||||
if !((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z')) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteStacktrace formats and outputs a stack trace to an io.Writer.
|
||||
func WriteStacktrace(w io.Writer, frames []runtime.Frame) {
|
||||
for _, frame := range frames {
|
||||
if frame.Function != "" {
|
||||
fmt.Fprintf(w, " %s\n", frame.Function)
|
||||
}
|
||||
if frame.File != "" {
|
||||
fmt.Fprintf(w, " %s:%d\n", frame.File, frame.Line)
|
||||
}
|
||||
}
|
||||
}
|
||||
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
45
vendor/github.com/mattermost/logr/levelcustom.go
сгенерированный
поставляемый
@@ -1,45 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CustomFilter allows targets to enable logging via a list of levels.
|
||||
type CustomFilter struct {
|
||||
mux sync.RWMutex
|
||||
levels map[LevelID]Level
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level exists in this list.
|
||||
func (st *CustomFilter) IsEnabled(level Level) bool {
|
||||
st.mux.RLock()
|
||||
defer st.mux.RUnlock()
|
||||
_, ok := st.levels[level.ID]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (st *CustomFilter) IsStacktraceEnabled(level Level) bool {
|
||||
st.mux.RLock()
|
||||
defer st.mux.RUnlock()
|
||||
lvl, ok := st.levels[level.ID]
|
||||
if ok {
|
||||
return lvl.Stacktrace
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add adds one or more levels to the list. Adding a level enables logging for
|
||||
// that level on any targets using this CustomFilter.
|
||||
func (st *CustomFilter) Add(levels ...Level) {
|
||||
st.mux.Lock()
|
||||
defer st.mux.Unlock()
|
||||
|
||||
if st.levels == nil {
|
||||
st.levels = make(map[LevelID]Level)
|
||||
}
|
||||
|
||||
for _, s := range levels {
|
||||
st.levels[s.ID] = s
|
||||
}
|
||||
}
|
||||
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
37
vendor/github.com/mattermost/logr/levelstd.go
сгенерированный
поставляемый
@@ -1,37 +0,0 @@
|
||||
package logr
|
||||
|
||||
// StdFilter allows targets to filter via classic log levels where any level
|
||||
// beyond a certain verbosity/severity is enabled.
|
||||
type StdFilter struct {
|
||||
Lvl Level
|
||||
Stacktrace Level
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level is at or above this verbosity. Also
|
||||
// determines if a stack trace is required.
|
||||
func (lt StdFilter) IsEnabled(level Level) bool {
|
||||
return level.ID <= lt.Lvl.ID
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (lt StdFilter) IsStacktraceEnabled(level Level) bool {
|
||||
return level.ID <= lt.Stacktrace.ID
|
||||
}
|
||||
|
||||
var (
|
||||
// Panic is the highest level of severity. Logs the message and then panics.
|
||||
Panic = Level{ID: 0, Name: "panic"}
|
||||
// Fatal designates a catastrophic error. Logs the message and then calls
|
||||
// `logr.Exit(1)`.
|
||||
Fatal = Level{ID: 1, Name: "fatal"}
|
||||
// Error designates a serious but possibly recoverable error.
|
||||
Error = Level{ID: 2, Name: "error"}
|
||||
// Warn designates non-critical error.
|
||||
Warn = Level{ID: 3, Name: "warn"}
|
||||
// Info designates information regarding application events.
|
||||
Info = Level{ID: 4, Name: "info"}
|
||||
// Debug designates verbose information typically used for debugging.
|
||||
Debug = Level{ID: 5, Name: "debug"}
|
||||
// Trace designates the highest verbosity of log output.
|
||||
Trace = Level{ID: 6, Name: "trace"}
|
||||
)
|
||||
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
218
vendor/github.com/mattermost/logr/logger.go
сгенерированный
поставляемый
@@ -1,218 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// Logger provides context for logging via fields.
|
||||
type Logger struct {
|
||||
logr *Logr
|
||||
fields Fields
|
||||
}
|
||||
|
||||
// Logr returns the `Logr` instance that created this `Logger`.
|
||||
func (logger Logger) Logr() *Logr {
|
||||
return logger.logr
|
||||
}
|
||||
|
||||
// WithField creates a new `Logger` with any existing fields
|
||||
// plus the new one.
|
||||
func (logger Logger) WithField(key string, value interface{}) Logger {
|
||||
return logger.WithFields(Fields{key: value})
|
||||
}
|
||||
|
||||
// WithFields creates a new `Logger` with any existing fields
|
||||
// plus the new ones.
|
||||
func (logger Logger) WithFields(fields Fields) Logger {
|
||||
l := Logger{logr: logger.logr}
|
||||
// if parent has no fields then avoid creating a new map.
|
||||
oldLen := len(logger.fields)
|
||||
if oldLen == 0 {
|
||||
l.fields = fields
|
||||
return l
|
||||
}
|
||||
|
||||
l.fields = make(Fields, len(fields)+oldLen)
|
||||
for k, v := range logger.fields {
|
||||
l.fields[k] = v
|
||||
}
|
||||
for k, v := range fields {
|
||||
l.fields[k] = v
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Log checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the Logr queue.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (logger Logger) Log(lvl Level, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, args...)`.
|
||||
func (logger Logger) Trace(args ...interface{}) {
|
||||
logger.Log(Trace, args...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, args...)`.
|
||||
func (logger Logger) Debug(args ...interface{}) {
|
||||
logger.Log(Debug, args...)
|
||||
}
|
||||
|
||||
// Print ensures compatibility with std lib logger.
|
||||
func (logger Logger) Print(args ...interface{}) {
|
||||
logger.Info(args...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, args...)`.
|
||||
func (logger Logger) Info(args ...interface{}) {
|
||||
logger.Log(Info, args...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, args...)`.
|
||||
func (logger Logger) Warn(args ...interface{}) {
|
||||
logger.Log(Warn, args...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, args...)`.
|
||||
func (logger Logger) Error(args ...interface{}) {
|
||||
logger.Log(Error, args...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatal(args ...interface{}) {
|
||||
logger.Log(Fatal, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panic(args ...interface{}) {
|
||||
logger.Log(Panic, args...)
|
||||
panic(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
//
|
||||
// Printf style
|
||||
//
|
||||
|
||||
// Logf checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Printf.
|
||||
func (logger Logger) Logf(lvl Level, format string, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, format, args, status.Stacktrace)
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`.
|
||||
func (logger Logger) Tracef(format string, args ...interface{}) {
|
||||
logger.Logf(Trace, format, args...)
|
||||
}
|
||||
|
||||
// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`.
|
||||
func (logger Logger) Debugf(format string, args ...interface{}) {
|
||||
logger.Logf(Debug, format, args...)
|
||||
}
|
||||
|
||||
// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`.
|
||||
func (logger Logger) Infof(format string, args ...interface{}) {
|
||||
logger.Logf(Info, format, args...)
|
||||
}
|
||||
|
||||
// Printf ensures compatibility with std lib logger.
|
||||
func (logger Logger) Printf(format string, args ...interface{}) {
|
||||
logger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`.
|
||||
func (logger Logger) Warnf(format string, args ...interface{}) {
|
||||
logger.Logf(Warn, format, args...)
|
||||
}
|
||||
|
||||
// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`.
|
||||
func (logger Logger) Errorf(format string, args ...interface{}) {
|
||||
logger.Logf(Error, format, args...)
|
||||
}
|
||||
|
||||
// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatalf(format string, args ...interface{}) {
|
||||
logger.Logf(Fatal, format, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panicf(format string, args ...interface{}) {
|
||||
logger.Logf(Panic, format, args...)
|
||||
}
|
||||
|
||||
//
|
||||
// Println style
|
||||
//
|
||||
|
||||
// Logln checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Println.
|
||||
func (logger Logger) Logln(lvl Level, args ...interface{}) {
|
||||
status := logger.logr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, "", args, status.Stacktrace)
|
||||
rec.newline = true
|
||||
logger.logr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// Traceln is a convenience method equivalent to `Logln(TraceLevel, args...)`.
|
||||
func (logger Logger) Traceln(args ...interface{}) {
|
||||
logger.Logln(Trace, args...)
|
||||
}
|
||||
|
||||
// Debugln is a convenience method equivalent to `Logln(DebugLevel, args...)`.
|
||||
func (logger Logger) Debugln(args ...interface{}) {
|
||||
logger.Logln(Debug, args...)
|
||||
}
|
||||
|
||||
// Infoln is a convenience method equivalent to `Logln(InfoLevel, args...)`.
|
||||
func (logger Logger) Infoln(args ...interface{}) {
|
||||
logger.Logln(Info, args...)
|
||||
}
|
||||
|
||||
// Println ensures compatibility with std lib logger.
|
||||
func (logger Logger) Println(args ...interface{}) {
|
||||
logger.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warnln is a convenience method equivalent to `Logln(WarnLevel, args...)`.
|
||||
func (logger Logger) Warnln(args ...interface{}) {
|
||||
logger.Logln(Warn, args...)
|
||||
}
|
||||
|
||||
// Errorln is a convenience method equivalent to `Logln(ErrorLevel, args...)`.
|
||||
func (logger Logger) Errorln(args ...interface{}) {
|
||||
logger.Logln(Error, args...)
|
||||
}
|
||||
|
||||
// Fatalln is a convenience method equivalent to `Logln(FatalLevel, args...)`
|
||||
// followed by a call to os.Exit(1).
|
||||
func (logger Logger) Fatalln(args ...interface{}) {
|
||||
logger.Logln(Fatal, args...)
|
||||
logger.logr.exit(1)
|
||||
}
|
||||
|
||||
// Panicln is a convenience method equivalent to `Logln(PanicLevel, args...)`
|
||||
// followed by a call to panic().
|
||||
func (logger Logger) Panicln(args ...interface{}) {
|
||||
logger.Logln(Panic, args...)
|
||||
}
|
||||
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
664
vendor/github.com/mattermost/logr/logr.go
сгенерированный
поставляемый
@@ -1,664 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg"
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Logr maintains a list of log targets and accepts incoming
|
||||
// log records.
|
||||
type Logr struct {
|
||||
tmux sync.RWMutex // target mutex
|
||||
targets []Target
|
||||
|
||||
mux sync.RWMutex
|
||||
maxQueueSizeActual int
|
||||
in chan *LogRec
|
||||
done chan struct{}
|
||||
once sync.Once
|
||||
shutdown bool
|
||||
lvlCache levelCache
|
||||
|
||||
metricsInitOnce sync.Once
|
||||
metricsCloseOnce sync.Once
|
||||
metricsDone chan struct{}
|
||||
metrics MetricsCollector
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
|
||||
bufferPool sync.Pool
|
||||
|
||||
// MaxQueueSize is the maximum number of log records that can be queued.
|
||||
// If exceeded, `OnQueueFull` is called which determines if the log
|
||||
// record will be dropped or block until add is successful.
|
||||
// If this is modified, it must be done before `Configure` or
|
||||
// `AddTarget`. Defaults to DefaultMaxQueueSize.
|
||||
MaxQueueSize int
|
||||
|
||||
// OnLoggerError, when not nil, is called any time an internal
|
||||
// logging error occurs. For example, this can happen when a
|
||||
// target cannot connect to its data sink.
|
||||
OnLoggerError func(error)
|
||||
|
||||
// OnQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full Logr queue.
|
||||
// `MaxQueueSize` can be used to modify the maximum queue size.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
OnQueueFull func(rec *LogRec, maxQueueSize int) bool
|
||||
|
||||
// OnTargetQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full target queue provided the target supports reporting
|
||||
// this condition.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
OnTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool
|
||||
|
||||
// OnExit, when not nil, is called when a FatalXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `os.Exit(code)`.
|
||||
OnExit func(code int)
|
||||
|
||||
// OnPanic, when not nil, is called when a PanicXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `panic(err)`.
|
||||
OnPanic func(err interface{})
|
||||
|
||||
// EnqueueTimeout is the amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull`
|
||||
// is called and returns false.
|
||||
EnqueueTimeout time.Duration
|
||||
|
||||
// ShutdownTimeout is the amount of time `logr.Shutdown` can execute before
|
||||
// timing out.
|
||||
ShutdownTimeout time.Duration
|
||||
|
||||
// FlushTimeout is the amount of time `logr.Flush` can execute before
|
||||
// timing out.
|
||||
FlushTimeout time.Duration
|
||||
|
||||
// UseSyncMapLevelCache can be set to true before the first target is added
|
||||
// when high concurrency (e.g. >32 cores) is expected. This may improve
|
||||
// performance with large numbers of cores - benchmark for your use case.
|
||||
UseSyncMapLevelCache bool
|
||||
|
||||
// MaxPooledFormatBuffer determines the maximum size of a buffer that can be
|
||||
// pooled. To reduce allocations, the buffers needed during formatting (etc)
|
||||
// are pooled. A very large log item will grow a buffer that could stay in
|
||||
// memory indefinitely. This settings lets you control how big a pooled buffer
|
||||
// can be - anything larger will be garbage collected after use.
|
||||
// Defaults to 1MB.
|
||||
MaxPooledBuffer int
|
||||
|
||||
// DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer.
|
||||
DisableBufferPool bool
|
||||
|
||||
// MetricsUpdateFreqMillis determines how often polled metrics are updated
|
||||
// when metrics are enabled.
|
||||
MetricsUpdateFreqMillis int64
|
||||
}
|
||||
|
||||
// Configure adds/removes targets via the supplied `Config`.
|
||||
func (logr *Logr) Configure(config *cfg.Config) error {
|
||||
// TODO
|
||||
return fmt.Errorf("not implemented yet")
|
||||
}
|
||||
|
||||
func (logr *Logr) ensureInit() {
|
||||
logr.once.Do(func() {
|
||||
defer func() {
|
||||
go logr.start()
|
||||
}()
|
||||
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
|
||||
logr.maxQueueSizeActual = logr.MaxQueueSize
|
||||
if logr.maxQueueSizeActual == 0 {
|
||||
logr.maxQueueSizeActual = DefaultMaxQueueSize
|
||||
}
|
||||
|
||||
if logr.maxQueueSizeActual < 0 {
|
||||
logr.maxQueueSizeActual = 0
|
||||
}
|
||||
|
||||
logr.in = make(chan *LogRec, logr.maxQueueSizeActual)
|
||||
logr.done = make(chan struct{})
|
||||
|
||||
if logr.UseSyncMapLevelCache {
|
||||
logr.lvlCache = &syncMapLevelCache{}
|
||||
} else {
|
||||
logr.lvlCache = &arrayLevelCache{}
|
||||
}
|
||||
|
||||
if logr.MaxPooledBuffer == 0 {
|
||||
logr.MaxPooledBuffer = DefaultMaxPooledBuffer
|
||||
}
|
||||
logr.bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
logr.lvlCache.setup()
|
||||
})
|
||||
}
|
||||
|
||||
// AddTarget adds one or more targets to the logger which will receive
|
||||
// log records for outputting.
|
||||
func (logr *Logr) AddTarget(targets ...Target) error {
|
||||
if logr.IsShutdown() {
|
||||
return fmt.Errorf("AddTarget called after Logr shut down")
|
||||
}
|
||||
|
||||
logr.ensureInit()
|
||||
metrics := logr.getMetricsCollector()
|
||||
defer logr.ResetLevelCache() // call this after tmux is released
|
||||
|
||||
logr.tmux.Lock()
|
||||
defer logr.tmux.Unlock()
|
||||
|
||||
errs := merror.New()
|
||||
for _, t := range targets {
|
||||
if t == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
logr.targets = append(logr.targets, t)
|
||||
if metrics != nil {
|
||||
if tm, ok := t.(TargetWithMetrics); ok {
|
||||
if err := tm.EnableMetrics(metrics, logr.MetricsUpdateFreqMillis); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// NewLogger creates a Logger using defaults. A `Logger` is light-weight
|
||||
// enough to create on-demand, but typically one or more Loggers are
|
||||
// created and re-used.
|
||||
func (logr *Logr) NewLogger() Logger {
|
||||
logger := Logger{logr: logr}
|
||||
return logger
|
||||
}
|
||||
|
||||
var levelStatusDisabled = LevelStatus{}
|
||||
|
||||
// IsLevelEnabled returns true if at least one target has the specified
|
||||
// level enabled. The result is cached so that subsequent checks are fast.
|
||||
func (logr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
|
||||
status, ok := logr.isLevelEnabledFromCache(lvl)
|
||||
if ok {
|
||||
return status
|
||||
}
|
||||
|
||||
// Check each target.
|
||||
logr.tmux.RLock()
|
||||
for _, t := range logr.targets {
|
||||
e, s := t.IsLevelEnabled(lvl)
|
||||
if e {
|
||||
status.Enabled = true
|
||||
if s {
|
||||
status.Stacktrace = true
|
||||
break // if both enabled then no sense checking more targets
|
||||
}
|
||||
}
|
||||
}
|
||||
logr.tmux.RUnlock()
|
||||
|
||||
// Cache and return the result.
|
||||
if err := logr.updateLevelCache(lvl.ID, status); err != nil {
|
||||
logr.ReportError(err)
|
||||
return LevelStatus{}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (logr *Logr) isLevelEnabledFromCache(lvl Level) (LevelStatus, bool) {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
|
||||
// Don't accept new log records after shutdown.
|
||||
if logr.shutdown {
|
||||
return levelStatusDisabled, true
|
||||
}
|
||||
|
||||
// Check cache. lvlCache may still be nil if no targets added.
|
||||
if logr.lvlCache == nil {
|
||||
return levelStatusDisabled, true
|
||||
}
|
||||
status, ok := logr.lvlCache.get(lvl.ID)
|
||||
if ok {
|
||||
return status, true
|
||||
}
|
||||
return LevelStatus{}, false
|
||||
}
|
||||
|
||||
func (logr *Logr) updateLevelCache(id LevelID, status LevelStatus) error {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.lvlCache != nil {
|
||||
return logr.lvlCache.put(id, status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasTargets returns true only if at least one target exists within the Logr.
|
||||
func (logr *Logr) HasTargets() bool {
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
return len(logr.targets) > 0
|
||||
}
|
||||
|
||||
// TargetInfo provides name and type for a Target.
|
||||
type TargetInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// TargetInfos enumerates all the targets added to this Logr.
|
||||
// The resulting slice represents a snapshot at time of calling.
|
||||
func (logr *Logr) TargetInfos() []TargetInfo {
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
|
||||
infos := make([]TargetInfo, 0)
|
||||
|
||||
for _, t := range logr.targets {
|
||||
inf := TargetInfo{
|
||||
Name: fmt.Sprintf("%v", t),
|
||||
Type: fmt.Sprintf("%T", t),
|
||||
}
|
||||
infos = append(infos, inf)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// RemoveTargets safely removes one or more targets based on the filtering method.
|
||||
// f should return true to delete the target, false to keep it.
|
||||
// When removing a target, best effort is made to write any queued log records before
|
||||
// closing, with cxt determining how much time can be spent in total.
|
||||
// Note, keep the timeout short since this method blocks certain logging operations.
|
||||
func (logr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error {
|
||||
var removed bool
|
||||
defer func() {
|
||||
if removed {
|
||||
// call this after tmux is released since
|
||||
// it will lock mux and we don't want to
|
||||
// introduce possible deadlock.
|
||||
logr.ResetLevelCache()
|
||||
}
|
||||
}()
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
logr.tmux.Lock()
|
||||
defer logr.tmux.Unlock()
|
||||
|
||||
cp := make([]Target, 0)
|
||||
|
||||
for _, t := range logr.targets {
|
||||
inf := TargetInfo{
|
||||
Name: fmt.Sprintf("%v", t),
|
||||
Type: fmt.Sprintf("%T", t),
|
||||
}
|
||||
if f(inf) {
|
||||
if err := t.Shutdown(cxt); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
removed = true
|
||||
} else {
|
||||
cp = append(cp, t)
|
||||
}
|
||||
}
|
||||
logr.targets = cp
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is
|
||||
// called any time a Target is added or a target's level is changed.
|
||||
func (logr *Logr) ResetLevelCache() {
|
||||
// Write lock so that new cache entries cannot be stored while we
|
||||
// clear the cache.
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
logr.resetLevelCache()
|
||||
}
|
||||
|
||||
// resetLevelCache empties the level cache without locking.
|
||||
// mux.Lock must be held before calling this function.
|
||||
func (logr *Logr) resetLevelCache() {
|
||||
// lvlCache may still be nil if no targets added.
|
||||
if logr.lvlCache != nil {
|
||||
logr.lvlCache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
// enqueue adds a log record to the logr queue. If the queue is full then
|
||||
// this function either blocks or the log record is dropped, depending on
|
||||
// the result of calling `OnQueueFull`.
|
||||
func (logr *Logr) enqueue(rec *LogRec) {
|
||||
if logr.in == nil {
|
||||
logr.ReportError(fmt.Errorf("AddTarget or Configure must be called before enqueue"))
|
||||
}
|
||||
|
||||
select {
|
||||
case logr.in <- rec:
|
||||
default:
|
||||
if logr.OnQueueFull != nil && logr.OnQueueFull(rec, logr.maxQueueSizeActual) {
|
||||
return // drop the record
|
||||
}
|
||||
select {
|
||||
case <-time.After(logr.enqueueTimeout()):
|
||||
logr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec))
|
||||
case logr.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exit is called by one of the FatalXXX style APIS. If `logr.OnExit` is not nil
|
||||
// then that method is called, otherwise the default behavior is to shut down this
|
||||
// Logr cleanly then call `os.Exit(code)`.
|
||||
func (logr *Logr) exit(code int) {
|
||||
if logr.OnExit != nil {
|
||||
logr.OnExit(code)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logr.Shutdown(); err != nil {
|
||||
logr.ReportError(err)
|
||||
}
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// panic is called by one of the PanicXXX style APIS. If `logr.OnPanic` is not nil
|
||||
// then that method is called, otherwise the default behavior is to shut down this
|
||||
// Logr cleanly then call `panic(err)`.
|
||||
func (logr *Logr) panic(err interface{}) {
|
||||
if logr.OnPanic != nil {
|
||||
logr.OnPanic(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := logr.Shutdown(); err != nil {
|
||||
logr.ReportError(err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// `logr.FlushTimeout` determines how long flush can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) Flush() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), logr.flushTimeout())
|
||||
defer cancel()
|
||||
return logr.FlushWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) FlushWithTimeout(ctx context.Context) error {
|
||||
if !logr.HasTargets() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if logr.IsShutdown() {
|
||||
return errors.New("Flush called on shut down Logr")
|
||||
}
|
||||
|
||||
rec := newFlushLogRec(logr.NewLogger())
|
||||
logr.enqueue(rec)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newTimeoutError("logr queue shutdown timeout")
|
||||
case <-rec.flush:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsShutdown returns true if this Logr instance has been shut down.
|
||||
// No further log records can be enqueued and no targets added after
|
||||
// shutdown.
|
||||
func (logr *Logr) IsShutdown() bool {
|
||||
logr.mux.Lock()
|
||||
defer logr.mux.Unlock()
|
||||
return logr.shutdown
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// `logr.ShutdownTimeout` determines how long shutdown can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (logr *Logr) Shutdown() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), logr.shutdownTimeout())
|
||||
defer cancel()
|
||||
return logr.ShutdownWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// Use `IsTimeoutError` to determine if the returned error is due to a
|
||||
// timeout.
|
||||
func (logr *Logr) ShutdownWithTimeout(ctx context.Context) error {
|
||||
logr.mux.Lock()
|
||||
if logr.shutdown {
|
||||
logr.mux.Unlock()
|
||||
return errors.New("Shutdown called again after shut down")
|
||||
}
|
||||
logr.shutdown = true
|
||||
logr.resetLevelCache()
|
||||
logr.mux.Unlock()
|
||||
|
||||
logr.metricsCloseOnce.Do(func() {
|
||||
if logr.metricsDone != nil {
|
||||
close(logr.metricsDone)
|
||||
}
|
||||
})
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
// close the incoming channel and wait for read loop to exit.
|
||||
if logr.in != nil {
|
||||
close(logr.in)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errs.Append(newTimeoutError("logr queue shutdown timeout"))
|
||||
case <-logr.done:
|
||||
}
|
||||
}
|
||||
|
||||
// logr.in channel should now be drained to targets and no more log records
|
||||
// can be added.
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, t := range logr.targets {
|
||||
err := t.Shutdown(ctx)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ReportError is used to notify the host application of any internal logging errors.
|
||||
// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is
|
||||
// output to `os.Stderr`.
|
||||
func (logr *Logr) ReportError(err interface{}) {
|
||||
logr.incErrorCounter()
|
||||
|
||||
if logr.OnLoggerError == nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
logr.OnLoggerError(fmt.Errorf("%v", err))
|
||||
}
|
||||
|
||||
// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection.
|
||||
func (logr *Logr) BorrowBuffer() *bytes.Buffer {
|
||||
if logr.DisableBufferPool {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
return logr.bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only
|
||||
// retained if less than MaxPooledBuffer.
|
||||
func (logr *Logr) ReleaseBuffer(buf *bytes.Buffer) {
|
||||
if !logr.DisableBufferPool && buf.Cap() < logr.MaxPooledBuffer {
|
||||
buf.Reset()
|
||||
logr.bufferPool.Put(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueTimeout returns amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
|
||||
// and returns false.
|
||||
func (logr *Logr) enqueueTimeout() time.Duration {
|
||||
if logr.EnqueueTimeout == 0 {
|
||||
return DefaultEnqueueTimeout
|
||||
}
|
||||
return logr.EnqueueTimeout
|
||||
}
|
||||
|
||||
// shutdownTimeout returns the timeout duration for `logr.Shutdown`.
|
||||
func (logr *Logr) shutdownTimeout() time.Duration {
|
||||
if logr.ShutdownTimeout == 0 {
|
||||
return DefaultShutdownTimeout
|
||||
}
|
||||
return logr.ShutdownTimeout
|
||||
}
|
||||
|
||||
// flushTimeout returns the timeout duration for `logr.Flush`.
|
||||
func (logr *Logr) flushTimeout() time.Duration {
|
||||
if logr.FlushTimeout == 0 {
|
||||
return DefaultFlushTimeout
|
||||
}
|
||||
return logr.FlushTimeout
|
||||
}
|
||||
|
||||
// start selects on incoming log records until done channel signals.
|
||||
// Incoming log records are fanned out to all log targets.
|
||||
func (logr *Logr) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logr.ReportError(r)
|
||||
go logr.start()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range logr.in {
|
||||
if rec.flush != nil {
|
||||
logr.flush(rec.flush)
|
||||
} else {
|
||||
rec.prep()
|
||||
logr.fanout(rec)
|
||||
}
|
||||
}
|
||||
close(logr.done)
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until
|
||||
// logr is closed.
|
||||
func (logr *Logr) startMetricsUpdater() {
|
||||
for {
|
||||
updateFreq := logr.getMetricsUpdateFreqMillis()
|
||||
if updateFreq == 0 {
|
||||
updateFreq = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreq < 250 {
|
||||
updateFreq = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
select {
|
||||
case <-logr.metricsDone:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreq) * time.Millisecond):
|
||||
logr.setQueueSizeGauge(float64(len(logr.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (logr *Logr) getMetricsUpdateFreqMillis() int64 {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
return logr.MetricsUpdateFreqMillis
|
||||
}
|
||||
|
||||
// fanout pushes a LogRec to all targets.
|
||||
func (logr *Logr) fanout(rec *LogRec) {
|
||||
var target Target
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logr.ReportError(fmt.Errorf("fanout failed for target %s, %v", target, r))
|
||||
}
|
||||
}()
|
||||
|
||||
var logged bool
|
||||
defer func() {
|
||||
if logged {
|
||||
logr.incLoggedCounter() // call this after tmux is released
|
||||
}
|
||||
}()
|
||||
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target = range logr.targets {
|
||||
if enabled, _ := target.IsLevelEnabled(rec.Level()); enabled {
|
||||
target.Log(rec)
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (logr *Logr) flush(done chan<- struct{}) {
|
||||
// first drain the logr queue.
|
||||
loop:
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-logr.in:
|
||||
if rec.flush == nil {
|
||||
rec.prep()
|
||||
logr.fanout(rec)
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
logger := logr.NewLogger()
|
||||
|
||||
// drain all the targets; block until finished.
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target := range logr.targets {
|
||||
rec := newFlushLogRec(logger)
|
||||
target.Log(rec)
|
||||
<-rec.flush
|
||||
}
|
||||
done <- struct{}{}
|
||||
}
|
||||
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
299
vendor/github.com/mattermost/logr/target.go
сгенерированный
поставляемый
@@ -1,299 +0,0 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Target represents a destination for log records such as file,
|
||||
// database, TCP socket, etc.
|
||||
type Target interface {
|
||||
// SetName provides an optional name for the target.
|
||||
SetName(name string)
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit
|
||||
// logs for the specified level. Also determines if
|
||||
// a stack trace is required.
|
||||
IsLevelEnabled(Level) (enabled bool, stacktrace bool)
|
||||
|
||||
// Formatter returns the Formatter associated with this Target.
|
||||
Formatter() Formatter
|
||||
|
||||
// Log outputs the log record to this target's destination.
|
||||
Log(rec *LogRec)
|
||||
|
||||
// Shutdown makes best effort to flush target queue and
|
||||
// frees/closes all resources.
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
|
||||
// RecordWriter can convert a LogRecord to bytes and output to some data sink.
|
||||
type RecordWriter interface {
|
||||
Write(rec *LogRec) error
|
||||
}
|
||||
|
||||
// Basic provides the basic functionality of a Target that can be used
|
||||
// to more easily compose your own Targets. To use, just embed Basic
|
||||
// in your target type, implement `RecordWriter`, and call `(*Basic).Start`.
|
||||
type Basic struct {
|
||||
target Target
|
||||
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
|
||||
in chan *LogRec
|
||||
done chan struct{}
|
||||
w RecordWriter
|
||||
|
||||
mux sync.RWMutex
|
||||
name string
|
||||
|
||||
metrics bool
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
droppedCounter Counter
|
||||
blockedCounter Counter
|
||||
|
||||
metricsUpdateFreqMillis int64
|
||||
}
|
||||
|
||||
// Start initializes this target helper and starts accepting log records for processing.
|
||||
func (b *Basic) Start(target Target, rw RecordWriter, filter Filter, formatter Formatter, maxQueued int) {
|
||||
if filter == nil {
|
||||
filter = &StdFilter{Lvl: Fatal}
|
||||
}
|
||||
if formatter == nil {
|
||||
formatter = &DefaultFormatter{}
|
||||
}
|
||||
|
||||
b.target = target
|
||||
b.filter = filter
|
||||
b.formatter = formatter
|
||||
b.in = make(chan *LogRec, maxQueued)
|
||||
b.done = make(chan struct{}, 1)
|
||||
b.w = rw
|
||||
go b.start()
|
||||
|
||||
if b.hasMetrics() {
|
||||
go b.startMetricsUpdater()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) SetName(name string) {
|
||||
b.mux.Lock()
|
||||
defer b.mux.Unlock()
|
||||
b.name = name
|
||||
}
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit
|
||||
// logs for the specified level. Also determines if
|
||||
// a stack trace is required.
|
||||
func (b *Basic) IsLevelEnabled(lvl Level) (enabled bool, stacktrace bool) {
|
||||
return b.filter.IsEnabled(lvl), b.filter.IsStacktraceEnabled(lvl)
|
||||
}
|
||||
|
||||
// Formatter returns the Formatter associated with this Target.
|
||||
func (b *Basic) Formatter() Formatter {
|
||||
return b.formatter
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (b *Basic) Shutdown(ctx context.Context) error {
|
||||
// close the incoming channel and wait for read loop to exit.
|
||||
close(b.in)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-b.done:
|
||||
}
|
||||
|
||||
// b.in channel should now be drained.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Log outputs the log record to this targets destination.
|
||||
func (b *Basic) Log(rec *LogRec) {
|
||||
lgr := rec.Logger().Logr()
|
||||
select {
|
||||
case b.in <- rec:
|
||||
default:
|
||||
handler := lgr.OnTargetQueueFull
|
||||
if handler != nil && handler(b.target, rec, cap(b.in)) {
|
||||
b.incDroppedCounter()
|
||||
return // drop the record
|
||||
}
|
||||
b.incBlockedCounter()
|
||||
|
||||
select {
|
||||
case <-time.After(lgr.enqueueTimeout()):
|
||||
lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec))
|
||||
case b.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics enables metrics collection using the provided MetricsCollector.
|
||||
func (b *Basic) EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error {
|
||||
name := fmt.Sprintf("%v", b)
|
||||
|
||||
b.mux.Lock()
|
||||
defer b.mux.Unlock()
|
||||
|
||||
b.metrics = true
|
||||
b.metricsUpdateFreqMillis = updateFreqMillis
|
||||
|
||||
var err error
|
||||
|
||||
if b.queueSizeGauge, err = collector.QueueSizeGauge(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.loggedCounter, err = collector.LoggedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.errorCounter, err = collector.ErrorCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.droppedCounter, err = collector.DroppedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
if b.blockedCounter, err = collector.BlockedCounter(name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Basic) hasMetrics() bool {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
return b.metrics
|
||||
}
|
||||
|
||||
func (b *Basic) setQueueSizeGauge(val float64) {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.queueSizeGauge != nil {
|
||||
b.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incLoggedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.loggedCounter != nil {
|
||||
b.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incErrorCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.errorCounter != nil {
|
||||
b.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incDroppedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.droppedCounter != nil {
|
||||
b.droppedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) incBlockedCounter() {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
if b.blockedCounter != nil {
|
||||
b.blockedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a name for this target. Use `SetName` to specify a name.
|
||||
func (b *Basic) String() string {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
|
||||
if b.name != "" {
|
||||
return b.name
|
||||
}
|
||||
return fmt.Sprintf("%T", b.target)
|
||||
}
|
||||
|
||||
// Start accepts log records via In channel and writes to the
|
||||
// supplied writer, until Done channel signaled.
|
||||
func (b *Basic) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "Basic.start -- ", r)
|
||||
go b.start()
|
||||
}
|
||||
}()
|
||||
|
||||
for rec := range b.in {
|
||||
if rec.flush != nil {
|
||||
b.flush(rec.flush)
|
||||
} else {
|
||||
err := b.w.Write(rec)
|
||||
if err != nil {
|
||||
b.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
} else {
|
||||
b.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
}
|
||||
close(b.done)
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `MetricsUpdateFreqSecs` seconds until
|
||||
// target is closed.
|
||||
func (b *Basic) startMetricsUpdater() {
|
||||
for {
|
||||
updateFreq := b.getMetricsUpdateFreqMillis()
|
||||
if updateFreq == 0 {
|
||||
updateFreq = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreq < 250 {
|
||||
updateFreq = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
select {
|
||||
case <-b.done:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreq) * time.Millisecond):
|
||||
b.setQueueSizeGauge(float64(len(b.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Basic) getMetricsUpdateFreqMillis() int64 {
|
||||
b.mux.RLock()
|
||||
defer b.mux.RUnlock()
|
||||
return b.metricsUpdateFreqMillis
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (b *Basic) flush(done chan<- struct{}) {
|
||||
for {
|
||||
var rec *LogRec
|
||||
var err error
|
||||
select {
|
||||
case rec = <-b.in:
|
||||
// ignore any redundant flush records.
|
||||
if rec.flush == nil {
|
||||
err = b.w.Write(rec)
|
||||
if err != nil {
|
||||
b.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
89
vendor/github.com/mattermost/logr/target/syslog.go
сгенерированный
поставляемый
@@ -1,89 +0,0 @@
|
||||
// +build !windows,!nacl,!plan9
|
||||
|
||||
package target
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/syslog"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
logr.Basic
|
||||
w *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogParams provides parameters for dialing a syslog daemon.
|
||||
type SyslogParams struct {
|
||||
Network string
|
||||
Raddr string
|
||||
Priority syslog.Priority
|
||||
Tag string
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog.
|
||||
func NewSyslogTarget(filter logr.Filter, formatter logr.Formatter, params *SyslogParams, maxQueue int) (*Syslog, error) {
|
||||
writer, err := syslog.Dial(params.Network, params.Raddr, params.Priority, params.Tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Syslog{w: writer}
|
||||
s.Basic.Start(s, s, filter, formatter, maxQueue)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (s *Syslog) Shutdown(ctx context.Context) error {
|
||||
errs := merror.New()
|
||||
|
||||
err := s.Basic.Shutdown(ctx)
|
||||
errs.Append(err)
|
||||
|
||||
err = s.w.Close()
|
||||
errs.Append(err)
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to syslog.
|
||||
func (s *Syslog) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := s.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := s.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txt := buf.String()
|
||||
|
||||
switch rec.Level() {
|
||||
case logr.Panic, logr.Fatal:
|
||||
err = s.w.Crit(txt)
|
||||
case logr.Error:
|
||||
err = s.w.Err(txt)
|
||||
case logr.Warn:
|
||||
err = s.w.Warning(txt)
|
||||
case logr.Debug, logr.Trace:
|
||||
err = s.w.Debug(txt)
|
||||
default:
|
||||
// logr.Info plus all custom levels.
|
||||
err = s.w.Info(txt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
reporter := rec.Logger().Logr().ReportError
|
||||
reporter(fmt.Errorf("syslog write fail: %w", err))
|
||||
// syslog writer will try to reconnect.
|
||||
}
|
||||
return err
|
||||
}
|
||||
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
40
vendor/github.com/mattermost/logr/target/writer.go
сгенерированный
поставляемый
@@ -1,40 +0,0 @@
|
||||
package target
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
)
|
||||
|
||||
// Writer outputs log records to any `io.Writer`.
|
||||
type Writer struct {
|
||||
logr.Basic
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewWriterTarget creates a target capable of outputting log records to an io.Writer.
|
||||
func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer {
|
||||
if out == nil {
|
||||
out = ioutil.Discard
|
||||
}
|
||||
w := &Writer{out: out}
|
||||
w.Basic.Start(w, w, filter, formatter, maxQueue)
|
||||
return w
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to the io.Writer.
|
||||
func (w *Writer) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := w.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := w.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.out.Write(buf.Bytes())
|
||||
return err
|
||||
}
|
||||
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
1
vendor/github.com/mattermost/logr/.gitignore → vendor/github.com/mattermost/logr/v2/.gitignore
сгенерированный
поставляемый
@@ -34,3 +34,4 @@ logs
|
||||
# test apps
|
||||
test/cmd/testapp1/testapp1
|
||||
test/cmd/simple/simple
|
||||
test/cmd/gelf/gelf
|
||||
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/.travis.yml → vendor/github.com/mattermost/logr/v2/.travis.yml
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/LICENSE → vendor/github.com/mattermost/logr/v2/LICENSE
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → vendor/github.com/mattermost/logr/v2/README.md
сгенерированный
поставляемый
49
vendor/github.com/mattermost/logr/README.md → vendor/github.com/mattermost/logr/v2/README.md
сгенерированный
поставляемый
@@ -16,9 +16,9 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
| entity | description |
|
||||
| ------ | ----------- |
|
||||
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr := &Logr{}```|
|
||||
| Logr | Engine instance typically instantiated once; used to configure logging.<br>```lgr,_ := logr.New()```|
|
||||
| Logger | Provides contextual logging via fields; lightweight, can be created once and accessed globally or create on demand.<br>```logger := lgr.NewLogger()```<br>```logger2 := logger.WithField("user", "Sam")```|
|
||||
| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).|
|
||||
| Target | A destination for log items such as console, file, database or just about anything that can be written to. Each target has its own filter/level and formatter, and any number of targets can be added to a Logr. Targets for file, syslog and any io.Writer are built-in and it is easy to create your own. You can also use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) via a simple [adapter](https://github.com/wiggin77/logrus4logr).|
|
||||
| Filter | Determines which logging calls get written versus filtered out. Also determines which logging calls generate a stack trace.<br>```filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Fatal}```|
|
||||
| Formatter | Formats the output. Logr includes built-in formatters for JSON and plain text with delimiters. It is easy to create your own formatters or you can also use any [Logrus formatters](https://github.com/sirupsen/logrus#formatters) via a simple [adapter](https://github.com/wiggin77/logrus4logr).<br>```formatter := &format.Plain{Delim: " \| "}```|
|
||||
|
||||
@@ -26,15 +26,15 @@ It is very much inspired by [Logrus](https://github.com/sirupsen/logrus) but add
|
||||
|
||||
```go
|
||||
// Create Logr instance.
|
||||
lgr := &logr.Logr{}
|
||||
lgr,_ := logr.New()
|
||||
|
||||
// Create a filter and formatter. Both can be shared by multiple
|
||||
// targets.
|
||||
filter := &logr.StdFilter{Lvl: logr.Warn, Stacktrace: logr.Error}
|
||||
formatter := &format.Plain{Delim: " | "}
|
||||
formatter := &formatters.Plain{Delim: " | "}
|
||||
|
||||
// WriterTarget outputs to any io.Writer
|
||||
t := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
t := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
lgr.AddTarget(t)
|
||||
|
||||
// One or more Loggers can be created, shared, used concurrently,
|
||||
@@ -56,7 +56,7 @@ Fields allow for contextual logging, meaning information can be added to log sta
|
||||
Fields are added via Loggers:
|
||||
|
||||
```go
|
||||
lgr := &Logr{}
|
||||
lgr,_ := logr.New()
|
||||
// ... add targets ...
|
||||
logger := lgr.NewLogger().WithFields(logr.Fields{
|
||||
"user": user,
|
||||
@@ -88,14 +88,14 @@ Logr also supports custom filters (logr.CustomFilter) which allow fine grained i
|
||||
LoginLevel := logr.Level{ID: 100, Name: "login ", Stacktrace: false}
|
||||
LogoutLevel := logr.Level{ID: 101, Name: "logout", Stacktrace: false}
|
||||
|
||||
lgr := &logr.Logr{}
|
||||
lgr,_ := logr.New()
|
||||
|
||||
// create a custom filter with custom levels.
|
||||
filter := &logr.CustomFilter{}
|
||||
filter.Add(LoginLevel, LogoutLevel)
|
||||
|
||||
formatter := &format.Plain{Delim: " | "}
|
||||
tgr := target.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
formatter := &formatters.Plain{Delim: " | "}
|
||||
tgr := targets.NewWriterTarget(filter, formatter, os.StdOut, 1000)
|
||||
lgr.AddTarget(tgr)
|
||||
logger := lgr.NewLogger().WithFields(logr.Fields{"user": "Bob", "role": "admin"})
|
||||
|
||||
@@ -113,36 +113,31 @@ You can use any [Logrus hooks](https://github.com/sirupsen/logrus/wiki/Hooks) vi
|
||||
|
||||
You can create your own target by implementing the [Target](./target.go) interface.
|
||||
|
||||
An easier method is to use the [logr.Basic](./target.go) type target and build your functionality on that. Basic handles all the queuing and other plumbing so you only need to implement two methods. Example target that outputs to `io.Writer`:
|
||||
Example target that outputs to `io.Writer`:
|
||||
|
||||
```go
|
||||
type Writer struct {
|
||||
logr.Basic
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func NewWriterTarget(filter logr.Filter, formatter logr.Formatter, out io.Writer, maxQueue int) *Writer {
|
||||
func NewWriterTarget(out io.Writer) *Writer {
|
||||
w := &Writer{out: out}
|
||||
w.Basic.Start(w, w, filter, formatter, maxQueue)
|
||||
return w
|
||||
}
|
||||
|
||||
// Called once to initialize target.
|
||||
func (w *Writer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write will always be called by a single goroutine, so no locking needed.
|
||||
// Just convert a log record to a []byte using the formatter and output the
|
||||
// bytes to your sink.
|
||||
func (w *Writer) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := w.IsLevelEnabled(rec.Level())
|
||||
func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return w.out.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// take a buffer from the pool to avoid allocations or just allocate a new one.
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := w.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.out.Write(buf.Bytes())
|
||||
return err
|
||||
// Called once to cleanup/free resources for target.
|
||||
func (w *Writer) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
209
vendor/github.com/mattermost/logr/v2/config/config.go
сгенерированный
поставляемый
Обычный файл
209
vendor/github.com/mattermost/logr/v2/config/config.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,209 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
"github.com/mattermost/logr/v2/formatters"
|
||||
"github.com/mattermost/logr/v2/targets"
|
||||
)
|
||||
|
||||
type TargetCfg struct {
|
||||
Type string `json:"type"` // one of "console", "file", "tcp", "syslog", "none".
|
||||
Options json.RawMessage `json:"options,omitempty"`
|
||||
Format string `json:"format"` // one of "json", "plain", "gelf"
|
||||
FormatOptions json.RawMessage `json:"format_options,omitempty"`
|
||||
Levels []logr.Level `json:"levels"`
|
||||
MaxQueueSize int `json:"maxqueuesize,omitempty"`
|
||||
}
|
||||
|
||||
type ConsoleOptions struct {
|
||||
Out string `json:"out"` // one of "stdout", "stderr"
|
||||
}
|
||||
|
||||
type TargetFactory func(targetType string, options json.RawMessage) (logr.Target, error)
|
||||
type FormatterFactory func(format string, options json.RawMessage) (logr.Formatter, error)
|
||||
|
||||
type Factories struct {
|
||||
targetFactory TargetFactory // can be nil
|
||||
formatterFactory FormatterFactory // can be nil
|
||||
}
|
||||
|
||||
var removeAll = func(ti logr.TargetInfo) bool { return true }
|
||||
|
||||
// ConfigureTargets replaces the current list of log targets with a new one based on a map
|
||||
// of name->TargetCfg. The map of TargetCfg's would typically be serialized from a JSON
|
||||
// source or can be programmatically created.
|
||||
//
|
||||
// An optional set of factories can be provided which will be called to create any target
|
||||
// types or formatters not built-in.
|
||||
//
|
||||
// To append log targets to an existing config, use `(*Logr).AddTarget` or
|
||||
// `(*Logr).AddTargetFromConfig` instead.
|
||||
func ConfigureTargets(lgr *logr.Logr, config map[string]TargetCfg, factories *Factories) error {
|
||||
if err := lgr.RemoveTargets(context.Background(), removeAll); err != nil {
|
||||
return fmt.Errorf("error removing existing log targets: %w", err)
|
||||
}
|
||||
|
||||
if factories == nil {
|
||||
factories = &Factories{nil, nil}
|
||||
}
|
||||
|
||||
for name, tcfg := range config {
|
||||
target, err := newTarget(tcfg.Type, tcfg.Options, factories.targetFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating log target %s: %w", name, err)
|
||||
}
|
||||
|
||||
if target == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
formatter, err := newFormatter(tcfg.Format, tcfg.FormatOptions, factories.formatterFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating formatter for log target %s: %w", name, err)
|
||||
}
|
||||
|
||||
filter := newFilter(tcfg.Levels)
|
||||
qSize := tcfg.MaxQueueSize
|
||||
if qSize == 0 {
|
||||
qSize = logr.DefaultMaxQueueSize
|
||||
}
|
||||
|
||||
if err = lgr.AddTarget(target, name, filter, formatter, qSize); err != nil {
|
||||
return fmt.Errorf("error adding log target %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFilter(levels []logr.Level) logr.Filter {
|
||||
filter := &logr.CustomFilter{}
|
||||
for _, lvl := range levels {
|
||||
filter.Add(lvl)
|
||||
}
|
||||
return filter
|
||||
}
|
||||
|
||||
func newTarget(targetType string, options json.RawMessage, factory TargetFactory) (logr.Target, error) {
|
||||
switch strings.ToLower(targetType) {
|
||||
case "console":
|
||||
c := ConsoleOptions{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &c); err != nil {
|
||||
return nil, fmt.Errorf("error decoding console target options: %w", err)
|
||||
}
|
||||
}
|
||||
var w io.Writer
|
||||
switch c.Out {
|
||||
case "stderr":
|
||||
w = os.Stderr
|
||||
case "stdout", "":
|
||||
w = os.Stdout
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid console target option '%s'", c.Out)
|
||||
}
|
||||
return targets.NewWriterTarget(w), nil
|
||||
case "file":
|
||||
fo := targets.FileOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing file target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &fo); err != nil {
|
||||
return nil, fmt.Errorf("error decoding file target options: %w", err)
|
||||
}
|
||||
if err := fo.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid file target options: %w", err)
|
||||
}
|
||||
return targets.NewFileTarget(fo), nil
|
||||
case "tcp":
|
||||
to := targets.TcpOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing TCP target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &to); err != nil {
|
||||
return nil, fmt.Errorf("error decoding TCP target options: %w", err)
|
||||
}
|
||||
if err := to.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid TCP target options: %w", err)
|
||||
}
|
||||
return targets.NewTcpTarget(&to), nil
|
||||
case "syslog":
|
||||
so := targets.SyslogOptions{}
|
||||
if len(options) == 0 {
|
||||
return nil, errors.New("missing SysLog target options")
|
||||
}
|
||||
if err := json.Unmarshal(options, &so); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Syslog target options: %w", err)
|
||||
}
|
||||
if err := so.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid SysLog target options: %w", err)
|
||||
}
|
||||
return targets.NewSyslogTarget(&so)
|
||||
case "none":
|
||||
return nil, nil
|
||||
default:
|
||||
if factory != nil {
|
||||
t, err := factory(targetType, options)
|
||||
if err != nil || t == nil {
|
||||
return nil, fmt.Errorf("error from target factory: %w", err)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("target type '%s' is unrecogized", targetType)
|
||||
}
|
||||
|
||||
func newFormatter(format string, options json.RawMessage, factory FormatterFactory) (logr.Formatter, error) {
|
||||
switch strings.ToLower(format) {
|
||||
case "json":
|
||||
j := formatters.JSON{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &j); err != nil {
|
||||
return nil, fmt.Errorf("error decoding JSON formatter options: %w", err)
|
||||
}
|
||||
if err := j.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid JSON formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &j, nil
|
||||
case "plain":
|
||||
p := formatters.Plain{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &p); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Plain formatter options: %w", err)
|
||||
}
|
||||
if err := p.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid plain formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &p, nil
|
||||
case "gelf":
|
||||
g := formatters.Gelf{}
|
||||
if len(options) != 0 {
|
||||
if err := json.Unmarshal(options, &g); err != nil {
|
||||
return nil, fmt.Errorf("error decoding Gelf formatter options: %w", err)
|
||||
}
|
||||
if err := g.CheckValid(); err != nil {
|
||||
return nil, fmt.Errorf("invalid GELF formatter options: %w", err)
|
||||
}
|
||||
}
|
||||
return &g, nil
|
||||
|
||||
default:
|
||||
if factory != nil {
|
||||
f, err := factory(format, options)
|
||||
if err != nil || f == nil {
|
||||
return nil, fmt.Errorf("error from formatter factory: %w", err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("format '%s' is unrecogized", format)
|
||||
}
|
||||
90
vendor/github.com/mattermost/logr/v2/config/sample-config.json
сгенерированный
поставляемый
Обычный файл
90
vendor/github.com/mattermost/logr/v2/config/sample-config.json
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"sample-console": {
|
||||
"type": "console",
|
||||
"options": {
|
||||
"out": "stdout"
|
||||
},
|
||||
"format": "plain",
|
||||
"format_options": {
|
||||
"delim": " | "
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-file": {
|
||||
"type": "file",
|
||||
"options": {
|
||||
"filename": "test.log",
|
||||
"max_size": 1000000,
|
||||
"max_age": 1,
|
||||
"max_backups": 10,
|
||||
"compress": true
|
||||
},
|
||||
"format": "json",
|
||||
"format_options": {
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-tcp": {
|
||||
"type": "tcp",
|
||||
"options": {
|
||||
"host": "localhost",
|
||||
"port": 18066,
|
||||
"tls": false,
|
||||
"cert": "",
|
||||
"insecure": false
|
||||
},
|
||||
"format": "gelf",
|
||||
"format_options": {
|
||||
"hostname": "server01"
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
},
|
||||
"sample-syslog": {
|
||||
"type": "syslog",
|
||||
"options": {
|
||||
"host": "localhost",
|
||||
"port": 18066,
|
||||
"tls": false,
|
||||
"cert": "",
|
||||
"insecure": false,
|
||||
"tag": "testapp"
|
||||
},
|
||||
"format": "plain",
|
||||
"format_options": {
|
||||
"delim": " "
|
||||
},
|
||||
"levels": [
|
||||
{"id": 5, "name": "debug"},
|
||||
{"id": 4, "name": "info"},
|
||||
{"id": 3, "name": "warn"},
|
||||
{"id": 2, "name": "error", "stacktrace": true},
|
||||
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||
{"id": 0, "name": "panic", "stacktrace": true}
|
||||
],
|
||||
"maxqueuesize": 1000
|
||||
}
|
||||
}
|
||||
2
vendor/github.com/mattermost/logr/const.go → vendor/github.com/mattermost/logr/v2/const.go
сгенерированный
поставляемый
2
vendor/github.com/mattermost/logr/const.go → vendor/github.com/mattermost/logr/v2/const.go
сгенерированный
поставляемый
@@ -13,7 +13,7 @@ const (
|
||||
|
||||
// MaxLevelID is the maximum value of a level ID. Some level cache implementations will
|
||||
// allocate a cache of this size. Cannot exceed uint.
|
||||
MaxLevelID = 256
|
||||
MaxLevelID = 65535
|
||||
|
||||
// DefaultEnqueueTimeout is the default amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull` is called
|
||||
403
vendor/github.com/mattermost/logr/v2/field.go
сгенерированный
поставляемый
Обычный файл
403
vendor/github.com/mattermost/logr/v2/field.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,403 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Comma = []byte{','}
|
||||
Equals = []byte{'='}
|
||||
Space = []byte{' '}
|
||||
Newline = []byte{'\n'}
|
||||
Quote = []byte{'"'}
|
||||
Colon = []byte{'"'}
|
||||
)
|
||||
|
||||
// LogCloner is implemented by `Any` types that require a clone to be provided
|
||||
// to the logger because the original may mutate.
|
||||
type LogCloner interface {
|
||||
LogClone() interface{}
|
||||
}
|
||||
|
||||
// LogWriter is implemented by `Any` types that provide custom formatting for
|
||||
// log output. A string representation of the type should be written directly to
|
||||
// the `io.Writer`.
|
||||
type LogWriter interface {
|
||||
LogWrite(w io.Writer) error
|
||||
}
|
||||
|
||||
type FieldType uint8
|
||||
|
||||
const (
|
||||
UnknownType FieldType = iota
|
||||
StringType
|
||||
StringerType
|
||||
StructType
|
||||
ErrorType
|
||||
BoolType
|
||||
TimestampMillisType
|
||||
TimeType
|
||||
DurationType
|
||||
Int64Type
|
||||
Int32Type
|
||||
IntType
|
||||
Uint64Type
|
||||
Uint32Type
|
||||
UintType
|
||||
Float64Type
|
||||
Float32Type
|
||||
BinaryType
|
||||
ArrayType
|
||||
MapType
|
||||
)
|
||||
|
||||
type Field struct {
|
||||
Key string
|
||||
Type FieldType
|
||||
Integer int64
|
||||
Float float64
|
||||
String string
|
||||
Interface interface{}
|
||||
}
|
||||
|
||||
func quoteString(w io.Writer, s string, shouldQuote func(s string) bool) error {
|
||||
b := shouldQuote(s)
|
||||
if b {
|
||||
if _, err := w.Write(Quote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(s)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b {
|
||||
if _, err := w.Write(Quote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValueString converts a known type to a string using default formatting.
|
||||
// This is called lazily by a formatter.
|
||||
// Formatters can provide custom formatting or types passed via `Any` can implement
|
||||
// the `LogString` interface to generate output for logging.
|
||||
// If the optional shouldQuote callback is provided, then it will be called for any
|
||||
// string output that could potentially need to be quoted.
|
||||
func (f Field) ValueString(w io.Writer, shouldQuote func(s string) bool) error {
|
||||
if shouldQuote == nil {
|
||||
shouldQuote = func(s string) bool { return false }
|
||||
}
|
||||
var err error
|
||||
switch f.Type {
|
||||
case StringType:
|
||||
err = quoteString(w, f.String, shouldQuote)
|
||||
|
||||
case StringerType:
|
||||
s, ok := f.Interface.(fmt.Stringer)
|
||||
if ok {
|
||||
err = quoteString(w, s.String(), shouldQuote)
|
||||
} else if f.Interface == nil {
|
||||
err = quoteString(w, "", shouldQuote)
|
||||
} else {
|
||||
err = fmt.Errorf("invalid fmt.Stringer for key %s", f.Key)
|
||||
}
|
||||
|
||||
case StructType:
|
||||
s, ok := f.Interface.(LogWriter)
|
||||
if ok {
|
||||
err = s.LogWrite(w)
|
||||
break
|
||||
}
|
||||
// structs that do not implement LogWriter fall back to reflection via Printf.
|
||||
// TODO: create custom reflection-based encoder.
|
||||
_, err = fmt.Fprintf(w, "%v", f.Interface)
|
||||
|
||||
case ErrorType:
|
||||
// TODO: create custom error encoder.
|
||||
err = quoteString(w, fmt.Sprintf("%v", f.Interface), shouldQuote)
|
||||
|
||||
case BoolType:
|
||||
var b bool
|
||||
if f.Integer != 0 {
|
||||
b = true
|
||||
}
|
||||
_, err = io.WriteString(w, strconv.FormatBool(b))
|
||||
|
||||
case TimestampMillisType:
|
||||
ts := time.Unix(f.Integer/1000, (f.Integer%1000)*int64(time.Millisecond))
|
||||
err = quoteString(w, ts.UTC().Format(TimestampMillisFormat), shouldQuote)
|
||||
|
||||
case TimeType:
|
||||
t, ok := f.Interface.(time.Time)
|
||||
if !ok {
|
||||
err = errors.New("invalid time")
|
||||
break
|
||||
}
|
||||
err = quoteString(w, t.Format(DefTimestampFormat), shouldQuote)
|
||||
|
||||
case DurationType:
|
||||
_, err = fmt.Fprintf(w, "%s", time.Duration(f.Integer))
|
||||
|
||||
case Int64Type, Int32Type, IntType:
|
||||
_, err = io.WriteString(w, strconv.FormatInt(f.Integer, 10))
|
||||
|
||||
case Uint64Type, Uint32Type, UintType:
|
||||
_, err = io.WriteString(w, strconv.FormatUint(uint64(f.Integer), 10))
|
||||
|
||||
case Float64Type, Float32Type:
|
||||
size := 64
|
||||
if f.Type == Float32Type {
|
||||
size = 32
|
||||
}
|
||||
err = quoteString(w, strconv.FormatFloat(f.Float, 'f', -1, size), shouldQuote)
|
||||
|
||||
case BinaryType:
|
||||
b, ok := f.Interface.([]byte)
|
||||
if ok {
|
||||
_, err = fmt.Fprintf(w, "[%X]", b)
|
||||
break
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "[%v]", f.Interface)
|
||||
|
||||
case ArrayType:
|
||||
a := reflect.ValueOf(f.Interface)
|
||||
arr:
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
item := a.Index(i)
|
||||
switch v := item.Interface().(type) {
|
||||
case LogWriter:
|
||||
if err = v.LogWrite(w); err != nil {
|
||||
break arr
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if err = quoteString(w, v.String(), shouldQuote); err != nil {
|
||||
break arr
|
||||
}
|
||||
default:
|
||||
s := fmt.Sprintf("%v", v)
|
||||
if err = quoteString(w, s, shouldQuote); err != nil {
|
||||
break arr
|
||||
}
|
||||
}
|
||||
if _, err = w.Write(Comma); err != nil {
|
||||
break arr
|
||||
}
|
||||
}
|
||||
|
||||
case MapType:
|
||||
a := reflect.ValueOf(f.Interface)
|
||||
iter := a.MapRange()
|
||||
it:
|
||||
for iter.Next() {
|
||||
if _, err = io.WriteString(w, iter.Key().String()); err != nil {
|
||||
break it
|
||||
}
|
||||
if _, err = w.Write(Equals); err != nil {
|
||||
break it
|
||||
}
|
||||
val := iter.Value().Interface()
|
||||
switch v := val.(type) {
|
||||
case LogWriter:
|
||||
if err = v.LogWrite(w); err != nil {
|
||||
break it
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if err = quoteString(w, v.String(), shouldQuote); err != nil {
|
||||
break it
|
||||
}
|
||||
default:
|
||||
s := fmt.Sprintf("%v", v)
|
||||
if err = quoteString(w, s, shouldQuote); err != nil {
|
||||
break it
|
||||
}
|
||||
}
|
||||
if _, err = w.Write(Comma); err != nil {
|
||||
break it
|
||||
}
|
||||
}
|
||||
|
||||
case UnknownType:
|
||||
_, err = fmt.Fprintf(w, "%v", f.Interface)
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("invalid type %d", f.Type)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func nilField(key string) Field {
|
||||
return String(key, "")
|
||||
}
|
||||
|
||||
func fieldForAny(key string, val interface{}) Field {
|
||||
switch v := val.(type) {
|
||||
case LogCloner:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
c := v.LogClone()
|
||||
return Field{Key: key, Type: StructType, Interface: c}
|
||||
case *LogCloner:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
c := (*v).LogClone()
|
||||
return Field{Key: key, Type: StructType, Interface: c}
|
||||
case LogWriter:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StructType, Interface: v}
|
||||
case *LogWriter:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StructType, Interface: *v}
|
||||
case bool:
|
||||
return Bool(key, v)
|
||||
case *bool:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Bool(key, *v)
|
||||
case float64:
|
||||
return Float64(key, v)
|
||||
case *float64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float64(key, *v)
|
||||
case float32:
|
||||
return Float32(key, v)
|
||||
case *float32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Float32(key, *v)
|
||||
case int:
|
||||
return Int(key, v)
|
||||
case *int:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int(key, *v)
|
||||
case int64:
|
||||
return Int64(key, v)
|
||||
case *int64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int64(key, *v)
|
||||
case int32:
|
||||
return Int32(key, v)
|
||||
case *int32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, *v)
|
||||
case int16:
|
||||
return Int32(key, int32(v))
|
||||
case *int16:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, int32(*v))
|
||||
case int8:
|
||||
return Int32(key, int32(v))
|
||||
case *int8:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Int32(key, int32(*v))
|
||||
case string:
|
||||
return String(key, v)
|
||||
case *string:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return String(key, *v)
|
||||
case uint:
|
||||
return Uint(key, v)
|
||||
case *uint:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint(key, *v)
|
||||
case uint64:
|
||||
return Uint64(key, v)
|
||||
case *uint64:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint64(key, *v)
|
||||
case uint32:
|
||||
return Uint32(key, v)
|
||||
case *uint32:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, *v)
|
||||
case uint16:
|
||||
return Uint32(key, uint32(v))
|
||||
case *uint16:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, uint32(*v))
|
||||
case uint8:
|
||||
return Uint32(key, uint32(v))
|
||||
case *uint8:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Uint32(key, uint32(*v))
|
||||
case []byte:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: BinaryType, Interface: v}
|
||||
case time.Time:
|
||||
return Time(key, v)
|
||||
case *time.Time:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Time(key, *v)
|
||||
case time.Duration:
|
||||
return Duration(key, v)
|
||||
case *time.Duration:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Duration(key, *v)
|
||||
case error:
|
||||
return NamedErr(key, v)
|
||||
case fmt.Stringer:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StringerType, Interface: v}
|
||||
case *fmt.Stringer:
|
||||
if v == nil {
|
||||
return nilField(key)
|
||||
}
|
||||
return Field{Key: key, Type: StringerType, Interface: *v}
|
||||
default:
|
||||
return Field{Key: key, Type: UnknownType, Interface: val}
|
||||
}
|
||||
}
|
||||
|
||||
// FieldSorter provides sorting of an array of fields by key.
|
||||
type FieldSorter []Field
|
||||
|
||||
func (fs FieldSorter) Len() int { return len(fs) }
|
||||
func (fs FieldSorter) Less(i, j int) bool { return fs[i].Key < fs[j].Key }
|
||||
func (fs FieldSorter) Swap(i, j int) { fs[i], fs[j] = fs[j], fs[i] }
|
||||
110
vendor/github.com/mattermost/logr/v2/fieldapi.go
сгенерированный
поставляемый
Обычный файл
110
vendor/github.com/mattermost/logr/v2/fieldapi.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,110 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Any picks the best supported field type based on type of val.
|
||||
// For best performance when passing a struct (or struct pointer),
|
||||
// implement `logr.LogWriter` on the struct, otherwise reflection
|
||||
// will be used to generate a string representation.
|
||||
func Any(key string, val interface{}) Field {
|
||||
return fieldForAny(key, val)
|
||||
}
|
||||
|
||||
// Int64 constructs a field containing a key and Int64 value.
|
||||
func Int64(key string, val int64) Field {
|
||||
return Field{Key: key, Type: Int64Type, Integer: val}
|
||||
}
|
||||
|
||||
// Int32 constructs a field containing a key and Int32 value.
|
||||
func Int32(key string, val int32) Field {
|
||||
return Field{Key: key, Type: Int32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Int constructs a field containing a key and Int value.
|
||||
func Int(key string, val int) Field {
|
||||
return Field{Key: key, Type: IntType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint64 constructs a field containing a key and Uint64 value.
|
||||
func Uint64(key string, val uint64) Field {
|
||||
return Field{Key: key, Type: Uint64Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint32 constructs a field containing a key and Uint32 value.
|
||||
func Uint32(key string, val uint32) Field {
|
||||
return Field{Key: key, Type: Uint32Type, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Uint constructs a field containing a key and Uint value.
|
||||
func Uint(key string, val uint) Field {
|
||||
return Field{Key: key, Type: UintType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Float64 constructs a field containing a key and Float64 value.
|
||||
func Float64(key string, val float64) Field {
|
||||
return Field{Key: key, Type: Float64Type, Float: val}
|
||||
}
|
||||
|
||||
// Float32 constructs a field containing a key and Float32 value.
|
||||
func Float32(key string, val float32) Field {
|
||||
return Field{Key: key, Type: Float32Type, Float: float64(val)}
|
||||
}
|
||||
|
||||
// String constructs a field containing a key and String value.
|
||||
func String(key string, val string) Field {
|
||||
return Field{Key: key, Type: StringType, String: val}
|
||||
}
|
||||
|
||||
// Stringer constructs a field containing a key and a `fmt.Stringer` value.
|
||||
// The `String` method will be called in lazy fashion.
|
||||
func Stringer(key string, val fmt.Stringer) Field {
|
||||
return Field{Key: key, Type: StringerType, Interface: val}
|
||||
}
|
||||
|
||||
// Err constructs a field containing a default key ("error") and error value.
|
||||
func Err(err error) Field {
|
||||
return NamedErr("error", err)
|
||||
}
|
||||
|
||||
// NamedErr constructs a field containing a key and error value.
|
||||
func NamedErr(key string, err error) Field {
|
||||
return Field{Key: key, Type: ErrorType, Interface: err}
|
||||
}
|
||||
|
||||
// Bool constructs a field containing a key and bool value.
|
||||
func Bool(key string, val bool) Field {
|
||||
var b int64
|
||||
if val {
|
||||
b = 1
|
||||
}
|
||||
return Field{Key: key, Type: BoolType, Integer: b}
|
||||
}
|
||||
|
||||
// Time constructs a field containing a key and time.Time value.
|
||||
func Time(key string, val time.Time) Field {
|
||||
return Field{Key: key, Type: TimeType, Interface: val}
|
||||
}
|
||||
|
||||
// Duration constructs a field containing a key and time.Duration value.
|
||||
func Duration(key string, val time.Duration) Field {
|
||||
return Field{Key: key, Type: DurationType, Integer: int64(val)}
|
||||
}
|
||||
|
||||
// Millis constructs a field containing a key and timestamp value.
|
||||
// The timestamp is expected to be milliseconds since Jan 1, 1970 UTC.
|
||||
func Millis(key string, val int64) Field {
|
||||
return Field{Key: key, Type: TimestampMillisType, Integer: val}
|
||||
}
|
||||
|
||||
// Array constructs a field containing a key and array value.
|
||||
func Array(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: ArrayType, Interface: val}
|
||||
}
|
||||
|
||||
// Map constructs a field containing a key and map value.
|
||||
func Map(key string, val interface{}) Field {
|
||||
return Field{Key: key, Type: MapType, Interface: val}
|
||||
}
|
||||
10
vendor/github.com/mattermost/logr/v2/filter.go
сгенерированный
поставляемый
Обычный файл
10
vendor/github.com/mattermost/logr/v2/filter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,10 @@
|
||||
package logr
|
||||
|
||||
// Filter allows targets to determine which Level(s) are active
|
||||
// for logging and which Level(s) require a stack trace to be output.
|
||||
// A default implementation using "panic, fatal..." is provided, and
|
||||
// a more flexible alternative implementation is also provided that
|
||||
// allows any number of custom levels.
|
||||
type Filter interface {
|
||||
GetEnabledLevel(level Level) (Level, bool)
|
||||
}
|
||||
47
vendor/github.com/mattermost/logr/v2/filtercustom.go
сгенерированный
поставляемый
Обычный файл
47
vendor/github.com/mattermost/logr/v2/filtercustom.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,47 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// CustomFilter allows targets to enable logging via a list of discrete levels.
|
||||
type CustomFilter struct {
|
||||
mux sync.RWMutex
|
||||
levels map[LevelID]Level
|
||||
}
|
||||
|
||||
// NewCustomFilter creates a filter supporting discrete log levels.
|
||||
func NewCustomFilter(levels ...Level) *CustomFilter {
|
||||
filter := &CustomFilter{}
|
||||
filter.Add(levels...)
|
||||
return filter
|
||||
}
|
||||
|
||||
// GetEnabledLevel returns the Level with the specified Level.ID and whether the level
|
||||
// is enabled for this filter.
|
||||
func (cf *CustomFilter) GetEnabledLevel(level Level) (Level, bool) {
|
||||
cf.mux.RLock()
|
||||
defer cf.mux.RUnlock()
|
||||
levelEnabled, ok := cf.levels[level.ID]
|
||||
|
||||
if ok && levelEnabled.Name == "" {
|
||||
levelEnabled.Name = level.Name
|
||||
}
|
||||
|
||||
return levelEnabled, ok
|
||||
}
|
||||
|
||||
// Add adds one or more levels to the list. Adding a level enables logging for
|
||||
// that level on any targets using this CustomFilter.
|
||||
func (cf *CustomFilter) Add(levels ...Level) {
|
||||
cf.mux.Lock()
|
||||
defer cf.mux.Unlock()
|
||||
|
||||
if cf.levels == nil {
|
||||
cf.levels = make(map[LevelID]Level)
|
||||
}
|
||||
|
||||
for _, s := range levels {
|
||||
cf.levels[s.ID] = s
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/mattermost/logr/v2/filterstd.go
сгенерированный
поставляемый
Обычный файл
65
vendor/github.com/mattermost/logr/v2/filterstd.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,65 @@
|
||||
package logr
|
||||
|
||||
// StdFilter allows targets to filter via classic log levels where any level
|
||||
// beyond a certain verbosity/severity is enabled.
|
||||
type StdFilter struct {
|
||||
Lvl Level
|
||||
Stacktrace Level
|
||||
}
|
||||
|
||||
// GetEnabledLevel returns the Level with the specified Level.ID and whether the level
|
||||
// is enabled for this filter.
|
||||
func (lt StdFilter) GetEnabledLevel(level Level) (Level, bool) {
|
||||
enabled := level.ID <= lt.Lvl.ID
|
||||
var levelEnabled Level
|
||||
|
||||
if enabled {
|
||||
switch level.ID {
|
||||
case Panic.ID:
|
||||
levelEnabled = Panic
|
||||
case Fatal.ID:
|
||||
levelEnabled = Fatal
|
||||
case Error.ID:
|
||||
levelEnabled = Error
|
||||
case Warn.ID:
|
||||
levelEnabled = Warn
|
||||
case Info.ID:
|
||||
levelEnabled = Info
|
||||
case Debug.ID:
|
||||
levelEnabled = Debug
|
||||
case Trace.ID:
|
||||
levelEnabled = Trace
|
||||
default:
|
||||
levelEnabled = level
|
||||
}
|
||||
}
|
||||
return levelEnabled, enabled
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the specified Level is at or above this verbosity. Also
|
||||
// determines if a stack trace is required.
|
||||
func (lt StdFilter) IsEnabled(level Level) bool {
|
||||
return level.ID <= lt.Lvl.ID
|
||||
}
|
||||
|
||||
// IsStacktraceEnabled returns true if the specified Level requires a stack trace.
|
||||
func (lt StdFilter) IsStacktraceEnabled(level Level) bool {
|
||||
return level.ID <= lt.Stacktrace.ID
|
||||
}
|
||||
|
||||
var (
|
||||
// Panic is the highest level of severity.
|
||||
Panic = Level{ID: 0, Name: "panic", Color: Red}
|
||||
// Fatal designates a catastrophic error.
|
||||
Fatal = Level{ID: 1, Name: "fatal", Color: Red}
|
||||
// Error designates a serious but possibly recoverable error.
|
||||
Error = Level{ID: 2, Name: "error", Color: Red}
|
||||
// Warn designates non-critical error.
|
||||
Warn = Level{ID: 3, Name: "warn", Color: Yellow}
|
||||
// Info designates information regarding application events.
|
||||
Info = Level{ID: 4, Name: "info", Color: Cyan}
|
||||
// Debug designates verbose information typically used for debugging.
|
||||
Debug = Level{ID: 5, Name: "debug", Color: NoColor}
|
||||
// Trace designates the highest verbosity of log output.
|
||||
Trace = Level{ID: 6, Name: "trace", Color: NoColor}
|
||||
)
|
||||
184
vendor/github.com/mattermost/logr/v2/formatter.go
сгенерированный
поставляемый
Обычный файл
184
vendor/github.com/mattermost/logr/v2/formatter.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,184 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"runtime"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Formatter turns a LogRec into a formatted string.
|
||||
type Formatter interface {
|
||||
// IsStacktraceNeeded returns true if this formatter requires a stacktrace to be
|
||||
// generated for each LogRecord. Enabling features such as `Caller` field require
|
||||
// a stacktrace.
|
||||
IsStacktraceNeeded() bool
|
||||
|
||||
// Format converts a log record to bytes. If buf is not nil then it will be
|
||||
// be filled with the formatted results, otherwise a new buffer will be allocated.
|
||||
Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefTimestampFormat is the default time stamp format used by Plain formatter and others.
|
||||
DefTimestampFormat = "2006-01-02 15:04:05.000 Z07:00"
|
||||
|
||||
// TimestampMillisFormat is the format for logging milliseconds UTC
|
||||
TimestampMillisFormat = "Jan _2 15:04:05.000"
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w Writer) Writes(elems ...[]byte) (int, error) {
|
||||
var count int
|
||||
for _, e := range elems {
|
||||
if c, err := w.Write(e); err != nil {
|
||||
return count + c, err
|
||||
} else {
|
||||
count += c
|
||||
}
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// DefaultFormatter is the default formatter, outputting only text with
|
||||
// no colors and a space delimiter. Use `format.Plain` instead.
|
||||
type DefaultFormatter struct {
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded always returns false for default formatter since the
|
||||
// `Caller` field is not supported.
|
||||
func (p *DefaultFormatter) IsStacktraceNeeded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *DefaultFormatter) Format(rec *LogRec, level Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
timestampFmt := DefTimestampFormat
|
||||
|
||||
buf.WriteString(rec.Time().Format(timestampFmt))
|
||||
buf.Write(Space)
|
||||
|
||||
buf.WriteString(level.Name)
|
||||
buf.Write(Space)
|
||||
|
||||
buf.WriteString(rec.Msg())
|
||||
buf.Write(Space)
|
||||
|
||||
fields := rec.Fields()
|
||||
if len(fields) > 0 {
|
||||
if err := WriteFields(buf, fields, Space, NoColor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if level.Stacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.Write(Newline)
|
||||
if err := WriteStacktrace(buf, rec.StackFrames()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
buf.Write(Newline)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// WriteFields writes zero or more name value pairs to the io.Writer.
|
||||
// The pairs output in key=value format with optional separator between fields.
|
||||
func WriteFields(w io.Writer, fields []Field, separator []byte, color Color) error {
|
||||
ws := Writer{w}
|
||||
|
||||
sep := []byte{}
|
||||
for _, field := range fields {
|
||||
if err := writeField(ws, field, sep, color); err != nil {
|
||||
return err
|
||||
}
|
||||
sep = separator
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeField(ws Writer, field Field, sep []byte, color Color) error {
|
||||
if len(sep) != 0 {
|
||||
if _, err := ws.Write(sep); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := WriteWithColor(ws, field.Key, color); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ws.Write(Equals); err != nil {
|
||||
return err
|
||||
}
|
||||
return field.ValueString(ws, shouldQuote)
|
||||
}
|
||||
|
||||
// shouldQuote returns true if val contains any characters that might be unsafe
|
||||
// when injecting log output into an aggregator, viewer or report.
|
||||
func shouldQuote(val string) bool {
|
||||
for _, c := range val {
|
||||
if !((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
c == '-' || c == '.' || c == '_' || c == '/' || c == '@' || c == '^' || c == '+') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteStacktrace formats and outputs a stack trace to an io.Writer.
|
||||
func WriteStacktrace(w io.Writer, frames []runtime.Frame) error {
|
||||
ws := Writer{w}
|
||||
for _, frame := range frames {
|
||||
if frame.Function != "" {
|
||||
if _, err := ws.Writes(Space, Space, []byte(frame.Function), Newline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if frame.File != "" {
|
||||
s := strconv.FormatInt(int64(frame.Line), 10)
|
||||
if _, err := ws.Writes([]byte{' ', ' ', ' ', ' ', ' ', ' '}, []byte(frame.File), Colon, []byte(s), Newline); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteWithColor outputs a string with the specified ANSI color.
|
||||
func WriteWithColor(w io.Writer, s string, color Color) error {
|
||||
var err error
|
||||
|
||||
writer := func(buf []byte) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = w.Write(buf)
|
||||
}
|
||||
|
||||
if color != NoColor {
|
||||
writer(AnsiColorPrefix)
|
||||
writer([]byte(strconv.FormatInt(int64(color), 10)))
|
||||
writer(AnsiColorSuffix)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
_, err = io.WriteString(w, s)
|
||||
}
|
||||
|
||||
if color != NoColor {
|
||||
writer(AnsiColorPrefix)
|
||||
writer([]byte(strconv.FormatInt(int64(NoColor), 10)))
|
||||
writer(AnsiColorSuffix)
|
||||
}
|
||||
return err
|
||||
}
|
||||
152
vendor/github.com/mattermost/logr/v2/formatters/gelf.go
сгенерированный
поставляемый
Обычный файл
152
vendor/github.com/mattermost/logr/v2/formatters/gelf.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,152 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
GelfVersion = "1.1"
|
||||
GelfVersionKey = "version"
|
||||
GelfHostKey = "host"
|
||||
GelfShortKey = "short_message"
|
||||
GelfFullKey = "full_message"
|
||||
GelfTimestampKey = "timestamp"
|
||||
GelfLevelKey = "level"
|
||||
)
|
||||
|
||||
// Gelf formats log records as GELF rcords (https://docs.graylog.org/en/4.0/pages/gelf.html).
|
||||
type Gelf struct {
|
||||
// Hostname allows a custom hostname, otherwise os.Hostname is used
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// FieldSorter allows custom sorting for the context fields.
|
||||
FieldSorter func(fields []logr.Field) []logr.Field `json:"-"`
|
||||
}
|
||||
|
||||
func (g *Gelf) CheckValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (g *Gelf) IsStacktraceNeeded() bool {
|
||||
return g.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in GELF format.
|
||||
func (g *Gelf) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
gr := gelfRecord{
|
||||
LogRec: rec,
|
||||
Gelf: g,
|
||||
level: level,
|
||||
sorter: g.FieldSorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(gr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf.WriteByte(0)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type gelfRecord struct {
|
||||
*logr.LogRec
|
||||
*Gelf
|
||||
level logr.Level
|
||||
sorter func(fields []logr.Field) []logr.Field
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (gr gelfRecord) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey(GelfVersionKey, GelfVersion)
|
||||
enc.AddStringKey(GelfHostKey, gr.getHostname())
|
||||
enc.AddStringKey(GelfShortKey, gr.Msg())
|
||||
|
||||
if gr.level.Stacktrace {
|
||||
frames := gr.StackFrames()
|
||||
if len(frames) != 0 {
|
||||
var sbuf strings.Builder
|
||||
for _, frame := range frames {
|
||||
fmt.Fprintf(&sbuf, "%s\n %s:%d\n", frame.Function, frame.File, frame.Line)
|
||||
}
|
||||
enc.AddStringKey(GelfFullKey, sbuf.String())
|
||||
}
|
||||
}
|
||||
|
||||
secs := float64(gr.Time().UTC().Unix())
|
||||
millis := float64(gr.Time().Nanosecond() / 1000000)
|
||||
ts := secs + (millis / 1000)
|
||||
enc.AddFloat64Key(GelfTimestampKey, ts)
|
||||
|
||||
enc.AddUint32Key(GelfLevelKey, uint32(gr.level.ID))
|
||||
|
||||
var fields []logr.Field
|
||||
if gr.EnableCaller {
|
||||
caller := logr.Field{
|
||||
Key: "_caller",
|
||||
Type: logr.StringType,
|
||||
String: gr.LogRec.Caller(),
|
||||
}
|
||||
fields = append(fields, caller)
|
||||
}
|
||||
|
||||
fields = append(fields, gr.Fields()...)
|
||||
if gr.sorter != nil {
|
||||
fields = gr.sorter(fields)
|
||||
}
|
||||
|
||||
if len(fields) > 0 {
|
||||
for _, field := range fields {
|
||||
if !strings.HasPrefix("_", field.Key) {
|
||||
field.Key = "_" + field.Key
|
||||
}
|
||||
if err := encodeField(enc, field); err != nil {
|
||||
enc.AddStringKey(field.Key, fmt.Sprintf("<error encoding field: %v>", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if the gelf record pointer is nil.
|
||||
func (gr gelfRecord) IsNil() bool {
|
||||
return gr.LogRec == nil
|
||||
}
|
||||
|
||||
func (g *Gelf) getHostname() string {
|
||||
if g.Hostname != "" {
|
||||
return g.Hostname
|
||||
}
|
||||
h, err := os.Hostname()
|
||||
if err == nil {
|
||||
return h
|
||||
}
|
||||
|
||||
// get the egress IP by fake dialing any address. UDP ensures no dial.
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
local := conn.LocalAddr().(*net.UDPAddr)
|
||||
return local.IP.String()
|
||||
}
|
||||
273
vendor/github.com/mattermost/logr/v2/formatters/json.go
сгенерированный
поставляемый
Обычный файл
273
vendor/github.com/mattermost/logr/v2/formatters/json.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,273 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/francoispqt/gojay"
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// JSON formats log records as JSON.
|
||||
type JSON struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool `json:"disable_timestamp"`
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool `json:"disable_level"`
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool `json:"disable_msg"`
|
||||
// DisableFields disables output of all fields.
|
||||
DisableFields bool `json:"disable_fields"`
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool `json:"disable_stacktrace"`
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string `json:"timestamp_format"`
|
||||
|
||||
// KeyTimestamp overrides the timestamp field key name.
|
||||
KeyTimestamp string `json:"key_timestamp"`
|
||||
|
||||
// KeyLevel overrides the level field key name.
|
||||
KeyLevel string `json:"key_level"`
|
||||
|
||||
// KeyMsg overrides the msg field key name.
|
||||
KeyMsg string `json:"key_msg"`
|
||||
|
||||
// KeyGroupFields when not empty will group all context fields
|
||||
// under this key.
|
||||
KeyGroupFields string `json:"key_group_fields"`
|
||||
|
||||
// KeyStacktrace overrides the stacktrace field key name.
|
||||
KeyStacktrace string `json:"key_stacktrace"`
|
||||
|
||||
// KeyCaller overrides the caller field key name.
|
||||
KeyCaller string `json:"key_caller"`
|
||||
|
||||
// FieldSorter allows custom sorting of the fields. If nil then
|
||||
// no sorting is done.
|
||||
FieldSorter func(fields []logr.Field) []logr.Field `json:"-"`
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (j *JSON) CheckValid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (j *JSON) IsStacktraceNeeded() bool {
|
||||
return j.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes in JSON format.
|
||||
func (j *JSON) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
j.once.Do(j.applyDefaultKeyNames)
|
||||
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
enc := gojay.BorrowEncoder(buf)
|
||||
defer func() {
|
||||
enc.Release()
|
||||
}()
|
||||
|
||||
jlr := JSONLogRec{
|
||||
LogRec: rec,
|
||||
JSON: j,
|
||||
level: level,
|
||||
sorter: j.FieldSorter,
|
||||
}
|
||||
|
||||
err := enc.EncodeObject(jlr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (j *JSON) applyDefaultKeyNames() {
|
||||
if j.KeyTimestamp == "" {
|
||||
j.KeyTimestamp = "timestamp"
|
||||
}
|
||||
if j.KeyLevel == "" {
|
||||
j.KeyLevel = "level"
|
||||
}
|
||||
if j.KeyMsg == "" {
|
||||
j.KeyMsg = "msg"
|
||||
}
|
||||
if j.KeyStacktrace == "" {
|
||||
j.KeyStacktrace = "stacktrace"
|
||||
}
|
||||
if j.KeyCaller == "" {
|
||||
j.KeyCaller = "caller"
|
||||
}
|
||||
}
|
||||
|
||||
// JSONLogRec decorates a LogRec adding JSON encoding.
|
||||
type JSONLogRec struct {
|
||||
*logr.LogRec
|
||||
*JSON
|
||||
level logr.Level
|
||||
sorter func(fields []logr.Field) []logr.Field
|
||||
}
|
||||
|
||||
// MarshalJSONObject encodes the LogRec as JSON.
|
||||
func (jlr JSONLogRec) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
if !jlr.DisableTimestamp {
|
||||
timestampFmt := jlr.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
time := jlr.Time()
|
||||
enc.AddTimeKey(jlr.KeyTimestamp, &time, timestampFmt)
|
||||
}
|
||||
if !jlr.DisableLevel {
|
||||
enc.AddStringKey(jlr.KeyLevel, jlr.level.Name)
|
||||
}
|
||||
if !jlr.DisableMsg {
|
||||
enc.AddStringKey(jlr.KeyMsg, jlr.Msg())
|
||||
}
|
||||
if jlr.EnableCaller {
|
||||
enc.AddStringKey(jlr.KeyCaller, jlr.Caller())
|
||||
}
|
||||
if !jlr.DisableFields {
|
||||
fields := jlr.Fields()
|
||||
if jlr.sorter != nil {
|
||||
fields = jlr.sorter(fields)
|
||||
}
|
||||
if jlr.KeyGroupFields != "" {
|
||||
enc.AddObjectKey(jlr.KeyGroupFields, FieldArray(fields))
|
||||
} else {
|
||||
if len(fields) > 0 {
|
||||
for _, field := range fields {
|
||||
field = jlr.prefixCollision(field)
|
||||
if err := encodeField(enc, field); err != nil {
|
||||
enc.AddStringKey(field.Key, "<error encoding field: "+err.Error()+">")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if jlr.level.Stacktrace && !jlr.DisableStacktrace {
|
||||
frames := jlr.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
enc.AddArrayKey(jlr.KeyStacktrace, stackFrames(frames))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if the LogRec pointer is nil.
|
||||
func (rec JSONLogRec) IsNil() bool {
|
||||
return rec.LogRec == nil
|
||||
}
|
||||
|
||||
func (rec JSONLogRec) prefixCollision(field logr.Field) logr.Field {
|
||||
switch field.Key {
|
||||
case rec.KeyTimestamp, rec.KeyLevel, rec.KeyMsg, rec.KeyStacktrace:
|
||||
f := field
|
||||
f.Key = "_" + field.Key
|
||||
return rec.prefixCollision(f)
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
type stackFrames []runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrames slice as JSON.
|
||||
func (s stackFrames) MarshalJSONArray(enc *gojay.Encoder) {
|
||||
for _, frame := range s {
|
||||
enc.AddObject(stackFrame(frame))
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if stackFrames is empty slice.
|
||||
func (s stackFrames) IsNil() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
type stackFrame runtime.Frame
|
||||
|
||||
// MarshalJSONArray encodes stackFrame as JSON.
|
||||
func (f stackFrame) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
enc.AddStringKey("Function", f.Function)
|
||||
enc.AddStringKey("File", f.File)
|
||||
enc.AddIntKey("Line", f.Line)
|
||||
}
|
||||
|
||||
func (f stackFrame) IsNil() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type FieldArray []logr.Field
|
||||
|
||||
// MarshalJSONObject encodes Fields map to JSON.
|
||||
func (fa FieldArray) MarshalJSONObject(enc *gojay.Encoder) {
|
||||
for _, fld := range fa {
|
||||
if err := encodeField(enc, fld); err != nil {
|
||||
enc.AddStringKey(fld.Key, "<error encoding field: "+err.Error()+">")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsNil returns true if map is nil.
|
||||
func (fa FieldArray) IsNil() bool {
|
||||
return fa == nil
|
||||
}
|
||||
|
||||
func encodeField(enc *gojay.Encoder, field logr.Field) error {
|
||||
// first check if the value has a marshaller already.
|
||||
switch vt := field.Interface.(type) {
|
||||
case gojay.MarshalerJSONObject:
|
||||
enc.AddObjectKey(field.Key, vt)
|
||||
return nil
|
||||
case gojay.MarshalerJSONArray:
|
||||
enc.AddArrayKey(field.Key, vt)
|
||||
return nil
|
||||
}
|
||||
|
||||
switch field.Type {
|
||||
case logr.StringType:
|
||||
enc.AddStringKey(field.Key, field.String)
|
||||
|
||||
case logr.BoolType:
|
||||
var b bool
|
||||
if field.Integer != 0 {
|
||||
b = true
|
||||
}
|
||||
enc.AddBoolKey(field.Key, b)
|
||||
|
||||
case logr.StructType, logr.ArrayType, logr.MapType, logr.UnknownType:
|
||||
b, err := json.Marshal(field.Interface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
embed := gojay.EmbeddedJSON(b)
|
||||
enc.AddEmbeddedJSONKey(field.Key, &embed)
|
||||
|
||||
case logr.StringerType, logr.ErrorType, logr.TimestampMillisType, logr.TimeType, logr.DurationType, logr.BinaryType:
|
||||
var buf strings.Builder
|
||||
_ = field.ValueString(&buf, nil)
|
||||
enc.AddStringKey(field.Key, buf.String())
|
||||
|
||||
case logr.Int64Type, logr.Int32Type, logr.IntType:
|
||||
enc.AddInt64Key(field.Key, field.Integer)
|
||||
|
||||
case logr.Uint64Type, logr.Uint32Type, logr.UintType:
|
||||
enc.AddUint64Key(field.Key, uint64(field.Integer))
|
||||
|
||||
case logr.Float64Type, logr.Float32Type:
|
||||
enc.AddFloat64Key(field.Key, field.Float)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid field type: %d", field.Type)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
146
vendor/github.com/mattermost/logr/v2/formatters/plain.go
сгенерированный
поставляемый
Обычный файл
146
vendor/github.com/mattermost/logr/v2/formatters/plain.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,146 @@
|
||||
package formatters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// Plain is the simplest formatter, outputting only text with
|
||||
// no colors.
|
||||
type Plain struct {
|
||||
// DisableTimestamp disables output of timestamp field.
|
||||
DisableTimestamp bool `json:"disable_timestamp"`
|
||||
// DisableLevel disables output of level field.
|
||||
DisableLevel bool `json:"disable_level"`
|
||||
// DisableMsg disables output of msg field.
|
||||
DisableMsg bool `json:"disable_msg"`
|
||||
// DisableFields disables output of all fields.
|
||||
DisableFields bool `json:"disable_fields"`
|
||||
// DisableStacktrace disables output of stack trace.
|
||||
DisableStacktrace bool `json:"disable_stacktrace"`
|
||||
// EnableCaller enables output of the file and line number that emitted a log record.
|
||||
EnableCaller bool `json:"enable_caller"`
|
||||
|
||||
// Delim is an optional delimiter output between each log field.
|
||||
// Defaults to a single space.
|
||||
Delim string `json:"delim"`
|
||||
|
||||
// MinLevelLen sets the minimum level name length. If the level name is less
|
||||
// than the minimum it will be padded with spaces.
|
||||
MinLevelLen int `json:"min_level_len"`
|
||||
|
||||
// MinMessageLen sets the minimum msg length. If the msg text is less
|
||||
// than the minimum it will be padded with spaces.
|
||||
MinMessageLen int `json:"min_msg_len"`
|
||||
|
||||
// TimestampFormat is an optional format for timestamps. If empty
|
||||
// then DefTimestampFormat is used.
|
||||
TimestampFormat string `json:"timestamp_format"`
|
||||
|
||||
// LineEnd sets the end of line character(s). Defaults to '\n'.
|
||||
LineEnd string `json:"line_end"`
|
||||
|
||||
// EnableColor sets whether output should include color.
|
||||
EnableColor bool `json:"enable_color"`
|
||||
}
|
||||
|
||||
func (p *Plain) CheckValid() error {
|
||||
if p.MinMessageLen < 0 || p.MinMessageLen > 1024 {
|
||||
return fmt.Errorf("min_msg_len is invalid(%d)", p.MinMessageLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStacktraceNeeded returns true if a stacktrace is needed so we can output the `Caller` field.
|
||||
func (p *Plain) IsStacktraceNeeded() bool {
|
||||
return p.EnableCaller
|
||||
}
|
||||
|
||||
// Format converts a log record to bytes.
|
||||
func (p *Plain) Format(rec *logr.LogRec, level logr.Level, buf *bytes.Buffer) (*bytes.Buffer, error) {
|
||||
delim := p.Delim
|
||||
if delim == "" {
|
||||
delim = " "
|
||||
}
|
||||
if buf == nil {
|
||||
buf = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
timestampFmt := p.TimestampFormat
|
||||
if timestampFmt == "" {
|
||||
timestampFmt = logr.DefTimestampFormat
|
||||
}
|
||||
|
||||
color := logr.NoColor
|
||||
if p.EnableColor {
|
||||
color = level.Color
|
||||
}
|
||||
|
||||
if !p.DisableLevel {
|
||||
_ = logr.WriteWithColor(buf, level.Name, color)
|
||||
count := len(level.Name)
|
||||
if p.MinLevelLen > count {
|
||||
_, _ = buf.WriteString(strings.Repeat(" ", p.MinLevelLen-count))
|
||||
}
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
|
||||
if !p.DisableTimestamp {
|
||||
var arr [128]byte
|
||||
tbuf := rec.Time().AppendFormat(arr[:0], timestampFmt)
|
||||
buf.WriteByte('[')
|
||||
buf.Write(tbuf)
|
||||
buf.WriteByte(']')
|
||||
buf.WriteString(delim)
|
||||
}
|
||||
|
||||
if !p.DisableMsg {
|
||||
count, _ := buf.WriteString(rec.Msg())
|
||||
if p.MinMessageLen > count {
|
||||
_, _ = buf.WriteString(strings.Repeat(" ", p.MinMessageLen-count))
|
||||
}
|
||||
_, _ = buf.WriteString(delim)
|
||||
}
|
||||
|
||||
var fields []logr.Field
|
||||
|
||||
if p.EnableCaller {
|
||||
fld := logr.Field{
|
||||
Key: "caller",
|
||||
Type: logr.StringType,
|
||||
String: rec.Caller(),
|
||||
}
|
||||
fields = append(fields, fld)
|
||||
}
|
||||
|
||||
if !p.DisableFields {
|
||||
fields = append(fields, rec.Fields()...)
|
||||
}
|
||||
|
||||
if len(fields) > 0 {
|
||||
if err := logr.WriteFields(buf, fields, logr.Space, color); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if level.Stacktrace && !p.DisableStacktrace {
|
||||
frames := rec.StackFrames()
|
||||
if len(frames) > 0 {
|
||||
buf.WriteString("\n")
|
||||
if err := logr.WriteStacktrace(buf, rec.StackFrames()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.LineEnd == "" {
|
||||
buf.WriteString("\n")
|
||||
} else {
|
||||
buf.WriteString(p.LineEnd)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
6
vendor/github.com/mattermost/logr/go.mod → vendor/github.com/mattermost/logr/v2/go.mod
сгенерированный
поставляемый
6
vendor/github.com/mattermost/logr/go.mod → vendor/github.com/mattermost/logr/v2/go.mod
сгенерированный
поставляемый
@@ -1,11 +1,11 @@
|
||||
module github.com/mattermost/logr
|
||||
module github.com/mattermost/logr/v2
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/francoispqt/gojay v1.2.13
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/wiggin77/cfg v1.0.2
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/wiggin77/merror v1.0.2
|
||||
github.com/wiggin77/srslog v1.0.1
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
8
vendor/github.com/mattermost/logr/go.sum → vendor/github.com/mattermost/logr/v2/go.sum
сгенерированный
поставляемый
8
vendor/github.com/mattermost/logr/go.sum → vendor/github.com/mattermost/logr/v2/go.sum
сгенерированный
поставляемый
@@ -15,6 +15,7 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
@@ -93,15 +94,18 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/wiggin77/cfg v1.0.2 h1:NBUX+iJRr+RTncTqTNvajHwzduqbhCQjEqxLHr6Fk7A=
|
||||
github.com/wiggin77/cfg v1.0.2/go.mod h1:b3gotba2e5bXTqTW48DwIFoLc+4lWKP7WPi/CdvZ4aE=
|
||||
github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
|
||||
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
|
||||
github.com/wiggin77/srslog v1.0.1 h1:gA2XjSMy3DrRdX9UqLuDtuVAAshb8bE1NhX1YK0Qe+8=
|
||||
github.com/wiggin77/srslog v1.0.1/go.mod h1:fehkyYDq1QfuYn60TDPu9YdY2bB85VUW2mvN1WynEls=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
34
vendor/github.com/mattermost/logr/v2/level.go
сгенерированный
поставляемый
Обычный файл
34
vendor/github.com/mattermost/logr/v2/level.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,34 @@
|
||||
package logr
|
||||
|
||||
var AnsiColorPrefix = []byte("\u001b[")
|
||||
var AnsiColorSuffix = []byte("m")
|
||||
|
||||
// Color for formatters that support color output.
|
||||
type Color uint8
|
||||
|
||||
const (
|
||||
NoColor Color = 0
|
||||
Red Color = 31
|
||||
Green Color = 32
|
||||
Yellow Color = 33
|
||||
Blue Color = 34
|
||||
Magenta Color = 35
|
||||
Cyan Color = 36
|
||||
White Color = 37
|
||||
)
|
||||
|
||||
// LevelID is the unique id of each level.
|
||||
type LevelID uint
|
||||
|
||||
// Level provides a mechanism to enable/disable specific log lines.
|
||||
type Level struct {
|
||||
ID LevelID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Stacktrace bool `json:"stacktrace,omitempty"`
|
||||
Color Color `json:"color,omitempty"`
|
||||
}
|
||||
|
||||
// String returns the name of this level.
|
||||
func (level Level) String() string {
|
||||
return level.Name
|
||||
}
|
||||
0
vendor/github.com/mattermost/logr/levelcache.go → vendor/github.com/mattermost/logr/v2/levelcache.go
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/levelcache.go → vendor/github.com/mattermost/logr/v2/levelcache.go
сгенерированный
поставляемый
99
vendor/github.com/mattermost/logr/v2/logger.go
сгенерированный
поставляемый
Обычный файл
99
vendor/github.com/mattermost/logr/v2/logger.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,99 @@
|
||||
package logr
|
||||
|
||||
import "log"
|
||||
|
||||
// Logger provides context for logging via fields.
|
||||
type Logger struct {
|
||||
lgr *Logr
|
||||
fields []Field
|
||||
}
|
||||
|
||||
// Logr returns the `Logr` instance that created this `Logger`.
|
||||
func (logger Logger) Logr() *Logr {
|
||||
return logger.lgr
|
||||
}
|
||||
|
||||
// With creates a new `Logger` with any existing fields plus the new ones.
|
||||
func (logger Logger) With(fields ...Field) Logger {
|
||||
l := Logger{lgr: logger.lgr}
|
||||
size := len(logger.fields) + len(fields)
|
||||
if size > 0 {
|
||||
l.fields = make([]Field, 0, size)
|
||||
l.fields = append(l.fields, logger.fields...)
|
||||
l.fields = append(l.fields, fields...)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// StdLogger creates a standard logger backed by this `Logr.Logger` instance.
|
||||
// All log records are emitted with the specified log level.
|
||||
func (logger Logger) StdLogger(level Level) *log.Logger {
|
||||
return NewStdLogger(level, logger)
|
||||
}
|
||||
|
||||
// IsLevelEnabled determines if the specified level is enabled for at least
|
||||
// one log target.
|
||||
func (logger Logger) IsLevelEnabled(level Level) bool {
|
||||
status := logger.Logr().IsLevelEnabled(level)
|
||||
return status.Enabled
|
||||
}
|
||||
|
||||
// Sugar creates a new `Logger` with a less structured API. Any fields are preserved.
|
||||
func (logger Logger) Sugar(fields ...Field) Sugar {
|
||||
return Sugar{
|
||||
logger: logger.With(fields...),
|
||||
}
|
||||
}
|
||||
|
||||
// Log checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the Logr queue.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (logger Logger) Log(lvl Level, msg string, fields ...Field) {
|
||||
status := logger.lgr.IsLevelEnabled(lvl)
|
||||
if status.Enabled {
|
||||
rec := NewLogRec(lvl, logger, msg, fields, status.Stacktrace)
|
||||
logger.lgr.enqueue(rec)
|
||||
}
|
||||
}
|
||||
|
||||
// LogM calls `Log` multiple times, one for each level provided.
|
||||
func (logger Logger) LogM(levels []Level, msg string, fields ...Field) {
|
||||
for _, lvl := range levels {
|
||||
logger.Log(lvl, msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, msg, fields...)`.
|
||||
func (logger Logger) Trace(msg string, fields ...Field) {
|
||||
logger.Log(Trace, msg, fields...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, msg, fields...)`.
|
||||
func (logger Logger) Debug(msg string, fields ...Field) {
|
||||
logger.Log(Debug, msg, fields...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, msg, fields...)`.
|
||||
func (logger Logger) Info(msg string, fields ...Field) {
|
||||
logger.Log(Info, msg, fields...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, msg, fields...)`.
|
||||
func (logger Logger) Warn(msg string, fields ...Field) {
|
||||
logger.Log(Warn, msg, fields...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, msg, fields...)`.
|
||||
func (logger Logger) Error(msg string, fields ...Field) {
|
||||
logger.Log(Error, msg, fields...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, fields...)`
|
||||
func (logger Logger) Fatal(msg string, fields ...Field) {
|
||||
logger.Log(Fatal, msg, fields...)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, msg, fields...)`
|
||||
func (logger Logger) Panic(msg string, fields ...Field) {
|
||||
logger.Log(Panic, msg, fields...)
|
||||
}
|
||||
471
vendor/github.com/mattermost/logr/v2/logr.go
сгенерированный
поставляемый
Обычный файл
471
vendor/github.com/mattermost/logr/v2/logr.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,471 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// Logr maintains a list of log targets and accepts incoming
|
||||
// log records. Use `New` to create instances.
|
||||
type Logr struct {
|
||||
tmux sync.RWMutex // targetHosts mutex
|
||||
targetHosts []*TargetHost
|
||||
|
||||
in chan *LogRec
|
||||
quit chan struct{} // closed by Shutdown to exit read loop
|
||||
done chan struct{} // closed when read loop exited
|
||||
lvlCache levelCache
|
||||
bufferPool sync.Pool
|
||||
options *options
|
||||
|
||||
metricsMux sync.RWMutex
|
||||
metrics *metrics
|
||||
|
||||
shutdown int32
|
||||
}
|
||||
|
||||
// New creates a new Logr instance with one or more options specified.
|
||||
// Some options with invalid values can cause an error to be returned,
|
||||
// however `logr.New()` using just defaults never errors.
|
||||
func New(opts ...Option) (*Logr, error) {
|
||||
options := &options{
|
||||
maxQueueSize: DefaultMaxQueueSize,
|
||||
enqueueTimeout: DefaultEnqueueTimeout,
|
||||
shutdownTimeout: DefaultShutdownTimeout,
|
||||
flushTimeout: DefaultFlushTimeout,
|
||||
maxPooledBuffer: DefaultMaxPooledBuffer,
|
||||
}
|
||||
|
||||
lgr := &Logr{options: options}
|
||||
|
||||
// apply the options
|
||||
for _, opt := range opts {
|
||||
if err := opt(lgr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
pkgName := GetLogrPackageName()
|
||||
if pkgName != "" {
|
||||
opt := StackFilter(pkgName, pkgName+"/targets", pkgName+"/formatters")
|
||||
_ = opt(lgr)
|
||||
}
|
||||
|
||||
lgr.in = make(chan *LogRec, lgr.options.maxQueueSize)
|
||||
lgr.quit = make(chan struct{})
|
||||
lgr.done = make(chan struct{})
|
||||
|
||||
if lgr.options.useSyncMapLevelCache {
|
||||
lgr.lvlCache = &syncMapLevelCache{}
|
||||
} else {
|
||||
lgr.lvlCache = &arrayLevelCache{}
|
||||
}
|
||||
lgr.lvlCache.setup()
|
||||
|
||||
lgr.bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
|
||||
lgr.initMetrics(lgr.options.metricsCollector, lgr.options.metricsUpdateFreqMillis)
|
||||
|
||||
go lgr.start()
|
||||
|
||||
return lgr, nil
|
||||
}
|
||||
|
||||
// AddTarget adds a target to the logger which will receive
|
||||
// log records for outputting.
|
||||
func (lgr *Logr) AddTarget(target Target, name string, filter Filter, formatter Formatter, maxQueueSize int) error {
|
||||
if lgr.IsShutdown() {
|
||||
return fmt.Errorf("AddTarget called after Logr shut down")
|
||||
}
|
||||
|
||||
lgr.metricsMux.RLock()
|
||||
metrics := lgr.metrics
|
||||
lgr.metricsMux.RUnlock()
|
||||
|
||||
hostOpts := targetHostOptions{
|
||||
name: name,
|
||||
filter: filter,
|
||||
formatter: formatter,
|
||||
maxQueueSize: maxQueueSize,
|
||||
metrics: metrics,
|
||||
}
|
||||
|
||||
host, err := newTargetHost(target, hostOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lgr.tmux.Lock()
|
||||
defer lgr.tmux.Unlock()
|
||||
|
||||
lgr.targetHosts = append(lgr.targetHosts, host)
|
||||
|
||||
lgr.ResetLevelCache()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewLogger creates a Logger using defaults. A `Logger` is light-weight
|
||||
// enough to create on-demand, but typically one or more Loggers are
|
||||
// created and re-used.
|
||||
func (lgr *Logr) NewLogger() Logger {
|
||||
logger := Logger{lgr: lgr}
|
||||
return logger
|
||||
}
|
||||
|
||||
var levelStatusDisabled = LevelStatus{}
|
||||
|
||||
// IsLevelEnabled returns true if at least one target has the specified
|
||||
// level enabled. The result is cached so that subsequent checks are fast.
|
||||
func (lgr *Logr) IsLevelEnabled(lvl Level) LevelStatus {
|
||||
// No levels enabled after shutdown
|
||||
if atomic.LoadInt32(&lgr.shutdown) != 0 {
|
||||
return levelStatusDisabled
|
||||
}
|
||||
|
||||
// Check cache.
|
||||
status, ok := lgr.lvlCache.get(lvl.ID)
|
||||
if ok {
|
||||
return status
|
||||
}
|
||||
|
||||
status = LevelStatus{}
|
||||
|
||||
// Cache miss; check each target.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
enabled, level := host.IsLevelEnabled(lvl)
|
||||
if enabled {
|
||||
status.Enabled = true
|
||||
if level.Stacktrace || host.formatter.IsStacktraceNeeded() {
|
||||
status.Stacktrace = true
|
||||
break // if both level and stacktrace enabled then no sense checking more targets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache and return the result.
|
||||
if err := lgr.lvlCache.put(lvl.ID, status); err != nil {
|
||||
lgr.ReportError(err)
|
||||
return LevelStatus{}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
// HasTargets returns true only if at least one target exists within the lgr.
|
||||
func (lgr *Logr) HasTargets() bool {
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
return len(lgr.targetHosts) > 0
|
||||
}
|
||||
|
||||
// TargetInfo provides name and type for a Target.
|
||||
type TargetInfo struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// TargetInfos enumerates all the targets added to this lgr.
|
||||
// The resulting slice represents a snapshot at time of calling.
|
||||
func (lgr *Logr) TargetInfos() []TargetInfo {
|
||||
infos := make([]TargetInfo, 0)
|
||||
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
|
||||
for _, host := range lgr.targetHosts {
|
||||
inf := TargetInfo{
|
||||
Name: host.String(),
|
||||
Type: fmt.Sprintf("%T", host.target),
|
||||
}
|
||||
infos = append(infos, inf)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// RemoveTargets safely removes one or more targets based on the filtering method.
|
||||
// f should return true to delete the target, false to keep it.
|
||||
// When removing a target, best effort is made to write any queued log records before
|
||||
// closing, with cxt determining how much time can be spent in total.
|
||||
// Note, keep the timeout short since this method blocks certain logging operations.
|
||||
func (lgr *Logr) RemoveTargets(cxt context.Context, f func(ti TargetInfo) bool) error {
|
||||
errs := merror.New()
|
||||
hosts := make([]*TargetHost, 0)
|
||||
|
||||
lgr.tmux.Lock()
|
||||
defer lgr.tmux.Unlock()
|
||||
|
||||
for _, host := range lgr.targetHosts {
|
||||
inf := TargetInfo{
|
||||
Name: host.String(),
|
||||
Type: fmt.Sprintf("%T", host.target),
|
||||
}
|
||||
if f(inf) {
|
||||
if err := host.Shutdown(cxt); err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
} else {
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
}
|
||||
|
||||
lgr.targetHosts = hosts
|
||||
lgr.ResetLevelCache()
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ResetLevelCache resets the cached results of `IsLevelEnabled`. This is
|
||||
// called any time a Target is added or a target's level is changed.
|
||||
func (lgr *Logr) ResetLevelCache() {
|
||||
lgr.lvlCache.clear()
|
||||
}
|
||||
|
||||
// SetMetricsCollector sets (or resets) the metrics collector to be used for gathering
|
||||
// metrics for all targets. Only targets added after this call will use the collector.
|
||||
//
|
||||
// To ensure all targets use a collector, use the `SetMetricsCollector` option when
|
||||
// creating the Logr instead, or configure/reconfigure the Logr after calling this method.
|
||||
func (lgr *Logr) SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) {
|
||||
lgr.initMetrics(collector, updateFreqMillis)
|
||||
}
|
||||
|
||||
// enqueue adds a log record to the logr queue. If the queue is full then
|
||||
// this function either blocks or the log record is dropped, depending on
|
||||
// the result of calling `OnQueueFull`.
|
||||
func (lgr *Logr) enqueue(rec *LogRec) {
|
||||
select {
|
||||
case lgr.in <- rec:
|
||||
default:
|
||||
if lgr.options.onQueueFull != nil && lgr.options.onQueueFull(rec, cap(lgr.in)) {
|
||||
return // drop the record
|
||||
}
|
||||
select {
|
||||
case <-time.After(lgr.options.enqueueTimeout):
|
||||
lgr.ReportError(fmt.Errorf("enqueue timed out for log rec [%v]", rec))
|
||||
case lgr.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// `logr.FlushTimeout` determines how long flush can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) Flush() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), lgr.options.flushTimeout)
|
||||
defer cancel()
|
||||
return lgr.FlushWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Flush blocks while flushing the logr queue and all target queues, by
|
||||
// writing existing log records to valid targets.
|
||||
// Any attempts to add new log records will block until flush is complete.
|
||||
// Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) FlushWithTimeout(ctx context.Context) error {
|
||||
if !lgr.HasTargets() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if lgr.IsShutdown() {
|
||||
return errors.New("Flush called on shut down Logr")
|
||||
}
|
||||
|
||||
rec := newFlushLogRec(lgr.NewLogger())
|
||||
lgr.enqueue(rec)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newTimeoutError("logr queue flush timeout")
|
||||
case <-rec.flush:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsShutdown returns true if this Logr instance has been shut down.
|
||||
// No further log records can be enqueued and no targets added after
|
||||
// shutdown.
|
||||
func (lgr *Logr) IsShutdown() bool {
|
||||
return atomic.LoadInt32(&lgr.shutdown) != 0
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// `logr.ShutdownTimeout` determines how long shutdown can execute before
|
||||
// timing out. Use `IsTimeoutError` to determine if the returned error is
|
||||
// due to a timeout.
|
||||
func (lgr *Logr) Shutdown() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), lgr.options.shutdownTimeout)
|
||||
defer cancel()
|
||||
return lgr.ShutdownWithTimeout(ctx)
|
||||
}
|
||||
|
||||
// Shutdown cleanly stops the logging engine after making best efforts
|
||||
// to flush all targets. Call this function right before application
|
||||
// exit - logr cannot be restarted once shut down.
|
||||
// Use `IsTimeoutError` to determine if the returned error is due to a
|
||||
// timeout.
|
||||
func (lgr *Logr) ShutdownWithTimeout(ctx context.Context) error {
|
||||
if err := lgr.FlushWithTimeout(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if atomic.SwapInt32(&lgr.shutdown, 1) != 0 {
|
||||
return errors.New("Shutdown called again after shut down")
|
||||
}
|
||||
|
||||
lgr.ResetLevelCache()
|
||||
lgr.stopMetricsUpdater()
|
||||
|
||||
close(lgr.quit)
|
||||
|
||||
errs := merror.New()
|
||||
|
||||
// Wait for read loop to exit
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errs.Append(newTimeoutError("logr queue shutdown timeout"))
|
||||
case <-lgr.done:
|
||||
}
|
||||
|
||||
// logr.in channel should now be drained to targets and no more log records
|
||||
// can be added.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
err := host.Shutdown(ctx)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
}
|
||||
}
|
||||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
// ReportError is used to notify the host application of any internal logging errors.
|
||||
// If `OnLoggerError` is not nil, it is called with the error, otherwise the error is
|
||||
// output to `os.Stderr`.
|
||||
func (lgr *Logr) ReportError(err interface{}) {
|
||||
lgr.incErrorCounter()
|
||||
|
||||
if lgr.options.onLoggerError == nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
lgr.options.onLoggerError(fmt.Errorf("%v", err))
|
||||
}
|
||||
|
||||
// BorrowBuffer borrows a buffer from the pool. Release the buffer to reduce garbage collection.
|
||||
func (lgr *Logr) BorrowBuffer() *bytes.Buffer {
|
||||
if lgr.options.disableBufferPool {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
return lgr.bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// ReleaseBuffer returns a buffer to the pool to reduce garbage collection. The buffer is only
|
||||
// retained if less than MaxPooledBuffer.
|
||||
func (lgr *Logr) ReleaseBuffer(buf *bytes.Buffer) {
|
||||
if !lgr.options.disableBufferPool && buf.Cap() < lgr.options.maxPooledBuffer {
|
||||
buf.Reset()
|
||||
lgr.bufferPool.Put(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// start selects on incoming log records until shutdown record is received.
|
||||
// Incoming log records are fanned out to all log targets.
|
||||
func (lgr *Logr) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
lgr.ReportError(r)
|
||||
go lgr.start()
|
||||
} else {
|
||||
close(lgr.done)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-lgr.in:
|
||||
if rec.flush != nil {
|
||||
lgr.flush(rec.flush)
|
||||
} else {
|
||||
rec.prep()
|
||||
lgr.fanout(rec)
|
||||
}
|
||||
case <-lgr.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fanout pushes a LogRec to all targets.
|
||||
func (lgr *Logr) fanout(rec *LogRec) {
|
||||
var host *TargetHost
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
lgr.ReportError(fmt.Errorf("fanout failed for target %s, %v", host.String(), r))
|
||||
}
|
||||
}()
|
||||
|
||||
var logged bool
|
||||
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host = range lgr.targetHosts {
|
||||
if enabled, _ := host.IsLevelEnabled(rec.Level()); enabled {
|
||||
host.Log(rec)
|
||||
logged = true
|
||||
}
|
||||
}
|
||||
|
||||
if logged {
|
||||
lgr.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (lgr *Logr) flush(done chan<- struct{}) {
|
||||
// first drain the logr queue.
|
||||
loop:
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-lgr.in:
|
||||
if rec.flush == nil {
|
||||
rec.prep()
|
||||
lgr.fanout(rec)
|
||||
}
|
||||
default:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
logger := lgr.NewLogger()
|
||||
|
||||
// drain all the targets; block until finished.
|
||||
lgr.tmux.RLock()
|
||||
defer lgr.tmux.RUnlock()
|
||||
for _, host := range lgr.targetHosts {
|
||||
rec := newFlushLogRec(logger)
|
||||
host.Log(rec)
|
||||
<-rec.flush
|
||||
}
|
||||
done <- struct{}{}
|
||||
}
|
||||
117
vendor/github.com/mattermost/logr/logrec.go → vendor/github.com/mattermost/logr/v2/logrec.go
сгенерированный
поставляемый
117
vendor/github.com/mattermost/logr/logrec.go → vendor/github.com/mattermost/logr/v2/logrec.go
сгенерированный
поставляемый
@@ -2,24 +2,13 @@ package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"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 {
|
||||
@@ -29,9 +18,9 @@ type LogRec struct {
|
||||
level Level
|
||||
logger Logger
|
||||
|
||||
template string
|
||||
newline bool
|
||||
args []interface{}
|
||||
msg string
|
||||
newline bool
|
||||
fields []Field
|
||||
|
||||
stackPC []uintptr
|
||||
stackCount int
|
||||
@@ -40,13 +29,14 @@ type LogRec struct {
|
||||
flush chan struct{}
|
||||
|
||||
// remaining fields calculated by `prep`
|
||||
msg string
|
||||
frames []runtime.Frame
|
||||
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, template string, args []interface{}, incStacktrace bool) *LogRec {
|
||||
rec := &LogRec{time: time.Now(), logger: logger, level: lvl, template: template, args: args}
|
||||
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)
|
||||
@@ -60,44 +50,40 @@ 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.
|
||||
// prep 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...)
|
||||
}
|
||||
// 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 {
|
||||
f, more := frames.Next()
|
||||
rec.frames = append(rec.frames, f)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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:]
|
||||
// calc caller if stack trace provided
|
||||
if len(rec.frames) > 0 {
|
||||
rec.caller = calcCaller(rec.frames)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,10 +98,9 @@ func (rec *LogRec) WithTime(time time.Time) *LogRec {
|
||||
time: time,
|
||||
level: rec.level,
|
||||
logger: rec.logger,
|
||||
template: rec.template,
|
||||
newline: rec.newline,
|
||||
args: rec.args,
|
||||
msg: rec.msg,
|
||||
newline: rec.newline,
|
||||
fields: rec.fields,
|
||||
stackPC: rec.stackPC,
|
||||
stackCount: rec.stackCount,
|
||||
frames: rec.frames,
|
||||
@@ -140,9 +125,9 @@ func (rec *LogRec) Level() Level {
|
||||
}
|
||||
|
||||
// Fields returns this log record's Fields.
|
||||
func (rec *LogRec) Fields() Fields {
|
||||
func (rec *LogRec) Fields() []Field {
|
||||
// no locking needed as this field is not mutated.
|
||||
return rec.logger.fields
|
||||
return rec.fieldsAll
|
||||
}
|
||||
|
||||
// Msg returns this log record's message text.
|
||||
@@ -160,6 +145,15 @@ func (rec *LogRec) StackFrames() []runtime.Frame {
|
||||
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 {
|
||||
@@ -167,23 +161,22 @@ func (rec *LogRec) String() string {
|
||||
}
|
||||
|
||||
f := &DefaultFormatter{}
|
||||
buf := rec.logger.logr.BorrowBuffer()
|
||||
defer rec.logger.logr.ReleaseBuffer(buf)
|
||||
buf, _ = f.Format(rec, true, buf)
|
||||
buf := rec.logger.lgr.BorrowBuffer()
|
||||
defer rec.logger.lgr.ReleaseBuffer(buf)
|
||||
buf, _ = f.Format(rec, rec.Level(), 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
|
||||
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 f
|
||||
return ""
|
||||
}
|
||||
129
vendor/github.com/mattermost/logr/metrics.go → vendor/github.com/mattermost/logr/v2/metrics.go
сгенерированный
поставляемый
129
vendor/github.com/mattermost/logr/metrics.go → vendor/github.com/mattermost/logr/v2/metrics.go
сгенерированный
поставляемый
@@ -1,10 +1,6 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
import "time"
|
||||
|
||||
const (
|
||||
DefMetricsUpdateFreqMillis = 15000 // 15 seconds
|
||||
@@ -52,66 +48,93 @@ type TargetWithMetrics interface {
|
||||
EnableMetrics(collector MetricsCollector, updateFreqMillis int64) error
|
||||
}
|
||||
|
||||
func (logr *Logr) getMetricsCollector() MetricsCollector {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
return logr.metrics
|
||||
type metrics struct {
|
||||
collector MetricsCollector
|
||||
updateFreqMillis int64
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// SetMetricsCollector enables metrics collection by supplying a MetricsCollector.
|
||||
// The MetricsCollector provides counters and gauges that are updated by log targets.
|
||||
func (logr *Logr) SetMetricsCollector(collector MetricsCollector) error {
|
||||
// initMetrics initializes metrics collection.
|
||||
func (lgr *Logr) initMetrics(collector MetricsCollector, updatefreq int64) {
|
||||
lgr.stopMetricsUpdater()
|
||||
|
||||
if collector == nil {
|
||||
return errors.New("collector cannot be nil")
|
||||
lgr.metricsMux.Lock()
|
||||
lgr.metrics = nil
|
||||
lgr.metricsMux.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
logr.mux.Lock()
|
||||
logr.metrics = collector
|
||||
logr.queueSizeGauge, _ = collector.QueueSizeGauge("_logr")
|
||||
logr.loggedCounter, _ = collector.LoggedCounter("_logr")
|
||||
logr.errorCounter, _ = collector.ErrorCounter("_logr")
|
||||
logr.mux.Unlock()
|
||||
metrics := &metrics{
|
||||
collector: collector,
|
||||
updateFreqMillis: updatefreq,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
metrics.queueSizeGauge, _ = collector.QueueSizeGauge("_logr")
|
||||
metrics.loggedCounter, _ = collector.LoggedCounter("_logr")
|
||||
metrics.errorCounter, _ = collector.ErrorCounter("_logr")
|
||||
|
||||
logr.metricsInitOnce.Do(func() {
|
||||
logr.metricsDone = make(chan struct{})
|
||||
go logr.startMetricsUpdater()
|
||||
})
|
||||
lgr.metricsMux.Lock()
|
||||
lgr.metrics = metrics
|
||||
lgr.metricsMux.Unlock()
|
||||
|
||||
merr := merror.New()
|
||||
go lgr.startMetricsUpdater()
|
||||
}
|
||||
|
||||
logr.tmux.RLock()
|
||||
defer logr.tmux.RUnlock()
|
||||
for _, target := range logr.targets {
|
||||
if tm, ok := target.(TargetWithMetrics); ok {
|
||||
if err := tm.EnableMetrics(collector, logr.MetricsUpdateFreqMillis); err != nil {
|
||||
merr.Append(err)
|
||||
}
|
||||
func (lgr *Logr) setQueueSizeGauge(val float64) {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (lgr *Logr) incLoggedCounter() {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (lgr *Logr) incErrorCounter() {
|
||||
lgr.metricsMux.RLock()
|
||||
defer lgr.metricsMux.RUnlock()
|
||||
|
||||
if lgr.metrics != nil {
|
||||
lgr.metrics.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `metricsUpdateFreqSecs` seconds until
|
||||
// logr is closed.
|
||||
func (lgr *Logr) startMetricsUpdater() {
|
||||
for {
|
||||
lgr.metricsMux.RLock()
|
||||
metrics := lgr.metrics
|
||||
c := metrics.done
|
||||
lgr.metricsMux.RUnlock()
|
||||
|
||||
select {
|
||||
case <-c:
|
||||
return
|
||||
case <-time.After(time.Duration(metrics.updateFreqMillis) * time.Millisecond):
|
||||
lgr.setQueueSizeGauge(float64(len(lgr.in)))
|
||||
}
|
||||
|
||||
}
|
||||
return merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
func (logr *Logr) setQueueSizeGauge(val float64) {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.queueSizeGauge != nil {
|
||||
logr.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (logr *Logr) incLoggedCounter() {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.loggedCounter != nil {
|
||||
logr.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
func (lgr *Logr) stopMetricsUpdater() {
|
||||
lgr.metricsMux.Lock()
|
||||
defer lgr.metricsMux.Unlock()
|
||||
|
||||
func (logr *Logr) incErrorCounter() {
|
||||
logr.mux.RLock()
|
||||
defer logr.mux.RUnlock()
|
||||
if logr.errorCounter != nil {
|
||||
logr.errorCounter.Inc()
|
||||
if lgr.metrics != nil && lgr.metrics.done != nil {
|
||||
close(lgr.metrics.done)
|
||||
lgr.metrics.done = nil
|
||||
}
|
||||
}
|
||||
192
vendor/github.com/mattermost/logr/v2/options.go
сгенерированный
поставляемый
Обычный файл
192
vendor/github.com/mattermost/logr/v2/options.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,192 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Option func(*Logr) error
|
||||
|
||||
type options struct {
|
||||
maxQueueSize int
|
||||
onLoggerError func(error)
|
||||
onQueueFull func(rec *LogRec, maxQueueSize int) bool
|
||||
onTargetQueueFull func(target Target, rec *LogRec, maxQueueSize int) bool
|
||||
onExit func(code int)
|
||||
onPanic func(err interface{})
|
||||
enqueueTimeout time.Duration
|
||||
shutdownTimeout time.Duration
|
||||
flushTimeout time.Duration
|
||||
useSyncMapLevelCache bool
|
||||
maxPooledBuffer int
|
||||
disableBufferPool bool
|
||||
metricsCollector MetricsCollector
|
||||
metricsUpdateFreqMillis int64
|
||||
stackFilter map[string]struct{}
|
||||
}
|
||||
|
||||
// MaxQueueSize is the maximum number of log records that can be queued.
|
||||
// If exceeded, `OnQueueFull` is called which determines if the log
|
||||
// record will be dropped or block until add is successful.
|
||||
// Defaults to DefaultMaxQueueSize.
|
||||
func MaxQueueSize(size int) Option {
|
||||
return func(l *Logr) error {
|
||||
if size < 0 {
|
||||
return errors.New("size cannot be less than zero")
|
||||
}
|
||||
l.options.maxQueueSize = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnLoggerError, when not nil, is called any time an internal
|
||||
// logging error occurs. For example, this can happen when a
|
||||
// target cannot connect to its data sink.
|
||||
func OnLoggerError(f func(error)) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onLoggerError = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full Logr queue.
|
||||
// `MaxQueueSize` can be used to modify the maximum queue size.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
func OnQueueFull(f func(rec *LogRec, maxQueueSize int) bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onQueueFull = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnTargetQueueFull, when not nil, is called on an attempt to add
|
||||
// a log record to a full target queue provided the target supports reporting
|
||||
// this condition.
|
||||
// This function should return quickly, with a bool indicating whether
|
||||
// the log record should be dropped (true) or block until the log record
|
||||
// is successfully added (false). If nil then blocking (false) is assumed.
|
||||
func OnTargetQueueFull(f func(target Target, rec *LogRec, maxQueueSize int) bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onTargetQueueFull = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnExit, when not nil, is called when a FatalXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `os.Exit(code)`.
|
||||
func OnExit(f func(code int)) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onExit = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// OnPanic, when not nil, is called when a PanicXXX style log API is called.
|
||||
// When nil, then the default behavior is to cleanly shut down this Logr and
|
||||
// call `panic(err)`.
|
||||
func OnPanic(f func(err interface{})) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.onPanic = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnqueueTimeout is the amount of time a log record can take to be queued.
|
||||
// This only applies to blocking enqueue which happen after `logr.OnQueueFull`
|
||||
// is called and returns false.
|
||||
func EnqueueTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.enqueueTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ShutdownTimeout is the amount of time `logr.Shutdown` can execute before
|
||||
// timing out. An alternative is to use `logr.ShutdownWithContext` and supply
|
||||
// a timeout.
|
||||
func ShutdownTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.shutdownTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FlushTimeout is the amount of time `logr.Flush` can execute before
|
||||
// timing out. An alternative is to use `logr.FlushWithContext` and supply
|
||||
// a timeout.
|
||||
func FlushTimeout(dur time.Duration) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.flushTimeout = dur
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UseSyncMapLevelCache can be set to true when high concurrency (e.g. >32 cores)
|
||||
// is expected. This may improve performance with large numbers of cores - benchmark
|
||||
// for your use case.
|
||||
func UseSyncMapLevelCache(use bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.useSyncMapLevelCache = use
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxPooledBufferSize determines the maximum size of a buffer that can be
|
||||
// pooled. To reduce allocations, the buffers needed during formatting (etc)
|
||||
// are pooled. A very large log item will grow a buffer that could stay in
|
||||
// memory indefinitely. This setting lets you control how big a pooled buffer
|
||||
// can be - anything larger will be garbage collected after use.
|
||||
// Defaults to 1MB.
|
||||
func MaxPooledBufferSize(size int) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.maxPooledBuffer = size
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// DisableBufferPool when true disables the buffer pool. See MaxPooledBuffer.
|
||||
func DisableBufferPool(disable bool) Option {
|
||||
return func(l *Logr) error {
|
||||
l.options.disableBufferPool = disable
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// SetMetricsCollector enables metrics collection by supplying a MetricsCollector.
|
||||
// The MetricsCollector provides counters and gauges that are updated by log targets.
|
||||
// `updateFreqMillis` determines how often polled metrics are updated. Defaults to 15000 (15 seconds)
|
||||
// and must be at least 250 so we don't peg the CPU.
|
||||
func SetMetricsCollector(collector MetricsCollector, updateFreqMillis int64) Option {
|
||||
return func(l *Logr) error {
|
||||
if collector == nil {
|
||||
return errors.New("collector cannot be nil")
|
||||
}
|
||||
if updateFreqMillis < 250 {
|
||||
return errors.New("updateFreqMillis cannot be less than 250")
|
||||
}
|
||||
l.options.metricsCollector = collector
|
||||
l.options.metricsUpdateFreqMillis = updateFreqMillis
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StackFilter provides a list of package names to exclude from the top of
|
||||
// stack traces. The Logr packages are automatically filtered.
|
||||
func StackFilter(pkg ...string) Option {
|
||||
return func(l *Logr) error {
|
||||
if l.options.stackFilter == nil {
|
||||
l.options.stackFilter = make(map[string]struct{})
|
||||
}
|
||||
|
||||
for _, p := range pkg {
|
||||
if p != "" {
|
||||
l.options.stackFilter[p] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
57
vendor/github.com/mattermost/logr/v2/pkg.go
сгенерированный
поставляемый
Обычный файл
57
vendor/github.com/mattermost/logr/v2/pkg.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,57 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
maximumStackDepth int = 30
|
||||
)
|
||||
|
||||
var (
|
||||
logrPkg string
|
||||
pkgCalcOnce sync.Once
|
||||
)
|
||||
|
||||
// GetPackageName returns the root package name of Logr.
|
||||
func GetLogrPackageName() string {
|
||||
pkgCalcOnce.Do(func() {
|
||||
logrPkg = GetPackageName("GetLogrPackageName")
|
||||
})
|
||||
return logrPkg
|
||||
}
|
||||
|
||||
// GetPackageName returns the package name of the caller.
|
||||
// `callingFuncName` should be the name of the calling function and
|
||||
// should be unique enough not to collide with any runtime methods.
|
||||
func GetPackageName(callingFuncName string) string {
|
||||
var pkgName string
|
||||
|
||||
pcs := make([]uintptr, maximumStackDepth)
|
||||
_ = runtime.Callers(0, pcs)
|
||||
|
||||
for _, pc := range pcs {
|
||||
funcName := runtime.FuncForPC(pc).Name()
|
||||
if strings.Contains(funcName, callingFuncName) {
|
||||
pkgName = ResolvePackageName(funcName)
|
||||
break
|
||||
}
|
||||
}
|
||||
return pkgName
|
||||
}
|
||||
|
||||
// ResolvePackageName reduces a fully qualified function name to the package name
|
||||
func ResolvePackageName(f string) string {
|
||||
for {
|
||||
lastPeriod := strings.LastIndex(f, ".")
|
||||
lastSlash := strings.LastIndex(f, "/")
|
||||
if lastPeriod > lastSlash {
|
||||
f = f[:lastPeriod]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
56
vendor/github.com/mattermost/logr/v2/stdlogger.go
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/mattermost/logr/v2/stdlogger.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NewStdLogger creates a standard logger backed by a Logr instance.
|
||||
// All log records are emitted with the specified log level.
|
||||
func NewStdLogger(level Level, logger Logger) *log.Logger {
|
||||
adapter := newStdLogAdapter(logger, level)
|
||||
return log.New(adapter, "", 0)
|
||||
}
|
||||
|
||||
// RedirectStdLog redirects output from the standard library's package-global logger
|
||||
// to this logger at the specified level and with zero or more Field's. Since Logr already
|
||||
// handles caller annotations, timestamps, etc., it automatically disables the standard
|
||||
// library's annotations and prefixing.
|
||||
// A function is returned that restores the original prefix and flags and resets the standard
|
||||
// library's output to os.Stderr.
|
||||
func (lgr *Logr) RedirectStdLog(level Level, fields ...Field) func() {
|
||||
flags := log.Flags()
|
||||
prefix := log.Prefix()
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("")
|
||||
|
||||
logger := lgr.NewLogger().With(fields...)
|
||||
adapter := newStdLogAdapter(logger, level)
|
||||
log.SetOutput(adapter)
|
||||
|
||||
return func() {
|
||||
log.SetFlags(flags)
|
||||
log.SetPrefix(prefix)
|
||||
log.SetOutput(os.Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
type stdLogAdapter struct {
|
||||
logger Logger
|
||||
level Level
|
||||
}
|
||||
|
||||
func newStdLogAdapter(logger Logger, level Level) *stdLogAdapter {
|
||||
return &stdLogAdapter{
|
||||
logger: logger,
|
||||
level: level,
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements io.Writer
|
||||
func (a *stdLogAdapter) Write(p []byte) (int, error) {
|
||||
s := strings.TrimSpace(string(p))
|
||||
a.logger.Log(a.level, s)
|
||||
return len(p), nil
|
||||
}
|
||||
119
vendor/github.com/mattermost/logr/v2/sugar.go
сгенерированный
поставляемый
Обычный файл
119
vendor/github.com/mattermost/logr/v2/sugar.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,119 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Sugar provides a less structured API for logging.
|
||||
type Sugar struct {
|
||||
logger Logger
|
||||
}
|
||||
|
||||
func (s Sugar) sugarLog(lvl Level, msg string, args ...interface{}) {
|
||||
if s.logger.IsLevelEnabled(lvl) {
|
||||
fields := make([]Field, 0, len(args))
|
||||
for _, arg := range args {
|
||||
fields = append(fields, Any("", arg))
|
||||
}
|
||||
s.logger.Log(lvl, msg, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace is a convenience method equivalent to `Log(TraceLevel, msg, args...)`.
|
||||
func (s Sugar) Trace(msg string, args ...interface{}) {
|
||||
s.sugarLog(Trace, msg, args...)
|
||||
}
|
||||
|
||||
// Debug is a convenience method equivalent to `Log(DebugLevel, msg, args...)`.
|
||||
func (s Sugar) Debug(msg string, args ...interface{}) {
|
||||
s.sugarLog(Debug, msg, args...)
|
||||
}
|
||||
|
||||
// Print ensures compatibility with std lib logger.
|
||||
func (s Sugar) Print(msg string, args ...interface{}) {
|
||||
s.Info(msg, args...)
|
||||
}
|
||||
|
||||
// Info is a convenience method equivalent to `Log(InfoLevel, msg, args...)`.
|
||||
func (s Sugar) Info(msg string, args ...interface{}) {
|
||||
s.sugarLog(Info, msg, args...)
|
||||
}
|
||||
|
||||
// Warn is a convenience method equivalent to `Log(WarnLevel, msg, args...)`.
|
||||
func (s Sugar) Warn(msg string, args ...interface{}) {
|
||||
s.sugarLog(Warn, msg, args...)
|
||||
}
|
||||
|
||||
// Error is a convenience method equivalent to `Log(ErrorLevel, msg, args...)`.
|
||||
func (s Sugar) Error(msg string, args ...interface{}) {
|
||||
s.sugarLog(Error, msg, args...)
|
||||
}
|
||||
|
||||
// Fatal is a convenience method equivalent to `Log(FatalLevel, msg, args...)`
|
||||
func (s Sugar) Fatal(msg string, args ...interface{}) {
|
||||
s.sugarLog(Fatal, msg, args...)
|
||||
}
|
||||
|
||||
// Panic is a convenience method equivalent to `Log(PanicLevel, msg, args...)`
|
||||
func (s Sugar) Panic(msg string, args ...interface{}) {
|
||||
s.sugarLog(Panic, msg, args...)
|
||||
}
|
||||
|
||||
//
|
||||
// Printf style
|
||||
//
|
||||
|
||||
// Logf checks that the level matches one or more targets, and
|
||||
// if so, generates a log record that is added to the main
|
||||
// queue (channel). Arguments are handled in the manner of fmt.Printf.
|
||||
func (s Sugar) Logf(lvl Level, format string, args ...interface{}) {
|
||||
if s.logger.IsLevelEnabled(lvl) {
|
||||
var msg string
|
||||
if format == "" {
|
||||
msg = fmt.Sprint(args...)
|
||||
} else {
|
||||
msg = fmt.Sprintf(format, args...)
|
||||
}
|
||||
s.logger.Log(lvl, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef is a convenience method equivalent to `Logf(TraceLevel, args...)`.
|
||||
func (s Sugar) Tracef(format string, args ...interface{}) {
|
||||
s.Logf(Trace, format, args...)
|
||||
}
|
||||
|
||||
// Debugf is a convenience method equivalent to `Logf(DebugLevel, args...)`.
|
||||
func (s Sugar) Debugf(format string, args ...interface{}) {
|
||||
s.Logf(Debug, format, args...)
|
||||
}
|
||||
|
||||
// Infof is a convenience method equivalent to `Logf(InfoLevel, args...)`.
|
||||
func (s Sugar) Infof(format string, args ...interface{}) {
|
||||
s.Logf(Info, format, args...)
|
||||
}
|
||||
|
||||
// Printf ensures compatibility with std lib logger.
|
||||
func (s Sugar) Printf(format string, args ...interface{}) {
|
||||
s.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf is a convenience method equivalent to `Logf(WarnLevel, args...)`.
|
||||
func (s Sugar) Warnf(format string, args ...interface{}) {
|
||||
s.Logf(Warn, format, args...)
|
||||
}
|
||||
|
||||
// Errorf is a convenience method equivalent to `Logf(ErrorLevel, args...)`.
|
||||
func (s Sugar) Errorf(format string, args ...interface{}) {
|
||||
s.Logf(Error, format, args...)
|
||||
}
|
||||
|
||||
// Fatalf is a convenience method equivalent to `Logf(FatalLevel, args...)`
|
||||
func (s Sugar) Fatalf(format string, args ...interface{}) {
|
||||
s.Logf(Fatal, format, args...)
|
||||
}
|
||||
|
||||
// Panicf is a convenience method equivalent to `Logf(PanicLevel, args...)`
|
||||
func (s Sugar) Panicf(format string, args ...interface{}) {
|
||||
s.Logf(Panic, format, args...)
|
||||
}
|
||||
304
vendor/github.com/mattermost/logr/v2/target.go
сгенерированный
поставляемый
Обычный файл
304
vendor/github.com/mattermost/logr/v2/target.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,304 @@
|
||||
package logr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Target represents a destination for log records such as file,
|
||||
// database, TCP socket, etc.
|
||||
type Target interface {
|
||||
// Init is called once to initialize the target.
|
||||
Init() error
|
||||
|
||||
// Write outputs to this target's destination.
|
||||
Write(p []byte, rec *LogRec) (int, error)
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
Shutdown() error
|
||||
}
|
||||
|
||||
type targetMetrics struct {
|
||||
queueSizeGauge Gauge
|
||||
loggedCounter Counter
|
||||
errorCounter Counter
|
||||
droppedCounter Counter
|
||||
blockedCounter Counter
|
||||
}
|
||||
|
||||
type targetHostOptions struct {
|
||||
name string
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
maxQueueSize int
|
||||
metrics *metrics
|
||||
}
|
||||
|
||||
// TargetHost hosts and manages the lifecycle of a target.
|
||||
// Incoming log records are queued and formatted before
|
||||
// being passed to the target.
|
||||
type TargetHost struct {
|
||||
target Target
|
||||
name string
|
||||
|
||||
filter Filter
|
||||
formatter Formatter
|
||||
|
||||
in chan *LogRec
|
||||
quit chan struct{} // closed by Shutdown to exit read loop
|
||||
done chan struct{} // closed when read loop exited
|
||||
targetMetrics *targetMetrics
|
||||
|
||||
shutdown int32
|
||||
}
|
||||
|
||||
func newTargetHost(target Target, options targetHostOptions) (*TargetHost, error) {
|
||||
host := &TargetHost{
|
||||
target: target,
|
||||
name: options.name,
|
||||
filter: options.filter,
|
||||
formatter: options.formatter,
|
||||
in: make(chan *LogRec, options.maxQueueSize),
|
||||
quit: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
if host.name == "" {
|
||||
host.name = fmt.Sprintf("%T", target)
|
||||
}
|
||||
|
||||
if host.filter == nil {
|
||||
host.filter = &StdFilter{Lvl: Fatal}
|
||||
}
|
||||
if host.formatter == nil {
|
||||
host.formatter = &DefaultFormatter{}
|
||||
}
|
||||
|
||||
err := host.initMetrics(options.metrics)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = target.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go host.start()
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
func (h *TargetHost) initMetrics(metrics *metrics) error {
|
||||
if metrics == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
tmetrics := &targetMetrics{}
|
||||
|
||||
if tmetrics.queueSizeGauge, err = metrics.collector.QueueSizeGauge(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.loggedCounter, err = metrics.collector.LoggedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.errorCounter, err = metrics.collector.ErrorCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.droppedCounter, err = metrics.collector.DroppedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmetrics.blockedCounter, err = metrics.collector.BlockedCounter(h.name); err != nil {
|
||||
return err
|
||||
}
|
||||
h.targetMetrics = tmetrics
|
||||
|
||||
updateFreqMillis := metrics.updateFreqMillis
|
||||
if updateFreqMillis == 0 {
|
||||
updateFreqMillis = DefMetricsUpdateFreqMillis
|
||||
}
|
||||
if updateFreqMillis < 250 {
|
||||
updateFreqMillis = 250 // don't peg the CPU
|
||||
}
|
||||
|
||||
go h.startMetricsUpdater(updateFreqMillis)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLevelEnabled returns true if this target should emit logs for the specified level.
|
||||
func (h *TargetHost) IsLevelEnabled(lvl Level) (enabled bool, level Level) {
|
||||
level, enabled = h.filter.GetEnabledLevel(lvl)
|
||||
return enabled, level
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best
|
||||
// effort to flush queue.
|
||||
func (h *TargetHost) Shutdown(ctx context.Context) error {
|
||||
if atomic.SwapInt32(&h.shutdown, 1) != 0 {
|
||||
return errors.New("targetHost shutdown called more than once")
|
||||
}
|
||||
|
||||
close(h.quit)
|
||||
|
||||
// No more records can be accepted; now wait for read loop to exit.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-h.done:
|
||||
}
|
||||
|
||||
// b.in channel should now be drained.
|
||||
return h.target.Shutdown()
|
||||
}
|
||||
|
||||
// Log queues a log record to be output to this target's destination.
|
||||
func (h *TargetHost) Log(rec *LogRec) {
|
||||
if atomic.LoadInt32(&h.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
lgr := rec.Logger().Logr()
|
||||
select {
|
||||
case h.in <- rec:
|
||||
default:
|
||||
handler := lgr.options.onTargetQueueFull
|
||||
if handler != nil && handler(h.target, rec, cap(h.in)) {
|
||||
h.incDroppedCounter()
|
||||
return // drop the record
|
||||
}
|
||||
h.incBlockedCounter()
|
||||
|
||||
select {
|
||||
case <-time.After(lgr.options.enqueueTimeout):
|
||||
lgr.ReportError(fmt.Errorf("target enqueue timeout for log rec [%v]", rec))
|
||||
case h.in <- rec: // block until success or timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) setQueueSizeGauge(val float64) {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.queueSizeGauge.Set(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incLoggedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.loggedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incErrorCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.errorCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incDroppedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.droppedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) incBlockedCounter() {
|
||||
if h.targetMetrics != nil {
|
||||
h.targetMetrics.blockedCounter.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a name for this target.
|
||||
func (h *TargetHost) String() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
// start accepts log records via In channel and writes to the
|
||||
// supplied target, until Done channel signaled.
|
||||
func (h *TargetHost) start() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "TargetHost.start -- ", r)
|
||||
go h.start()
|
||||
} else {
|
||||
close(h.done)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
var rec *LogRec
|
||||
select {
|
||||
case rec = <-h.in:
|
||||
if rec.flush != nil {
|
||||
h.flush(rec.flush)
|
||||
} else {
|
||||
err := h.writeRec(rec)
|
||||
if err != nil {
|
||||
h.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
} else {
|
||||
h.incLoggedCounter()
|
||||
}
|
||||
}
|
||||
case <-h.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TargetHost) writeRec(rec *LogRec) error {
|
||||
level, enabled := h.filter.GetEnabledLevel(rec.Level())
|
||||
if !enabled {
|
||||
// how did we get here?
|
||||
return fmt.Errorf("level %s not enabled for target %s", rec.Level().Name, h.name)
|
||||
}
|
||||
|
||||
buf := rec.logger.lgr.BorrowBuffer()
|
||||
defer rec.logger.lgr.ReleaseBuffer(buf)
|
||||
|
||||
buf, err := h.formatter.Format(rec, level, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = h.target.Write(buf.Bytes(), rec)
|
||||
return err
|
||||
}
|
||||
|
||||
// startMetricsUpdater updates the metrics for any polled values every `updateFreqMillis` seconds until
|
||||
// target is shut down.
|
||||
func (h *TargetHost) startMetricsUpdater(updateFreqMillis int64) {
|
||||
for {
|
||||
select {
|
||||
case <-h.done:
|
||||
return
|
||||
case <-time.After(time.Duration(updateFreqMillis) * time.Millisecond):
|
||||
h.setQueueSizeGauge(float64(len(h.in)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush drains the queue and notifies when done.
|
||||
func (h *TargetHost) flush(done chan<- struct{}) {
|
||||
for {
|
||||
var rec *LogRec
|
||||
var err error
|
||||
select {
|
||||
case rec = <-h.in:
|
||||
// ignore any redundant flush records.
|
||||
if rec.flush == nil {
|
||||
err = h.writeRec(rec)
|
||||
if err != nil {
|
||||
h.incErrorCounter()
|
||||
rec.Logger().Logr().ReportError(err)
|
||||
}
|
||||
}
|
||||
default:
|
||||
done <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/mattermost/logr/target/file.go → vendor/github.com/mattermost/logr/v2/targets/file.go
сгенерированный
поставляемый
65
vendor/github.com/mattermost/logr/target/file.go → vendor/github.com/mattermost/logr/v2/targets/file.go
сгенерированный
поставляемый
@@ -1,11 +1,10 @@
|
||||
package target
|
||||
package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/mattermost/logr"
|
||||
"github.com/wiggin77/merror"
|
||||
"github.com/mattermost/logr/v2"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
@@ -13,38 +12,44 @@ type FileOptions struct {
|
||||
// Filename is the file to write logs to. Backup log files will be retained
|
||||
// in the same directory. It uses <processname>-lumberjack.log in
|
||||
// os.TempDir() if empty.
|
||||
Filename string
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// MaxSize is the maximum size in megabytes of the log file before it gets
|
||||
// rotated. It defaults to 100 megabytes.
|
||||
MaxSize int
|
||||
MaxSize int `json:"max_size"`
|
||||
|
||||
// MaxAge is the maximum number of days to retain old log files based on the
|
||||
// timestamp encoded in their filename. Note that a day is defined as 24
|
||||
// hours and may not exactly correspond to calendar days due to daylight
|
||||
// savings, leap seconds, etc. The default is not to remove old log files
|
||||
// based on age.
|
||||
MaxAge int
|
||||
MaxAge int `json:"max_age"`
|
||||
|
||||
// MaxBackups is the maximum number of old log files to retain. The default
|
||||
// is to retain all old log files (though MaxAge may still cause them to get
|
||||
// deleted.)
|
||||
MaxBackups int
|
||||
MaxBackups int `json:"max_backups"`
|
||||
|
||||
// Compress determines if the rotated log files should be compressed
|
||||
// using gzip. The default is not to perform compression.
|
||||
Compress bool
|
||||
Compress bool `json:"compress"`
|
||||
}
|
||||
|
||||
func (fo FileOptions) CheckValid() error {
|
||||
if fo.Filename == "" {
|
||||
return errors.New("filename cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// File outputs log records to a file which can be log rotated based on size or age.
|
||||
// Uses `https://github.com/natefinch/lumberjack` for rotation.
|
||||
type File struct {
|
||||
logr.Basic
|
||||
out io.WriteCloser
|
||||
}
|
||||
|
||||
// NewFileTarget creates a target capable of outputting log records to a rotated file.
|
||||
func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOptions, maxQueue int) *File {
|
||||
func NewFileTarget(opts FileOptions) *File {
|
||||
lumber := &lumberjack.Logger{
|
||||
Filename: opts.Filename,
|
||||
MaxSize: opts.MaxSize,
|
||||
@@ -53,35 +58,21 @@ func NewFileTarget(filter logr.Filter, formatter logr.Formatter, opts FileOption
|
||||
Compress: opts.Compress,
|
||||
}
|
||||
f := &File{out: lumber}
|
||||
f.Basic.Start(f, f, filter, formatter, maxQueue)
|
||||
return f
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter,
|
||||
// and outputs to a file.
|
||||
func (f *File) Write(rec *logr.LogRec) error {
|
||||
_, stacktrace := f.IsLevelEnabled(rec.Level())
|
||||
|
||||
buf := rec.Logger().Logr().BorrowBuffer()
|
||||
defer rec.Logger().Logr().ReleaseBuffer(buf)
|
||||
|
||||
buf, err := f.Formatter().Format(rec, stacktrace, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.out.Write(buf.Bytes())
|
||||
return err
|
||||
// Init is called once to initialize the target.
|
||||
func (f *File) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown flushes any remaining log records and closes the file.
|
||||
func (f *File) Shutdown(ctx context.Context) error {
|
||||
errs := merror.New()
|
||||
|
||||
err := f.Basic.Shutdown(ctx)
|
||||
errs.Append(err)
|
||||
|
||||
err = f.out.Close()
|
||||
errs.Append(err)
|
||||
|
||||
return errs.ErrorOrNil()
|
||||
// Write outputs bytes to this file target.
|
||||
func (f *File) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return f.out.Write(p)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (f *File) Shutdown() error {
|
||||
return f.out.Close()
|
||||
}
|
||||
112
vendor/github.com/mattermost/logr/v2/targets/syslog.go
сгенерированный
поставляемый
Обычный файл
112
vendor/github.com/mattermost/logr/v2/targets/syslog.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,112 @@
|
||||
// +build !windows,!nacl,!plan9
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
syslog "github.com/wiggin77/srslog"
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
params *SyslogOptions
|
||||
writer *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogOptions provides parameters for dialing a syslog daemon.
|
||||
type SyslogOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (so SyslogOptions) CheckValid() error {
|
||||
if so.Host == "" && so.IP == "" {
|
||||
return errors.New("missing host")
|
||||
}
|
||||
if so.Port == 0 {
|
||||
return errors.New("missing port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
|
||||
func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
|
||||
if params == nil {
|
||||
return nil, errors.New("params cannot be nil")
|
||||
}
|
||||
|
||||
s := &Syslog{
|
||||
params: params,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (s *Syslog) Init() error {
|
||||
network := "tcp"
|
||||
var config *tls.Config
|
||||
|
||||
if s.params.TLS {
|
||||
network = "tcp+tls"
|
||||
config = &tls.Config{InsecureSkipVerify: s.params.Insecure}
|
||||
if s.params.Cert != "" {
|
||||
pool, err := GetCertPool(s.params.Cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.RootCAs = pool
|
||||
}
|
||||
}
|
||||
raddr := fmt.Sprintf("%s:%d", s.params.IP, s.params.Port)
|
||||
if raddr == ":0" {
|
||||
// If no IP:port provided then connect to local syslog.
|
||||
raddr = ""
|
||||
network = ""
|
||||
}
|
||||
|
||||
var err error
|
||||
s.writer, err = syslog.DialWithTLSConfig(network, raddr, syslog.LOG_INFO, s.params.Tag, config)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
txt := string(p)
|
||||
n := len(txt)
|
||||
var err error
|
||||
|
||||
switch rec.Level() {
|
||||
case logr.Panic, logr.Fatal:
|
||||
err = s.writer.Crit(txt)
|
||||
case logr.Error:
|
||||
err = s.writer.Err(txt)
|
||||
case logr.Warn:
|
||||
err = s.writer.Warning(txt)
|
||||
case logr.Debug, logr.Trace:
|
||||
err = s.writer.Debug(txt)
|
||||
default:
|
||||
// logr.Info plus all custom levels.
|
||||
err = s.writer.Info(txt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n = 0
|
||||
// syslog writer will try to reconnect.
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (s *Syslog) Shutdown() error {
|
||||
return s.writer.Close()
|
||||
}
|
||||
56
vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/mattermost/logr/v2/targets/syslog_unsupported.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
// +build windows nacl plan9
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
syslog "github.com/wiggin77/srslog"
|
||||
)
|
||||
|
||||
const (
|
||||
unsupported = "Syslog target is not supported on this platform."
|
||||
)
|
||||
|
||||
// Syslog outputs log records to local or remote syslog.
|
||||
type Syslog struct {
|
||||
params *SyslogOptions
|
||||
writer *syslog.Writer
|
||||
}
|
||||
|
||||
// SyslogOptions provides parameters for dialing a syslog daemon.
|
||||
type SyslogOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
func (so SyslogOptions) CheckValid() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
|
||||
// NewSyslogTarget creates a target capable of outputting log records to remote or local syslog, with or without TLS.
|
||||
func NewSyslogTarget(params *SyslogOptions) (*Syslog, error) {
|
||||
return nil, errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (s *Syslog) Init() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (s *Syslog) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return 0, errors.New(unsupported)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (s *Syslog) Shutdown() error {
|
||||
return errors.New(unsupported)
|
||||
}
|
||||
251
vendor/github.com/mattermost/logr/v2/targets/tcp.go
сгенерированный
поставляемый
Обычный файл
251
vendor/github.com/mattermost/logr/v2/targets/tcp.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package targets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
DialTimeoutSecs = 30
|
||||
WriteTimeoutSecs = 30
|
||||
RetryBackoffMillis int64 = 100
|
||||
MaxRetryBackoffMillis int64 = 30 * 1000 // 30 seconds
|
||||
)
|
||||
|
||||
// Tcp outputs log records to raw socket server.
|
||||
type Tcp struct {
|
||||
options *TcpOptions
|
||||
addy string
|
||||
|
||||
mutex sync.Mutex
|
||||
conn net.Conn
|
||||
monitor chan struct{}
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
// TcpOptions provides parameters for dialing a socket server.
|
||||
type TcpOptions struct {
|
||||
IP string `json:"ip,omitempty"` // deprecated
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
TLS bool `json:"tls"`
|
||||
Cert string `json:"cert"`
|
||||
Insecure bool `json:"insecure"`
|
||||
}
|
||||
|
||||
func (to TcpOptions) CheckValid() error {
|
||||
if to.Host == "" && to.IP == "" {
|
||||
return errors.New("missing host")
|
||||
}
|
||||
if to.Port == 0 {
|
||||
return errors.New("missing port")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewTcpTarget creates a target capable of outputting log records to a raw socket, with or without TLS.
|
||||
func NewTcpTarget(options *TcpOptions) *Tcp {
|
||||
tcp := &Tcp{
|
||||
options: options,
|
||||
addy: fmt.Sprintf("%s:%d", options.IP, options.Port),
|
||||
monitor: make(chan struct{}),
|
||||
shutdown: make(chan struct{}),
|
||||
}
|
||||
return tcp
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (tcp *Tcp) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConn provides a net.Conn. If a connection already exists, it is returned immediately,
|
||||
// otherwise this method blocks until a new connection is created, timeout or shutdown.
|
||||
func (tcp *Tcp) getConn(reporter func(err interface{})) (net.Conn, error) {
|
||||
tcp.mutex.Lock()
|
||||
defer tcp.mutex.Unlock()
|
||||
|
||||
if tcp.conn != nil {
|
||||
return tcp.conn, nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
conn net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
connChan := make(chan result)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*DialTimeoutSecs)
|
||||
defer cancel()
|
||||
|
||||
go func(ctx context.Context, ch chan result) {
|
||||
conn, err := tcp.dial(ctx)
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
|
||||
return
|
||||
}
|
||||
tcp.conn = conn
|
||||
tcp.monitor = make(chan struct{})
|
||||
go monitor(tcp.conn, tcp.monitor)
|
||||
ch <- result{conn: conn, err: err}
|
||||
}(ctx, connChan)
|
||||
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
return nil, errors.New("shutdown")
|
||||
case res := <-connChan:
|
||||
return res.conn, res.err
|
||||
}
|
||||
}
|
||||
|
||||
// dial connects to a TCP socket, and optionally performs a TLS handshake.
|
||||
// A non-nil context must be provided which can cancel the dial.
|
||||
func (tcp *Tcp) dial(ctx context.Context) (net.Conn, error) {
|
||||
var dialer net.Dialer
|
||||
dialer.Timeout = time.Second * DialTimeoutSecs
|
||||
conn, err := dialer.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", tcp.options.IP, tcp.options.Port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !tcp.options.TLS {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
tlsconfig := &tls.Config{
|
||||
ServerName: tcp.options.IP,
|
||||
InsecureSkipVerify: tcp.options.Insecure,
|
||||
}
|
||||
if tcp.options.Cert != "" {
|
||||
pool, err := GetCertPool(tcp.options.Cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsconfig.RootCAs = pool
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, tlsconfig)
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func (tcp *Tcp) close() error {
|
||||
tcp.mutex.Lock()
|
||||
defer tcp.mutex.Unlock()
|
||||
|
||||
var err error
|
||||
if tcp.conn != nil {
|
||||
close(tcp.monitor)
|
||||
err = tcp.conn.Close()
|
||||
tcp.conn = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown stops processing log records after making best effort to flush queue.
|
||||
func (tcp *Tcp) Shutdown() error {
|
||||
err := tcp.close()
|
||||
close(tcp.shutdown)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write converts the log record to bytes, via the Formatter, and outputs to the socket.
|
||||
// Called by dedicated target goroutine and will block until success or shutdown.
|
||||
func (tcp *Tcp) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
try := 1
|
||||
backoff := RetryBackoffMillis
|
||||
for {
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
return 0, nil
|
||||
default:
|
||||
}
|
||||
|
||||
reporter := rec.Logger().Logr().ReportError
|
||||
|
||||
conn, err := tcp.getConn(reporter)
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s connection error: %w", tcp.String(), err))
|
||||
backoff = tcp.sleep(backoff)
|
||||
continue
|
||||
}
|
||||
|
||||
err = conn.SetWriteDeadline(time.Now().Add(time.Second * WriteTimeoutSecs))
|
||||
if err != nil {
|
||||
reporter(fmt.Errorf("log target %s set write deadline error: %w", tcp.String(), err))
|
||||
}
|
||||
|
||||
count, err := conn.Write(p)
|
||||
if err == nil {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
reporter(fmt.Errorf("log target %s write error: %w", tcp.String(), err))
|
||||
|
||||
_ = tcp.close()
|
||||
|
||||
backoff = tcp.sleep(backoff)
|
||||
try++
|
||||
}
|
||||
}
|
||||
|
||||
// monitor continuously tries to read from the connection to detect socket close.
|
||||
// This is needed because TCP target uses a write only socket and Linux systems
|
||||
// take a long time to detect a loss of connectivity on a socket when only writing;
|
||||
// the writes simply fail without an error returned.
|
||||
func monitor(conn net.Conn, done <-chan struct{}) {
|
||||
buf := make([]byte, 1)
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-time.After(1 * time.Second):
|
||||
}
|
||||
|
||||
err := conn.SetReadDeadline(time.Now().Add(time.Second * 30))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = conn.Read(buf)
|
||||
|
||||
if errt, ok := err.(net.Error); ok && errt.Timeout() {
|
||||
// read timeout is expected, keep looping.
|
||||
continue
|
||||
}
|
||||
|
||||
// Any other error closes the connection, forcing a reconnect.
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of this target.
|
||||
func (tcp *Tcp) String() string {
|
||||
return fmt.Sprintf("TcpTarget[%s:%d]", tcp.options.IP, tcp.options.Port)
|
||||
}
|
||||
|
||||
func (tcp *Tcp) sleep(backoff int64) int64 {
|
||||
select {
|
||||
case <-tcp.shutdown:
|
||||
case <-time.After(time.Millisecond * time.Duration(backoff)):
|
||||
}
|
||||
|
||||
nextBackoff := backoff + (backoff >> 1)
|
||||
if nextBackoff > MaxRetryBackoffMillis {
|
||||
nextBackoff = MaxRetryBackoffMillis
|
||||
}
|
||||
return nextBackoff
|
||||
}
|
||||
43
vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
сгенерированный
поставляемый
Обычный файл
43
vendor/github.com/mattermost/logr/v2/targets/test-tls-client-cert.pem
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,43 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjzCCAnegAwIBAgIRAPYfRSwdzKopBKxYxKqslJUwDQYJKoZIhvcNAQELBQAw
|
||||
JzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0xOTAz
|
||||
MjIwMDE0MTVaFw0yMjAzMDYwMDE0MTVaMDsxOTA3BgNVBAMTME1hdHRlcm1vc3Qs
|
||||
IEluYy4gSW50ZXJuYWwgSW50ZXJtZWRpYXRlIEF1dGhvcml0eTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMjliRdmvnNL4u/Jr/M2dPwQmTJXEBY/Vq9Q
|
||||
vAU52X3tRMCPxcaFz+x6ftuvdO2NdohXGAmtx9QU5LZcvFeTDpoVEBo9A+4jtLvD
|
||||
DZYaTNLpJmoSoJHaDbdWX+OAOqyDiWS741LuiMKWHhew9QOisat2ZINPxjmAd9wE
|
||||
xthTMgzsv7MUqnMer8U5OGQ0Qy7wAmNRc+2K3qPwkxe2RUvcte50DUFNgxEginsh
|
||||
vrkOXR383vUCZfu72qu8oggjiQpyTllu5je2Ap6JLjYLkEMiMqrYADuWor/ZHwa6
|
||||
WrFqVETxWfAV5u9Eh0wZM/KKYwRQuw9y+Nans77FmUl1tVWWNN8CAwEAAaOBoTCB
|
||||
njAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBQY4Uqswyr2hO/HetZt2RDxJdTIPjBi
|
||||
BgNVHSMEWzBZgBRFZXVg2Z5tNIsWeWjBLEy2yzKbMKErpCkwJzElMCMGA1UEAwwc
|
||||
TWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQYIUEifGUOM+bIFZo1tkjZB5YGBr
|
||||
0xEwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQAEdexL30Q0zBHmPAH8
|
||||
LhdK7dbzW1CmILbxRZlKAwRN+hKRXiMW3MHIkhNuoV9Aev602Q+ja4lWsRi/ktOL
|
||||
ni1FWx5gSScgdG8JGj47dOmoT3vXKX7+umiv4rQLPDl9/DKMuv204OYJq6VT+uNU
|
||||
6C6kL157jGJEO76H4fMZ8oYsD7Sq0zjiNKtuCYii0ngH3j3gB1jACLqRgveU7MdT
|
||||
pqOV2KfY31+h8VBtkUvljNztQ9xNY8Fjmt0SMf7E3FaUcaar3ZCr70G5aU3dKbe7
|
||||
47vGOBa5tCqw4YK0jgDKid3IJQul9a3J1mSsH8Wy3to9cAV4KGZBQLnzCX15a/+v
|
||||
3yVh
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfjCCAmagAwIBAgIUEifGUOM+bIFZo1tkjZB5YGBr0xEwDQYJKoZIhvcNAQEL
|
||||
BQAwJzElMCMGA1UEAwwcTWF0dGVybW9zdCwgSW5jLiBJbnRlcm5hbCBDQTAeFw0x
|
||||
OTAzMjEyMTI4NDNaFw0yOTAzMTgyMTI4NDNaMCcxJTAjBgNVBAMMHE1hdHRlcm1v
|
||||
c3QsIEluYy4gSW50ZXJuYWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQDH0Xq5rMBGpKOVWTpb5MnaJIWFP/vOtvEk+7hVrfOfe1/5x0Kk3UgAHj85
|
||||
otaEZD1Lhn/JLkEqCiE/UXMJFwJDlNcO4CkdKBSpYX4bKAqy5q/X3QwioMSNpJG1
|
||||
+YYrNGBH0sgKcKjyCaLhmqYLD0xZDVOmWIYBU9jUPyXw5U0tnsVrTqGMxVkm1xCY
|
||||
krCWN1ZoUrLvL0MCZc5qpxoPTopr9UO9cqSBSuy6BVWVuEWBZhpqHt+ul8VxhzzY
|
||||
q1k4l7r2qw+/wm1iJBedTeBVeWNag8JaVfLgu+/W7oJVlPO32Po7pnvHp8iJ3b4K
|
||||
zXyVHaTX4S6Em+6LV8855TYrShzlAgMBAAGjgaEwgZ4wHQYDVR0OBBYEFEVldWDZ
|
||||
nm00ixZ5aMEsTLbLMpswMGIGA1UdIwRbMFmAFEVldWDZnm00ixZ5aMEsTLbLMpsw
|
||||
oSukKTAnMSUwIwYDVQQDDBxNYXR0ZXJtb3N0LCBJbmMuIEludGVybmFsIENBghQS
|
||||
J8ZQ4z5sgVmjW2SNkHlgYGvTETAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjAN
|
||||
BgkqhkiG9w0BAQsFAAOCAQEAPiCWFmopyAkY2T3Zyo4yaRPhX1+VOTMKJtY6EUhq
|
||||
/GHz6kzEyvCUBf0N892cibGxekrEoItY9NqO6RQRfowg+Gn5kc13z4NyL2W8/eoT
|
||||
Xy0ZvfaQbU++fQ6pVtWtMblDMU9xiYd7/MDvJpO328l1Vhcdp8kEi+lCvpy0sCRc
|
||||
PxzPhbgCMAbZEGx+4TMQd4SZKzlRxW/2fflpReh6v1Dv0VDUSYQWwsUnaLpdKHfh
|
||||
a5k0vuySYcszE4YKlY0zakeFlJfp7fBp1xTwcdW8aTfw15EicPMwTc6xxA4JJUJx
|
||||
cddu817n1nayK5u6r9Qh1oIVkr0nC9YELMMy4dpPgJ88SA==
|
||||
-----END CERTIFICATE-----
|
||||
33
vendor/github.com/mattermost/logr/v2/targets/utils.go
сгенерированный
поставляемый
Обычный файл
33
vendor/github.com/mattermost/logr/v2/targets/utils.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// GetCertPool returns a x509.CertPool containing the cert(s)
|
||||
// from `cert`, which can be a path to a .pem or .crt file,
|
||||
// or a base64 encoded cert.
|
||||
func GetCertPool(cert string) (*x509.CertPool, error) {
|
||||
if cert == "" {
|
||||
return nil, errors.New("no cert provided")
|
||||
}
|
||||
|
||||
// first treat as a file and try to read.
|
||||
serverCert, err := ioutil.ReadFile(cert)
|
||||
if err != nil {
|
||||
// maybe it's a base64 encoded cert
|
||||
serverCert, err = base64.StdEncoding.DecodeString(cert)
|
||||
if err != nil {
|
||||
return nil, errors.New("cert cannot be read")
|
||||
}
|
||||
}
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM(serverCert); ok {
|
||||
return pool, nil
|
||||
}
|
||||
return nil, errors.New("cannot parse cert")
|
||||
}
|
||||
38
vendor/github.com/mattermost/logr/v2/targets/writer.go
сгенерированный
поставляемый
Обычный файл
38
vendor/github.com/mattermost/logr/v2/targets/writer.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,38 @@
|
||||
package targets
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mattermost/logr/v2"
|
||||
)
|
||||
|
||||
// Writer outputs log records to any `io.Writer`.
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// NewWriterTarget creates a target capable of outputting log records to an io.Writer.
|
||||
func NewWriterTarget(out io.Writer) *Writer {
|
||||
if out == nil {
|
||||
out = ioutil.Discard
|
||||
}
|
||||
w := &Writer{out: out}
|
||||
return w
|
||||
}
|
||||
|
||||
// Init is called once to initialize the target.
|
||||
func (w *Writer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write outputs bytes to this file target.
|
||||
func (w *Writer) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||
return w.out.Write(p)
|
||||
}
|
||||
|
||||
// Shutdown is called once to free/close any resources.
|
||||
// Target queue is already drained when this is called.
|
||||
func (w *Writer) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
0
vendor/github.com/mattermost/logr/timeout.go → vendor/github.com/mattermost/logr/v2/timeout.go
сгенерированный
поставляемый
0
vendor/github.com/mattermost/logr/timeout.go → vendor/github.com/mattermost/logr/v2/timeout.go
сгенерированный
поставляемый
12
vendor/github.com/wiggin77/cfg/.gitignore
сгенерированный
поставляемый
12
vendor/github.com/wiggin77/cfg/.gitignore
сгенерированный
поставляемый
@@ -1,12 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
5
vendor/github.com/wiggin77/cfg/.travis.yml
сгенерированный
поставляемый
5
vendor/github.com/wiggin77/cfg/.travis.yml
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
before_script:
|
||||
- go vet ./...
|
||||
|
||||
21
vendor/github.com/wiggin77/cfg/LICENSE
сгенерированный
поставляемый
21
vendor/github.com/wiggin77/cfg/LICENSE
сгенерированный
поставляемый
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 wiggin77
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
43
vendor/github.com/wiggin77/cfg/README.md
сгенерированный
поставляемый
43
vendor/github.com/wiggin77/cfg/README.md
сгенерированный
поставляемый
@@ -1,43 +0,0 @@
|
||||
# cfg
|
||||
|
||||
[](https://godoc.org/github.com/wiggin77/cfg)
|
||||
[](https://travis-ci.org/wiggin77/cfg)
|
||||
|
||||
Go package for app configuration. Supports chained configuration sources for multiple levels of defaults.
|
||||
Includes APIs for loading Linux style configuration files (name/value pairs) or INI files, map based properties,
|
||||
or easily create new configuration sources (e.g. load from database).
|
||||
|
||||
Supports monitoring configuration sources for changes, hot loading properties, and notifying listeners of changes.
|
||||
|
||||
## Usage
|
||||
|
||||
```Go
|
||||
config := &cfg.Config{}
|
||||
defer config.Shutdown() // stops monitoring
|
||||
|
||||
// load file via filespec string, os.File
|
||||
src, err := Config.NewSrcFileFromFilespec("./myfile.conf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add src to top of chain, meaning first searched
|
||||
cfg.PrependSource(src)
|
||||
|
||||
// fetch prop 'retries', default to 3 if not found
|
||||
val := config.Int("retries", 3)
|
||||
```
|
||||
|
||||
See [example](./example_test.go) for more complete example, including listening for configuration changes.
|
||||
|
||||
Config API parses the following data types:
|
||||
|
||||
| type | method | example property values |
|
||||
| ------- | ------ | -------- |
|
||||
| string | Config.String | test, "" |
|
||||
| int | Config.Int | -1, 77, 0 |
|
||||
| int64 | Config.Int64 | -9223372036854775, 372036854775808 |
|
||||
| float64 | Config.Float64 | -77.3456, 95642331.1 |
|
||||
| bool | Config.Bool | T,t,true,True,1,0,False,false,f,F |
|
||||
| time.Duration | Config.Duration | "10ms", "2 hours", "5 min" * |
|
||||
|
||||
\* Units of measure supported: ms, sec, min, hour, day, week, year.
|
||||
366
vendor/github.com/wiggin77/cfg/config.go
сгенерированный
поставляемый
366
vendor/github.com/wiggin77/cfg/config.go
сгенерированный
поставляемый
@@ -1,366 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg/timeconv"
|
||||
)
|
||||
|
||||
// ErrNotFound returned when an operation is attempted on a
|
||||
// resource that doesn't exist, such as fetching a non-existing
|
||||
// property name.
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
type sourceEntry struct {
|
||||
src Source
|
||||
props map[string]string
|
||||
}
|
||||
|
||||
// Config provides methods for retrieving property values from one or more
|
||||
// configuration sources.
|
||||
type Config struct {
|
||||
mutexSrc sync.RWMutex
|
||||
mutexListeners sync.RWMutex
|
||||
srcs []*sourceEntry
|
||||
chgListeners []ChangedListener
|
||||
shutdown chan interface{}
|
||||
wantPanicOnError bool
|
||||
}
|
||||
|
||||
// PrependSource inserts one or more `Sources` at the beginning of
|
||||
// the list of sources such that the first source will be the
|
||||
// source checked first when resolving a property value.
|
||||
func (config *Config) PrependSource(srcs ...Source) {
|
||||
arr := config.wrapSources(srcs...)
|
||||
|
||||
config.mutexSrc.Lock()
|
||||
if config.shutdown == nil {
|
||||
config.shutdown = make(chan interface{})
|
||||
}
|
||||
config.srcs = append(arr, config.srcs...)
|
||||
config.mutexSrc.Unlock()
|
||||
|
||||
for _, se := range arr {
|
||||
if _, ok := se.src.(SourceMonitored); ok {
|
||||
config.monitor(se)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AppendSource appends one or more `Sources` at the end of
|
||||
// the list of sources such that the last source will be the
|
||||
// source checked last when resolving a property value.
|
||||
func (config *Config) AppendSource(srcs ...Source) {
|
||||
arr := config.wrapSources(srcs...)
|
||||
|
||||
config.mutexSrc.Lock()
|
||||
if config.shutdown == nil {
|
||||
config.shutdown = make(chan interface{})
|
||||
}
|
||||
config.srcs = append(config.srcs, arr...)
|
||||
config.mutexSrc.Unlock()
|
||||
|
||||
for _, se := range arr {
|
||||
if _, ok := se.src.(SourceMonitored); ok {
|
||||
config.monitor(se)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wrapSources wraps one or more Source's and returns
|
||||
// them as an array of `sourceEntry`.
|
||||
func (config *Config) wrapSources(srcs ...Source) []*sourceEntry {
|
||||
arr := make([]*sourceEntry, 0, len(srcs))
|
||||
for _, src := range srcs {
|
||||
se := &sourceEntry{src: src}
|
||||
config.reloadProps(se)
|
||||
arr = append(arr, se)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// SetWantPanicOnError sets the flag determining if Config
|
||||
// should panic when `GetProps` or `GetLastModified` errors
|
||||
// for a `Source`.
|
||||
func (config *Config) SetWantPanicOnError(b bool) {
|
||||
config.mutexSrc.Lock()
|
||||
config.wantPanicOnError = b
|
||||
config.mutexSrc.Unlock()
|
||||
}
|
||||
|
||||
// ShouldPanicOnError gets the flag determining if Config
|
||||
// should panic when `GetProps` or `GetLastModified` errors
|
||||
// for a `Source`.
|
||||
func (config *Config) ShouldPanicOnError() (b bool) {
|
||||
config.mutexSrc.RLock()
|
||||
b = config.wantPanicOnError
|
||||
config.mutexSrc.RUnlock()
|
||||
return b
|
||||
}
|
||||
|
||||
// getProp returns the value of a named property.
|
||||
// Each `Source` is checked, in the order created by adding via
|
||||
// `AppendSource` and `PrependSource`, until a value for the
|
||||
// property is found.
|
||||
func (config *Config) getProp(name string) (val string, ok bool) {
|
||||
config.mutexSrc.RLock()
|
||||
defer config.mutexSrc.RUnlock()
|
||||
|
||||
var s string
|
||||
for _, se := range config.srcs {
|
||||
if se.props != nil {
|
||||
if s, ok = se.props[name]; ok {
|
||||
val = strings.TrimSpace(s)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// String returns the value of the named prop as a string.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
func (config *Config) String(name string, def string) (val string, err error) {
|
||||
if v, ok := config.getProp(name); ok {
|
||||
val = v
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
err = ErrNotFound
|
||||
val = def
|
||||
return
|
||||
}
|
||||
|
||||
// Int returns the value of the named prop as an `int`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Int(name string, def int) (val int, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
var i int64
|
||||
if i, err = strconv.ParseInt(s, 10, 32); err == nil {
|
||||
val = int(i)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Int64 returns the value of the named prop as an `int64`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Int64(name string, def int64) (val int64, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
val, err = strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Float64 returns the value of the named prop as a `float64`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Float64(name string, def float64) (val float64, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
val, err = strconv.ParseFloat(s, 64)
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Bool returns the value of the named prop as a `bool`.
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// Supports (t, true, 1, y, yes) for true, and (f, false, 0, n, no) for false,
|
||||
// all case-insensitive.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Bool(name string, def bool) (val bool, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "t", "true", "1", "y", "yes":
|
||||
val = true
|
||||
case "f", "false", "0", "n", "no":
|
||||
val = false
|
||||
default:
|
||||
err = errors.New("invalid syntax")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Duration returns the value of the named prop as a `time.Duration`, representing
|
||||
// a span of time.
|
||||
//
|
||||
// Units of measure are supported: ms, sec, min, hour, day, week, year.
|
||||
// See config.UnitsToMillis for a complete list of units supported.
|
||||
//
|
||||
// If the property is not found then the supplied default `def`
|
||||
// and `ErrNotFound` are returned.
|
||||
//
|
||||
// See config.String
|
||||
func (config *Config) Duration(name string, def time.Duration) (val time.Duration, err error) {
|
||||
var s string
|
||||
if s, err = config.String(name, ""); err == nil {
|
||||
var ms int64
|
||||
ms, err = timeconv.ParseMilliseconds(s)
|
||||
val = time.Duration(ms) * time.Millisecond
|
||||
}
|
||||
if err != nil {
|
||||
val = def
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddChangedListener adds a listener that will receive notifications
|
||||
// whenever one or more property values change within the config.
|
||||
func (config *Config) AddChangedListener(l ChangedListener) {
|
||||
config.mutexListeners.Lock()
|
||||
defer config.mutexListeners.Unlock()
|
||||
|
||||
config.chgListeners = append(config.chgListeners, l)
|
||||
}
|
||||
|
||||
// RemoveChangedListener removes all instances of a ChangedListener.
|
||||
// Returns `ErrNotFound` if the listener was not present.
|
||||
func (config *Config) RemoveChangedListener(l ChangedListener) error {
|
||||
config.mutexListeners.Lock()
|
||||
defer config.mutexListeners.Unlock()
|
||||
|
||||
dest := make([]ChangedListener, 0, len(config.chgListeners))
|
||||
err := ErrNotFound
|
||||
|
||||
// Remove all instances of the listener by
|
||||
// copying list while filtering.
|
||||
for _, s := range config.chgListeners {
|
||||
if s != l {
|
||||
dest = append(dest, s)
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
config.chgListeners = dest
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown can be called to stop monitoring of all config sources.
|
||||
func (config *Config) Shutdown() {
|
||||
config.mutexSrc.RLock()
|
||||
defer config.mutexSrc.RUnlock()
|
||||
if config.shutdown != nil {
|
||||
close(config.shutdown)
|
||||
}
|
||||
}
|
||||
|
||||
// onSourceChanged is called whenever one or more properties of a
|
||||
// config source has changed.
|
||||
func (config *Config) onSourceChanged(src SourceMonitored) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
fmt.Println(p)
|
||||
}
|
||||
}()
|
||||
config.mutexListeners.RLock()
|
||||
defer config.mutexListeners.RUnlock()
|
||||
for _, l := range config.chgListeners {
|
||||
l.ConfigChanged(config, src)
|
||||
}
|
||||
}
|
||||
|
||||
// monitor periodically checks a config source for changes.
|
||||
func (config *Config) monitor(se *sourceEntry) {
|
||||
go func(se *sourceEntry, shutdown <-chan interface{}) {
|
||||
var src SourceMonitored
|
||||
var ok bool
|
||||
if src, ok = se.src.(SourceMonitored); !ok {
|
||||
return
|
||||
}
|
||||
paused := false
|
||||
last := time.Time{}
|
||||
freq := src.GetMonitorFreq()
|
||||
if freq <= 0 {
|
||||
paused = true
|
||||
freq = 10
|
||||
last, _ = src.GetLastModified()
|
||||
}
|
||||
timer := time.NewTimer(freq)
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
if !paused {
|
||||
if latest, err := src.GetLastModified(); err != nil {
|
||||
if config.ShouldPanicOnError() {
|
||||
panic(fmt.Sprintf("error <%v> getting last modified for %v", err, src))
|
||||
}
|
||||
} else {
|
||||
if last.Before(latest) {
|
||||
last = latest
|
||||
config.reloadProps(se)
|
||||
// TODO: calc diff and provide detailed changes
|
||||
config.onSourceChanged(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
freq = src.GetMonitorFreq()
|
||||
if freq <= 0 {
|
||||
paused = true
|
||||
freq = 10
|
||||
} else {
|
||||
paused = false
|
||||
}
|
||||
timer.Reset(freq)
|
||||
case <-shutdown:
|
||||
// stop the timer and exit
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}(se, config.shutdown)
|
||||
}
|
||||
|
||||
// reloadProps causes a Source to reload its properties.
|
||||
func (config *Config) reloadProps(se *sourceEntry) {
|
||||
config.mutexSrc.Lock()
|
||||
defer config.mutexSrc.Unlock()
|
||||
|
||||
m, err := se.src.GetProps()
|
||||
if err != nil {
|
||||
if config.wantPanicOnError {
|
||||
panic(fmt.Sprintf("GetProps error for %v", se.src))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
se.props = make(map[string]string)
|
||||
for k, v := range m {
|
||||
se.props[k] = v
|
||||
}
|
||||
}
|
||||
5
vendor/github.com/wiggin77/cfg/go.mod
сгенерированный
поставляемый
5
vendor/github.com/wiggin77/cfg/go.mod
сгенерированный
поставляемый
@@ -1,5 +0,0 @@
|
||||
module github.com/wiggin77/cfg
|
||||
|
||||
go 1.12
|
||||
|
||||
require github.com/wiggin77/merror v1.0.2
|
||||
2
vendor/github.com/wiggin77/cfg/go.sum
сгенерированный
поставляемый
2
vendor/github.com/wiggin77/cfg/go.sum
сгенерированный
поставляемый
@@ -1,2 +0,0 @@
|
||||
github.com/wiggin77/merror v1.0.2 h1:V0nH9eFp64ASyaXC+pB5WpvBoCg7NUwvaCSKdzlcHqw=
|
||||
github.com/wiggin77/merror v1.0.2/go.mod h1:uQTcIU0Z6jRK4OwqganPYerzQxSFJ4GSHM3aurxxQpg=
|
||||
167
vendor/github.com/wiggin77/cfg/ini/ini.go
сгенерированный
поставляемый
167
vendor/github.com/wiggin77/cfg/ini/ini.go
сгенерированный
поставляемый
@@ -1,167 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ini provides parsing and querying of INI format or simple name/value pairs
|
||||
// such as a simple config file.
|
||||
// A name/value pair format is just an INI with no sections, and properties can
|
||||
// be queried using an empty section name.
|
||||
type Ini struct {
|
||||
mutex sync.RWMutex
|
||||
m map[string]*Section
|
||||
lm time.Time
|
||||
}
|
||||
|
||||
// LoadFromFilespec loads an INI file from string containing path and filename.
|
||||
func (ini *Ini) LoadFromFilespec(filespec string) error {
|
||||
f, err := os.Open(filespec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ini.LoadFromFile(f)
|
||||
}
|
||||
|
||||
// LoadFromFile loads an INI file from `os.File`.
|
||||
func (ini *Ini) LoadFromFile(file *os.File) error {
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lm := fi.ModTime()
|
||||
|
||||
if err := ini.LoadFromReader(file); err != nil {
|
||||
return err
|
||||
}
|
||||
ini.lm = lm
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromReader loads an INI file from an `io.Reader`.
|
||||
func (ini *Ini) LoadFromReader(reader io.Reader) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ini.LoadFromString(string(data))
|
||||
}
|
||||
|
||||
// LoadFromString parses an INI from a string .
|
||||
func (ini *Ini) LoadFromString(s string) error {
|
||||
m, err := getSections(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ini.mutex.Lock()
|
||||
ini.m = m
|
||||
ini.lm = time.Now()
|
||||
ini.mutex.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetLastModified returns the last modified timestamp of the
|
||||
// INI contents.
|
||||
func (ini *Ini) GetLastModified() time.Time {
|
||||
return ini.lm
|
||||
}
|
||||
|
||||
// GetSectionNames returns the names of all sections in this INI.
|
||||
// Note, the returned section names are a snapshot in time, meaning
|
||||
// other goroutines may change the contents of this INI as soon as
|
||||
// the method returns.
|
||||
func (ini *Ini) GetSectionNames() []string {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
arr := make([]string, 0, len(ini.m))
|
||||
for key := range ini.m {
|
||||
arr = append(arr, key)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// GetKeys returns the names of all keys in the specified section.
|
||||
// Note, the returned key names are a snapshot in time, meaning other
|
||||
// goroutines may change the contents of this INI as soon as the
|
||||
// method returns.
|
||||
func (ini *Ini) GetKeys(sectionName string) ([]string, error) {
|
||||
sec, err := ini.getSection(sectionName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sec.getKeys(), nil
|
||||
}
|
||||
|
||||
// getSection returns the named section.
|
||||
func (ini *Ini) getSection(sectionName string) (*Section, error) {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
sec, ok := ini.m[sectionName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("section '%s' not found", sectionName)
|
||||
}
|
||||
return sec, nil
|
||||
}
|
||||
|
||||
// GetFlattenedKeys returns all section names plus keys as one
|
||||
// flattened array.
|
||||
func (ini *Ini) GetFlattenedKeys() []string {
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
arr := make([]string, 0, len(ini.m)*2)
|
||||
for _, section := range ini.m {
|
||||
keys := section.getKeys()
|
||||
for _, key := range keys {
|
||||
name := section.GetName()
|
||||
if name != "" {
|
||||
key = name + "." + key
|
||||
}
|
||||
arr = append(arr, key)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// GetProp returns the value of the specified key in the named section.
|
||||
func (ini *Ini) GetProp(section string, key string) (val string, ok bool) {
|
||||
sec, err := ini.getSection(section)
|
||||
if err != nil {
|
||||
return val, false
|
||||
}
|
||||
return sec.GetProp(key)
|
||||
}
|
||||
|
||||
// ToMap returns a flattened map of the section name plus keys mapped
|
||||
// to values.
|
||||
func (ini *Ini) ToMap() map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
ini.mutex.RLock()
|
||||
defer ini.mutex.RUnlock()
|
||||
|
||||
for _, section := range ini.m {
|
||||
for _, key := range section.getKeys() {
|
||||
val, ok := section.GetProp(key)
|
||||
if ok {
|
||||
name := section.GetName()
|
||||
var mapkey string
|
||||
if name != "" {
|
||||
mapkey = name + "." + key
|
||||
} else {
|
||||
mapkey = key
|
||||
}
|
||||
m[mapkey] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
142
vendor/github.com/wiggin77/cfg/ini/parser.go
сгенерированный
поставляемый
142
vendor/github.com/wiggin77/cfg/ini/parser.go
сгенерированный
поставляемый
@@ -1,142 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/wiggin77/merror"
|
||||
)
|
||||
|
||||
// LF is linefeed
|
||||
const LF byte = 0x0A
|
||||
|
||||
// CR is carriage return
|
||||
const CR byte = 0x0D
|
||||
|
||||
// getSections parses an INI formatted string, or string containing just name/value pairs,
|
||||
// returns map of `Section`'s.
|
||||
//
|
||||
// Any name/value pairs appearing before a section name are added to the section named
|
||||
// with an empty string (""). Also true for Linux-style config files where all props
|
||||
// are outside a named section.
|
||||
//
|
||||
// Any errors encountered are aggregated and returned, along with the partially parsed
|
||||
// sections.
|
||||
func getSections(str string) (map[string]*Section, error) {
|
||||
merr := merror.New()
|
||||
mapSections := make(map[string]*Section)
|
||||
lines := buildLineArray(str)
|
||||
section := newSection("")
|
||||
|
||||
for _, line := range lines {
|
||||
name, ok := parseSection(line)
|
||||
if ok {
|
||||
// A section name encountered. Stop processing the current one.
|
||||
// Don't add the current section to the map if the section name is blank
|
||||
// and the prop map is empty.
|
||||
nameCurr := section.GetName()
|
||||
if nameCurr != "" || section.hasKeys() {
|
||||
mapSections[nameCurr] = section
|
||||
}
|
||||
// Start processing a new section.
|
||||
section = newSection(name)
|
||||
} else {
|
||||
// Parse the property and add to the current section, or ignore if comment.
|
||||
if k, v, comment, err := parseProp(line); !comment && err == nil {
|
||||
section.setProp(k, v)
|
||||
} else if err != nil {
|
||||
merr.Append(err) // aggregate errors
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// If the current section is not empty, add it.
|
||||
if section.hasKeys() {
|
||||
mapSections[section.GetName()] = section
|
||||
}
|
||||
return mapSections, merr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// buildLineArray parses the given string buffer and creates a list of strings,
|
||||
// one for each line in the string buffer.
|
||||
//
|
||||
// A line is considered to be terminated by any one of a line feed ('\n'),
|
||||
// a carriage return ('\r'), or a carriage return followed immediately by a
|
||||
// linefeed.
|
||||
//
|
||||
// Lines prefixed with ';' or '#' are considered comments and skipped.
|
||||
func buildLineArray(str string) []string {
|
||||
arr := make([]string, 0, 10)
|
||||
str = str + "\n"
|
||||
|
||||
iLen := len(str)
|
||||
iPos, iBegin := 0, 0
|
||||
var ch byte
|
||||
|
||||
for iPos < iLen {
|
||||
ch = str[iPos]
|
||||
if ch == LF || ch == CR {
|
||||
sub := str[iBegin:iPos]
|
||||
sub = strings.TrimSpace(sub)
|
||||
if sub != "" && !strings.HasPrefix(sub, ";") && !strings.HasPrefix(sub, "#") {
|
||||
arr = append(arr, sub)
|
||||
}
|
||||
iPos++
|
||||
if ch == CR && iPos < iLen && str[iPos] == LF {
|
||||
iPos++
|
||||
}
|
||||
iBegin = iPos
|
||||
} else {
|
||||
iPos++
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// parseSection parses the specified string for a section name enclosed in square brackets.
|
||||
// Returns the section name found, or `ok=false` if `str` is not a section header.
|
||||
func parseSection(str string) (name string, ok bool) {
|
||||
str = strings.TrimSpace(str)
|
||||
if !strings.HasPrefix(str, "[") {
|
||||
return "", false
|
||||
}
|
||||
iCloser := strings.Index(str, "]")
|
||||
if iCloser == -1 {
|
||||
return "", false
|
||||
}
|
||||
return strings.TrimSpace(str[1:iCloser]), true
|
||||
}
|
||||
|
||||
// parseProp parses the specified string and extracts a key/value pair.
|
||||
//
|
||||
// If the string is a comment (prefixed with ';' or '#') then `comment=true`
|
||||
// and key will be empty.
|
||||
func parseProp(str string) (key string, val string, comment bool, err error) {
|
||||
iLen := len(str)
|
||||
iEqPos := strings.Index(str, "=")
|
||||
if iEqPos == -1 {
|
||||
return "", "", false, fmt.Errorf("not a key/value pair:'%s'", str)
|
||||
}
|
||||
|
||||
key = str[0:iEqPos]
|
||||
key = strings.TrimSpace(key)
|
||||
if iEqPos+1 < iLen {
|
||||
val = str[iEqPos+1:]
|
||||
val = strings.TrimSpace(val)
|
||||
}
|
||||
|
||||
// Check that the key has at least 1 char.
|
||||
if key == "" {
|
||||
return "", "", false, fmt.Errorf("key is empty for '%s'", str)
|
||||
}
|
||||
|
||||
// Check if this line is a comment that just happens
|
||||
// to have an equals sign in it. Not an error, but not a
|
||||
// useable line either.
|
||||
if strings.HasPrefix(key, ";") || strings.HasPrefix(key, "#") {
|
||||
key = ""
|
||||
val = ""
|
||||
comment = true
|
||||
}
|
||||
return key, val, comment, err
|
||||
}
|
||||
109
vendor/github.com/wiggin77/cfg/ini/section.go
сгенерированный
поставляемый
109
vendor/github.com/wiggin77/cfg/ini/section.go
сгенерированный
поставляемый
@@ -1,109 +0,0 @@
|
||||
package ini
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Section represents a section in an INI file. The section has a name, which is
|
||||
// enclosed in square brackets in the file. The section also has an array of
|
||||
// key/value pairs.
|
||||
type Section struct {
|
||||
name string
|
||||
props map[string]string
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func newSection(name string) *Section {
|
||||
sec := &Section{}
|
||||
sec.name = name
|
||||
sec.props = make(map[string]string)
|
||||
return sec
|
||||
}
|
||||
|
||||
// addLines addes an array of strings containing name/value pairs
|
||||
// of the format `key=value`.
|
||||
//func addLines(lines []string) {
|
||||
// TODO
|
||||
//}
|
||||
|
||||
// GetName returns the name of the section.
|
||||
func (sec *Section) GetName() (name string) {
|
||||
sec.mtx.RLock()
|
||||
name = sec.name
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetProp returns the value associated with the given key, or
|
||||
// `ok=false` if key does not exist.
|
||||
func (sec *Section) GetProp(key string) (val string, ok bool) {
|
||||
sec.mtx.RLock()
|
||||
val, ok = sec.props[key]
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetProp sets the value associated with the given key.
|
||||
func (sec *Section) setProp(key string, val string) {
|
||||
sec.mtx.Lock()
|
||||
sec.props[key] = val
|
||||
sec.mtx.Unlock()
|
||||
}
|
||||
|
||||
// hasKeys returns true if there are one or more properties in
|
||||
// this section.
|
||||
func (sec *Section) hasKeys() (b bool) {
|
||||
sec.mtx.RLock()
|
||||
b = len(sec.props) > 0
|
||||
sec.mtx.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// getKeys returns an array containing all keys in this section.
|
||||
func (sec *Section) getKeys() []string {
|
||||
sec.mtx.RLock()
|
||||
defer sec.mtx.RUnlock()
|
||||
|
||||
arr := make([]string, len(sec.props))
|
||||
idx := 0
|
||||
for k := range sec.props {
|
||||
arr[idx] = k
|
||||
idx++
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// combine the given section with this one.
|
||||
func (sec *Section) combine(sec2 *Section) {
|
||||
sec.mtx.Lock()
|
||||
sec2.mtx.RLock()
|
||||
defer sec.mtx.Unlock()
|
||||
defer sec2.mtx.RUnlock()
|
||||
|
||||
for k, v := range sec2.props {
|
||||
sec.props[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of this section.
|
||||
func (sec *Section) String() string {
|
||||
return fmt.Sprintf("[%s]\n%s", sec.GetName(), sec.StringPropsOnly())
|
||||
}
|
||||
|
||||
// StringPropsOnly returns a string representation of this section
|
||||
// without the section header.
|
||||
func (sec *Section) StringPropsOnly() string {
|
||||
sec.mtx.RLock()
|
||||
defer sec.mtx.RUnlock()
|
||||
sb := &strings.Builder{}
|
||||
|
||||
for k, v := range sec.props {
|
||||
sb.WriteString(k)
|
||||
sb.WriteString("=")
|
||||
sb.WriteString(v)
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
11
vendor/github.com/wiggin77/cfg/listener.go
сгенерированный
поставляемый
11
vendor/github.com/wiggin77/cfg/listener.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package cfg
|
||||
|
||||
// ChangedListener interface is for receiving notifications
|
||||
// when one or more properties within monitored config sources
|
||||
// (SourceMonitored) have changed values.
|
||||
type ChangedListener interface {
|
||||
|
||||
// Changed is called when one or more properties in a `SourceMonitored` has a
|
||||
// changed value.
|
||||
ConfigChanged(cfg *Config, src SourceMonitored)
|
||||
}
|
||||
11
vendor/github.com/wiggin77/cfg/nocopy.go
сгенерированный
поставляемый
11
vendor/github.com/wiggin77/cfg/nocopy.go
сгенерированный
поставляемый
@@ -1,11 +0,0 @@
|
||||
package cfg
|
||||
|
||||
// noCopy may be embedded into structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://golang.org/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
58
vendor/github.com/wiggin77/cfg/source.go
сгенерированный
поставляемый
58
vendor/github.com/wiggin77/cfg/source.go
сгенерированный
поставляемый
@@ -1,58 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Source is the interface required for any source of name/value pairs.
|
||||
type Source interface {
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
GetProps() (map[string]string, error)
|
||||
}
|
||||
|
||||
// SourceMonitored is the interface required for any config source that is
|
||||
// monitored for changes.
|
||||
type SourceMonitored interface {
|
||||
Source
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source. If a source does not support
|
||||
// modifying properties at runtime then the zero value for `Time`
|
||||
// should be returned to ensure reload events are not generated.
|
||||
GetLastModified() (time.Time, error)
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source.
|
||||
//
|
||||
// Returning zero (or less) will temporarily suspend calls to `GetLastModified`
|
||||
// and `GetMonitorFreq` will be called every 10 seconds until resumed, after which
|
||||
// `GetMontitorFreq` will be called at a frequency roughly equal to the `time.Duration`
|
||||
// returned.
|
||||
GetMonitorFreq() time.Duration
|
||||
}
|
||||
|
||||
// AbstractSourceMonitor can be embedded in a custom `Source` to provide the
|
||||
// basic plumbing for monitor frequency.
|
||||
type AbstractSourceMonitor struct {
|
||||
mutex sync.RWMutex
|
||||
freq time.Duration
|
||||
}
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source.
|
||||
func (asm *AbstractSourceMonitor) GetMonitorFreq() (freq time.Duration) {
|
||||
asm.mutex.RLock()
|
||||
freq = asm.freq
|
||||
asm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetMonitorFreq sets the frequency between checks for changes to this config source.
|
||||
func (asm *AbstractSourceMonitor) SetMonitorFreq(freq time.Duration) {
|
||||
asm.mutex.Lock()
|
||||
asm.freq = freq
|
||||
asm.mutex.Unlock()
|
||||
}
|
||||
63
vendor/github.com/wiggin77/cfg/srcfile.go
сгенерированный
поставляемый
63
vendor/github.com/wiggin77/cfg/srcfile.go
сгенерированный
поставляемый
@@ -1,63 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/wiggin77/cfg/ini"
|
||||
)
|
||||
|
||||
// SrcFile is a configuration `Source` backed by a file containing
|
||||
// name/value pairs or INI format.
|
||||
type SrcFile struct {
|
||||
AbstractSourceMonitor
|
||||
ini ini.Ini
|
||||
file *os.File
|
||||
}
|
||||
|
||||
// NewSrcFileFromFilespec creates a new SrcFile with the specified filespec.
|
||||
func NewSrcFileFromFilespec(filespec string) (*SrcFile, error) {
|
||||
file, err := os.Open(filespec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewSrcFile(file)
|
||||
}
|
||||
|
||||
// NewSrcFile creates a new SrcFile with the specified os.File.
|
||||
func NewSrcFile(file *os.File) (*SrcFile, error) {
|
||||
sf := &SrcFile{}
|
||||
sf.freq = time.Minute
|
||||
sf.file = file
|
||||
if err := sf.ini.LoadFromFile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sf, nil
|
||||
}
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
func (sf *SrcFile) GetProps() (map[string]string, error) {
|
||||
lm, err := sf.GetLastModified()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if we need to reload.
|
||||
if sf.ini.GetLastModified() != lm {
|
||||
if err := sf.ini.LoadFromFile(sf.file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return sf.ini.ToMap(), nil
|
||||
}
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source.
|
||||
func (sf *SrcFile) GetLastModified() (time.Time, error) {
|
||||
fi, err := sf.file.Stat()
|
||||
if err != nil {
|
||||
return time.Now(), err
|
||||
}
|
||||
return fi.ModTime(), nil
|
||||
}
|
||||
78
vendor/github.com/wiggin77/cfg/srcmap.go
сгенерированный
поставляемый
78
vendor/github.com/wiggin77/cfg/srcmap.go
сгенерированный
поставляемый
@@ -1,78 +0,0 @@
|
||||
package cfg
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SrcMap is a configuration `Source` backed by a simple map.
|
||||
type SrcMap struct {
|
||||
AbstractSourceMonitor
|
||||
m map[string]string
|
||||
lm time.Time
|
||||
}
|
||||
|
||||
// NewSrcMap creates an empty `SrcMap`.
|
||||
func NewSrcMap() *SrcMap {
|
||||
sm := &SrcMap{}
|
||||
sm.m = make(map[string]string)
|
||||
sm.lm = time.Now()
|
||||
sm.freq = time.Minute
|
||||
return sm
|
||||
}
|
||||
|
||||
// NewSrcMapFromMap creates a `SrcMap` containing a copy of the
|
||||
// specified map.
|
||||
func NewSrcMapFromMap(mapIn map[string]string) *SrcMap {
|
||||
sm := NewSrcMap()
|
||||
sm.PutAll(mapIn)
|
||||
return sm
|
||||
}
|
||||
|
||||
// Put inserts or updates a value in the `SrcMap`.
|
||||
func (sm *SrcMap) Put(key string, val string) {
|
||||
sm.mutex.Lock()
|
||||
sm.m[key] = val
|
||||
sm.lm = time.Now()
|
||||
sm.mutex.Unlock()
|
||||
}
|
||||
|
||||
// PutAll inserts a copy of `mapIn` into the `SrcMap`
|
||||
func (sm *SrcMap) PutAll(mapIn map[string]string) {
|
||||
sm.mutex.Lock()
|
||||
defer sm.mutex.Unlock()
|
||||
|
||||
for k, v := range mapIn {
|
||||
sm.m[k] = v
|
||||
}
|
||||
sm.lm = time.Now()
|
||||
}
|
||||
|
||||
// GetProps fetches all the properties from a source and returns
|
||||
// them as a map.
|
||||
func (sm *SrcMap) GetProps() (m map[string]string, err error) {
|
||||
sm.mutex.RLock()
|
||||
m = sm.m
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetLastModified returns the time of the latest modification to any
|
||||
// property value within the source. If a source does not support
|
||||
// modifying properties at runtime then the zero value for `Time`
|
||||
// should be returned to ensure reload events are not generated.
|
||||
func (sm *SrcMap) GetLastModified() (last time.Time, err error) {
|
||||
sm.mutex.RLock()
|
||||
last = sm.lm
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// GetMonitorFreq returns the frequency as a `time.Duration` between
|
||||
// checks for changes to this config source. Defaults to 1 minute
|
||||
// unless changed with `SetMonitorFreq`.
|
||||
func (sm *SrcMap) GetMonitorFreq() (freq time.Duration) {
|
||||
sm.mutex.RLock()
|
||||
freq = sm.freq
|
||||
sm.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
108
vendor/github.com/wiggin77/cfg/timeconv/parse.go
сгенерированный
поставляемый
108
vendor/github.com/wiggin77/cfg/timeconv/parse.go
сгенерированный
поставляемый
@@ -1,108 +0,0 @@
|
||||
package timeconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MillisPerSecond is the number of millseconds per second.
|
||||
const MillisPerSecond int64 = 1000
|
||||
|
||||
// MillisPerMinute is the number of millseconds per minute.
|
||||
const MillisPerMinute int64 = MillisPerSecond * 60
|
||||
|
||||
// MillisPerHour is the number of millseconds per hour.
|
||||
const MillisPerHour int64 = MillisPerMinute * 60
|
||||
|
||||
// MillisPerDay is the number of millseconds per day.
|
||||
const MillisPerDay int64 = MillisPerHour * 24
|
||||
|
||||
// MillisPerWeek is the number of millseconds per week.
|
||||
const MillisPerWeek int64 = MillisPerDay * 7
|
||||
|
||||
// MillisPerYear is the approximate number of millseconds per year.
|
||||
const MillisPerYear int64 = MillisPerDay*365 + int64((float64(MillisPerDay) * 0.25))
|
||||
|
||||
// ParseMilliseconds parses a string containing a number plus
|
||||
// a unit of measure for time and returns the number of milliseconds
|
||||
// it represents.
|
||||
//
|
||||
// Example:
|
||||
// * "1 second" returns 1000
|
||||
// * "1 minute" returns 60000
|
||||
// * "1 hour" returns 3600000
|
||||
//
|
||||
// See config.UnitsToMillis for a list of supported units of measure.
|
||||
func ParseMilliseconds(str string) (int64, error) {
|
||||
s := strings.TrimSpace(str)
|
||||
reg := regexp.MustCompile("([0-9\\.\\-+]*)(.*)")
|
||||
matches := reg.FindStringSubmatch(s)
|
||||
if matches == nil || len(matches) < 1 || matches[1] == "" {
|
||||
return 0, fmt.Errorf("invalid syntax - '%s'", s)
|
||||
}
|
||||
digits := matches[1]
|
||||
units := "ms"
|
||||
if len(matches) > 1 && matches[2] != "" {
|
||||
units = matches[2]
|
||||
}
|
||||
|
||||
fDigits, err := strconv.ParseFloat(digits, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
msPerUnit, err := UnitsToMillis(units)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Check for overflow.
|
||||
fms := float64(msPerUnit) * fDigits
|
||||
if fms > math.MaxInt64 || fms < math.MinInt64 {
|
||||
return 0, fmt.Errorf("out of range - '%s' overflows", s)
|
||||
}
|
||||
ms := int64(fms)
|
||||
return ms, nil
|
||||
}
|
||||
|
||||
// UnitsToMillis returns the number of milliseconds represented by the specified unit of measure.
|
||||
//
|
||||
// Example:
|
||||
// * "second" returns 1000 <br/>
|
||||
// * "minute" returns 60000 <br/>
|
||||
// * "hour" returns 3600000 <br/>
|
||||
//
|
||||
// Supported units of measure:
|
||||
// * "milliseconds", "millis", "ms", "millisecond"
|
||||
// * "seconds", "sec", "s", "second"
|
||||
// * "minutes", "mins", "min", "m", "minute"
|
||||
// * "hours", "h", "hour"
|
||||
// * "days", "d", "day"
|
||||
// * "weeks", "w", "week"
|
||||
// * "years", "y", "year"
|
||||
func UnitsToMillis(units string) (ms int64, err error) {
|
||||
u := strings.TrimSpace(units)
|
||||
u = strings.ToLower(u)
|
||||
switch u {
|
||||
case "milliseconds", "millisecond", "millis", "ms":
|
||||
ms = 1
|
||||
case "seconds", "second", "sec", "s":
|
||||
ms = MillisPerSecond
|
||||
case "minutes", "minute", "mins", "min", "m":
|
||||
ms = MillisPerMinute
|
||||
case "hours", "hour", "h":
|
||||
ms = MillisPerHour
|
||||
case "days", "day", "d":
|
||||
ms = MillisPerDay
|
||||
case "weeks", "week", "w":
|
||||
ms = MillisPerWeek
|
||||
case "years", "year", "y":
|
||||
ms = MillisPerYear
|
||||
default:
|
||||
err = fmt.Errorf("invalid syntax - '%s' not a supported unit of measure", u)
|
||||
}
|
||||
return
|
||||
}
|
||||
Ссылка в новой задаче
Block a user