cmd/tfplan2cai/logger.go (106 lines of code) (raw):

// Copyright 2021 Google LLC // // 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 main import ( "go.uber.org/zap" "go.uber.org/zap/buffer" "go.uber.org/zap/zapcore" ) type errorDetails struct { // error message error string // stacktrace or additional context context string } func (ed errorDetails) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("error", ed.error) enc.AddString("context", ed.context) return nil } type errorEncoder struct { zapcore.Encoder } func (enc errorEncoder) Clone() zapcore.Encoder { return errorEncoder{ Encoder: enc.Encoder.Clone(), } } func (enc errorEncoder) EncodeEntry(ent zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { ed := errorDetails{ error: ent.Message, context: ent.Stack, } fields = append([]zapcore.Field{ zap.Object("error_details", ed), }, fields...) return enc.Encoder.EncodeEntry(ent, fields) } func newJSONEncoder(cfg zapcore.EncoderConfig) errorEncoder { return errorEncoder{ Encoder: zapcore.NewJSONEncoder(cfg), } } func newConsoleEncoder(cfg zapcore.EncoderConfig) errorEncoder { return errorEncoder{ Encoder: zapcore.NewConsoleEncoder(cfg), } } func newErrorLogger(verbosity string, useStructuredLogging bool, writeSyncer zapcore.WriteSyncer) *zap.Logger { // Return a logger that produces expected structured output format for errors var level zapcore.LevelEnabler options := []zap.Option{ zap.Fields( // Message format version zap.String("version", "v1.0.0"), ), } switch verbosity { case "debug": level = zap.DebugLevel options = append(options, zap.AddStacktrace(zap.WarnLevel)) // case "info" is handled by default case "warning": level = zap.WarnLevel options = append(options, zap.AddStacktrace(zap.ErrorLevel)) case "error": level = zap.ErrorLevel options = append(options, zap.AddStacktrace(zap.ErrorLevel)) case "critical": level = zap.PanicLevel options = append(options, zap.AddStacktrace(zap.PanicLevel)) case "none": return zap.NewNop() default: level = zap.InfoLevel options = append(options, zap.AddStacktrace(zap.WarnLevel)) } encoderConfig := zapcore.EncoderConfig{ TimeKey: "timestamp", LevelKey: "level", MessageKey: "", StacktraceKey: "", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.RFC3339NanoTimeEncoder, } var encoder errorEncoder if useStructuredLogging { encoder = newJSONEncoder(encoderConfig) } else { encoder = newConsoleEncoder(encoderConfig) } core := zapcore.NewCore(encoder, writeSyncer, level) return zap.New(core, options...) } func newOutputLogger(writeSyncer zapcore.WriteSyncer) *zap.Logger { // Return a logger that produces expected structured output format for output options := []zap.Option{ zap.Fields( // Message format version zap.String("version", "v1.0.0"), ), } level := zap.NewAtomicLevelAt(zap.DebugLevel) options = append(options, zap.AddStacktrace(zap.WarnLevel)) encoderConfig := zapcore.EncoderConfig{ TimeKey: "timestamp", LevelKey: "", MessageKey: "body", StacktraceKey: "", EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.RFC3339NanoTimeEncoder, } encoder := zapcore.NewJSONEncoder(encoderConfig) core := zapcore.NewCore(encoder, writeSyncer, level) return zap.New(core, options...) }