mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 21:48:25 +00:00
217 lines
6.0 KiB
Go
217 lines
6.0 KiB
Go
// Copyright 2017 HootSuite Media 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.
|
|
// Modified hereafter by contributors to runatlantis/atlantis.
|
|
//
|
|
// Package logging handles logging throughout Atlantis.
|
|
package logging
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/pkg/errors"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
//go:generate pegomock generate --package mocks -o mocks/mock_simple_logging.go SimpleLogging
|
|
|
|
// SimpleLogging is the interface used for logging throughout the codebase.
|
|
type SimpleLogging interface {
|
|
|
|
// These basically just fmt.Sprintf() the message and args.
|
|
Debug(format string, a ...interface{})
|
|
Info(format string, a ...interface{})
|
|
Warn(format string, a ...interface{})
|
|
Err(format string, a ...interface{})
|
|
Log(level LogLevel, format string, a ...interface{})
|
|
SetLevel(lvl LogLevel)
|
|
|
|
// With adds a variadic number of fields to the logging context. It accepts a
|
|
// mix of strongly-typed Field objects and loosely-typed key-value pairs. When
|
|
// processing pairs, the first element of the pair is used as the field key
|
|
// and the second as the field value.
|
|
With(a ...interface{}) SimpleLogging
|
|
|
|
// Creates a new logger with history preserved . log storage + search strategies
|
|
// should ideally be used instead of managing this ourselves.
|
|
// keeping as a separate method to ensure that usage of history is completely intentional
|
|
WithHistory(a ...interface{}) SimpleLogging
|
|
|
|
// Fetches the history we've stored associated with the logging context
|
|
GetHistory() string
|
|
|
|
// Flushes anything left in the buffer
|
|
Flush() error
|
|
}
|
|
|
|
type StructuredLogger struct {
|
|
z *zap.SugaredLogger
|
|
level zap.AtomicLevel
|
|
keepHistory bool
|
|
// History stores all log entries ever written using
|
|
// this logger. This is safe for short-lived loggers
|
|
// like those used during plan/apply commands.
|
|
// TODO: Deprecate this
|
|
// this is added here to maintain backwards compatibility
|
|
// This doesn't really make sense to keep given that structured logging
|
|
// gives us the ability to query our logs across multiple dimensions
|
|
// I don't believe we should mix this in with atlantis commands and expose this to the user
|
|
history bytes.Buffer
|
|
}
|
|
|
|
func NewStructuredLoggerFromLevel(lvl LogLevel) (SimpleLogging, error) {
|
|
cfg := zap.NewProductionConfig()
|
|
|
|
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
|
cfg.Level = zap.NewAtomicLevelAt(lvl.zLevel)
|
|
return newStructuredLogger(cfg)
|
|
}
|
|
|
|
func NewStructuredLogger() (SimpleLogging, error) {
|
|
cfg := zap.NewProductionConfig()
|
|
cfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
|
return newStructuredLogger(cfg)
|
|
}
|
|
|
|
func newStructuredLogger(cfg zap.Config) (*StructuredLogger, error) {
|
|
baseLogger, err := cfg.Build()
|
|
|
|
baseLogger = baseLogger.
|
|
// ensures that the caller doesn't just say logging/simple_logger each time
|
|
WithOptions(zap.AddCallerSkip(1)).
|
|
WithOptions(zap.AddStacktrace(zapcore.WarnLevel)).
|
|
// creates isolated context for all future kv pairs, name can be flexible as needed
|
|
With(zap.Namespace("json"))
|
|
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, " initializing structured logger")
|
|
}
|
|
|
|
return &StructuredLogger{
|
|
z: baseLogger.Sugar(),
|
|
level: cfg.Level,
|
|
}, nil
|
|
}
|
|
|
|
func (l *StructuredLogger) With(a ...interface{}) SimpleLogging {
|
|
return &StructuredLogger{
|
|
z: l.z.With(a...),
|
|
level: l.level,
|
|
}
|
|
}
|
|
|
|
func (l *StructuredLogger) WithHistory(a ...interface{}) SimpleLogging {
|
|
logger := &StructuredLogger{
|
|
z: l.z.With(a...),
|
|
level: l.level,
|
|
}
|
|
|
|
// ensure that the history is kept across loggers.
|
|
logger.keepHistory = true
|
|
logger.history = l.history
|
|
|
|
return logger
|
|
}
|
|
|
|
func (l *StructuredLogger) GetHistory() string {
|
|
return l.history.String()
|
|
}
|
|
|
|
func (l *StructuredLogger) Debug(format string, a ...interface{}) {
|
|
l.z.Debugf(format, a...)
|
|
l.saveToHistory(Debug, format, a...)
|
|
}
|
|
|
|
func (l *StructuredLogger) Info(format string, a ...interface{}) {
|
|
l.z.Infof(format, a...)
|
|
l.saveToHistory(Info, format, a...)
|
|
}
|
|
|
|
func (l *StructuredLogger) Warn(format string, a ...interface{}) {
|
|
l.z.Warnf(format, a...)
|
|
l.saveToHistory(Warn, format, a...)
|
|
}
|
|
|
|
func (l *StructuredLogger) Err(format string, a ...interface{}) {
|
|
l.z.Errorf(format, a...)
|
|
l.saveToHistory(Error, format, a...)
|
|
}
|
|
|
|
func (l *StructuredLogger) Log(level LogLevel, format string, a ...interface{}) {
|
|
switch level {
|
|
case Debug:
|
|
l.z.Debugf(format, a...)
|
|
case Info:
|
|
l.z.Infof(format, a...)
|
|
case Warn:
|
|
l.z.Warnf(format, a...)
|
|
case Error:
|
|
l.z.Errorf(format, a...)
|
|
}
|
|
}
|
|
|
|
func (l *StructuredLogger) SetLevel(lvl LogLevel) {
|
|
if l != nil {
|
|
l.level.SetLevel(lvl.zLevel)
|
|
}
|
|
}
|
|
|
|
func (l *StructuredLogger) Flush() error {
|
|
return l.z.Sync()
|
|
}
|
|
|
|
func (l *StructuredLogger) saveToHistory(lvl LogLevel, format string, a ...interface{}) {
|
|
if !l.keepHistory {
|
|
return
|
|
}
|
|
msg := fmt.Sprintf(format, a...)
|
|
l.history.WriteString(fmt.Sprintf("[%s] %s\n", lvl.shortStr, msg))
|
|
}
|
|
|
|
// NewNoopLogger creates a logger instance that discards all logs and never
|
|
// writes them. Used for testing.
|
|
func NewNoopLogger(t zaptest.TestingT) SimpleLogging {
|
|
level := zap.DebugLevel
|
|
return &StructuredLogger{
|
|
z: zaptest.NewLogger(t, zaptest.Level(level)).Sugar(),
|
|
level: zap.NewAtomicLevelAt(level),
|
|
}
|
|
}
|
|
|
|
type LogLevel struct {
|
|
zLevel zapcore.Level
|
|
shortStr string
|
|
}
|
|
|
|
var (
|
|
Debug = LogLevel{
|
|
zLevel: zapcore.DebugLevel,
|
|
shortStr: "DBUG",
|
|
}
|
|
Info = LogLevel{
|
|
zLevel: zapcore.InfoLevel,
|
|
shortStr: "INFO",
|
|
}
|
|
Warn = LogLevel{
|
|
zLevel: zapcore.WarnLevel,
|
|
shortStr: "WARN",
|
|
}
|
|
Error = LogLevel{
|
|
zLevel: zapcore.ErrorLevel,
|
|
shortStr: "EROR",
|
|
}
|
|
)
|