// Copyright (c) 2016-2019 Couchbase, Inc. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file // except in compliance with the License. You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software distributed under the // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing permissions // and limitations under the License. package logging import ( "bytes" "encoding/json" "fmt" "io" "log" "time" ) type goLogger struct { logger *log.Logger level Level entryFormatter formatter } const ( _LEVEL = "_level" _MSG = "_msg" _TIME = "_time" _RLEVEL = "_rlevel" ) func NewLogger(out io.Writer, lvl Level, fmtLogging LogEntryFormatter, fmtArgs ...interface{}) *goLogger { logger := &goLogger{ logger: log.New(out, "", 0), level: lvl, } if fmtLogging == JSONFORMATTER { logger.entryFormatter = &jsonFormatter{} } else if fmtLogging == KVFORMATTER { logger.entryFormatter = &keyvalueFormatter{} } else if fmtLogging == UNIFORMFORMATTER { logger.entryFormatter = &uniformFormatter{ callback: fmtArgs[0].(ComponentCallback), } } else { logger.entryFormatter = &textFormatter{} } return logger } func (gl *goLogger) Logp(level Level, msg string, kv ...Pair) { if gl.logger == nil { return } if level <= gl.level { e := newLogEntry(msg, level) copyPairs(e, kv) gl.log(e) } } func (gl *goLogger) Debugp(msg string, kv ...Pair) { gl.Logp(DEBUG, msg, kv...) } func (gl *goLogger) Tracep(msg string, kv ...Pair) { gl.Logp(TRACE, msg, kv...) } func (gl *goLogger) Requestp(rlevel Level, msg string, kv ...Pair) { if gl.logger == nil { return } if REQUEST <= gl.level { e := newLogEntry(msg, REQUEST) e.Rlevel = rlevel copyPairs(e, kv) gl.log(e) } } func (gl *goLogger) Infop(msg string, kv ...Pair) { gl.Logp(INFO, msg, kv...) } func (gl *goLogger) Warnp(msg string, kv ...Pair) { gl.Logp(WARN, msg, kv...) } func (gl *goLogger) Errorp(msg string, kv ...Pair) { gl.Logp(ERROR, msg, kv...) } func (gl *goLogger) Severep(msg string, kv ...Pair) { gl.Logp(SEVERE, msg, kv...) } func (gl *goLogger) Fatalp(msg string, kv ...Pair) { gl.Logp(FATAL, msg, kv...) } func (gl *goLogger) Logm(level Level, msg string, kv Map) { if gl.logger == nil { return } if level <= gl.level { e := newLogEntry(msg, level) e.Data = kv gl.log(e) } } func (gl *goLogger) Debugm(msg string, kv Map) { gl.Logm(DEBUG, msg, kv) } func (gl *goLogger) Tracem(msg string, kv Map) { gl.Logm(TRACE, msg, kv) } func (gl *goLogger) Requestm(rlevel Level, msg string, kv Map) { if gl.logger == nil { return } if REQUEST <= gl.level { e := newLogEntry(msg, REQUEST) e.Rlevel = rlevel e.Data = kv gl.log(e) } } func (gl *goLogger) Infom(msg string, kv Map) { gl.Logm(INFO, msg, kv) } func (gl *goLogger) Warnm(msg string, kv Map) { gl.Logm(WARN, msg, kv) } func (gl *goLogger) Errorm(msg string, kv Map) { gl.Logm(ERROR, msg, kv) } func (gl *goLogger) Severem(msg string, kv Map) { gl.Logm(SEVERE, msg, kv) } func (gl *goLogger) Fatalm(msg string, kv Map) { gl.Logm(FATAL, msg, kv) } func (gl *goLogger) Logf(level Level, format string, args ...interface{}) { if gl.logger == nil { return } if level <= gl.level { e := newLogEntry(fmt.Sprintf(format, args...), level) gl.log(e) } } func (gl *goLogger) Debugf(format string, args ...interface{}) { gl.Logf(DEBUG, format, args...) } func (gl *goLogger) Tracef(format string, args ...interface{}) { gl.Logf(TRACE, format, args...) } func (gl *goLogger) Requestf(rlevel Level, format string, args ...interface{}) { if gl.logger == nil { return } if REQUEST <= gl.level { e := newLogEntry(fmt.Sprintf(format, args...), REQUEST) e.Rlevel = rlevel gl.log(e) } } func (gl *goLogger) Infof(format string, args ...interface{}) { gl.Logf(INFO, format, args...) } func (gl *goLogger) Warnf(format string, args ...interface{}) { gl.Logf(WARN, format, args...) } func (gl *goLogger) Errorf(format string, args ...interface{}) { gl.Logf(ERROR, format, args...) } func (gl *goLogger) Severef(format string, args ...interface{}) { gl.Logf(SEVERE, format, args...) } func (gl *goLogger) Fatalf(format string, args ...interface{}) { gl.Logf(FATAL, format, args...) } func (gl *goLogger) Level() Level { return gl.level } func (gl *goLogger) SetLevel(level Level) { gl.level = level } func (gl *goLogger) log(newEntry *logEntry) { s := gl.entryFormatter.format(newEntry) gl.logger.Print(s) } type logEntry struct { Time string Level Level Rlevel Level Message string Data Map } func newLogEntry(msg string, level Level) *logEntry { return &logEntry{ Time: time.Now().Format("2006-01-02T15:04:05.000-07:00"), // time.RFC3339 with milliseconds Level: level, Rlevel: NONE, Message: msg, } } func copyPairs(newEntry *logEntry, pairs []Pair) { newEntry.Data = make(Map, len(pairs)) for _, p := range pairs { newEntry.Data[p.Name] = p.Value } } type formatter interface { format(*logEntry) string } type textFormatter struct { } // ex. 2016-02-10T09:15:25.498-08:00 [INFO] This is a message from test in text format func (*textFormatter) format(newEntry *logEntry) string { b := &bytes.Buffer{} appendValue(b, newEntry.Time) if newEntry.Rlevel != NONE { fmt.Fprintf(b, "[%s,%s] ", newEntry.Level.String(), newEntry.Rlevel.String()) } else { fmt.Fprintf(b, "[%s] ", newEntry.Level.String()) } appendValue(b, newEntry.Message) for key, value := range newEntry.Data { appendKeyValue(b, key, value) } b.WriteByte('\n') s := bytes.NewBuffer(b.Bytes()) return s.String() } func appendValue(b *bytes.Buffer, value interface{}) { if _, ok := value.(string); ok { fmt.Fprintf(b, "%s ", value) } else { fmt.Fprintf(b, "%v ", value) } } type keyvalueFormatter struct { } // ex. _time=2016-02-10T09:15:25.498-08:00 _level=INFO _msg=This is a message from test in key-value format func (*keyvalueFormatter) format(newEntry *logEntry) string { b := &bytes.Buffer{} appendKeyValue(b, _TIME, newEntry.Time) appendKeyValue(b, _LEVEL, newEntry.Level.String()) if newEntry.Rlevel != NONE { appendKeyValue(b, _RLEVEL, newEntry.Rlevel.String()) } appendKeyValue(b, _MSG, newEntry.Message) for key, value := range newEntry.Data { appendKeyValue(b, key, value) } b.WriteByte('\n') s := bytes.NewBuffer(b.Bytes()) return s.String() } func appendKeyValue(b *bytes.Buffer, key, value interface{}) { if _, ok := value.(string); ok { fmt.Fprintf(b, "%v=%s ", key, value) } else { fmt.Fprintf(b, "%v=%v ", key, value) } } type jsonFormatter struct { } // ex. {"_level":"INFO","_msg":"This is a message from test in json format","_time":"2016-02-10T09:12:59.518-08:00"} func (*jsonFormatter) format(newEntry *logEntry) string { if newEntry.Data == nil { newEntry.Data = make(Map, 5) } newEntry.Data[_TIME] = newEntry.Time newEntry.Data[_LEVEL] = newEntry.Level.String() if newEntry.Rlevel != NONE { newEntry.Data[_RLEVEL] = newEntry.Rlevel.String() } newEntry.Data[_MSG] = newEntry.Message serialized, _ := json.Marshal(newEntry.Data) s := bytes.NewBuffer(append(serialized, '\n')) return s.String() } type ComponentCallback func() string type uniformFormatter struct { callback ComponentCallback } // ex. 2019-03-15T11:28:07.652-04:00 DEBU COMPONENT.subcomponent This is a message from test in uniform format var _LEVEL_UNIFORM = []string{ DEBUG: "DEBU", TRACE: "TRAC", REQUEST: "REQU", INFO: "INFO", WARN: "WARN", ERROR: "ERRO", SEVERE: "SEVE", FATAL: "FATA", NONE: "NONE", } func (level Level) UniformString() string { return _LEVEL_UNIFORM[level] } func (uf *uniformFormatter) format(newEntry *logEntry) string { b := &bytes.Buffer{} appendValue(b, newEntry.Time) component := uf.callback() if newEntry.Rlevel != NONE { // not really any accommodation for a composite level in the uniform standard; just output as abbr,abbr fmt.Fprintf(b, "%s,%s %s ", newEntry.Level.UniformString(), newEntry.Rlevel.UniformString(), component) } else { fmt.Fprintf(b, "%s %s ", newEntry.Level.UniformString(), component) } appendValue(b, newEntry.Message) for key, value := range newEntry.Data { appendKeyValue(b, key, value) } b.WriteByte('\n') s := bytes.NewBuffer(b.Bytes()) return s.String() }