diff --git a/Makefile b/Makefile index 72741812..334d1ad0 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,7 @@ LIBREOFFICE_DISABLE_ROUTES=false LOG_LEVEL=info LOG_FORMAT=auto LOG_FIELDS_PREFIX= +LOG_ENABLE_GCP_SEVERITY=false PDFENGINES_ENGINES= PDFENGINES_MERGE_ENGINES=qpdf,pdfcpu,pdftk PDFENGINES_SPLIT_ENGINES=pdfcpu,qpdf,pdftk @@ -134,6 +135,7 @@ run: ## Start a Gotenberg container --log-level=$(LOG_LEVEL) \ --log-format=$(LOG_FORMAT) \ --log-fields-prefix=$(LOG_FIELDS_PREFIX) \ + --log-enable-gcp-severity=$(LOG_ENABLE_GCP_SEVERITY) \ --pdfengines-engines=$(PDFENGINES_ENGINES) \ --pdfengines-merge-engines=$(PDFENGINES_MERGE_ENGINES) \ --pdfengines-split-engines=$(PDFENGINES_SPLIT_ENGINES) \ diff --git a/pkg/modules/logging/color.go b/pkg/modules/logging/color.go new file mode 100644 index 00000000..e9a9d97b --- /dev/null +++ b/pkg/modules/logging/color.go @@ -0,0 +1,49 @@ +package logging + +import ( + "fmt" + + "go.uber.org/zap/zapcore" +) + +// Foreground colors. +// Copy pasted from go.uber.org/zap/internal/color/color.go +const ( + black color = iota + 30 + red + green + yellow + blue + magenta + cyan + white +) + +type color uint8 + +func (c color) Add(s string) string { + return fmt.Sprintf("\x1b[%dm%s\x1b[0m", uint8(c), s) +} + +func levelToColor(l zapcore.Level) color { + switch l { + case zapcore.DebugLevel: + return cyan + case zapcore.InfoLevel: + return blue + case zapcore.WarnLevel: + return yellow + case zapcore.ErrorLevel: + return red + case zapcore.DPanicLevel: + return red + case zapcore.PanicLevel: + return red + case zapcore.FatalLevel: + return red + case zapcore.InvalidLevel: + return red + default: + return red + } +} diff --git a/pkg/modules/logging/gcp.go b/pkg/modules/logging/gcp.go new file mode 100644 index 00000000..e9ddc9f6 --- /dev/null +++ b/pkg/modules/logging/gcp.go @@ -0,0 +1,36 @@ +package logging + +import "go.uber.org/zap/zapcore" + +func gcpSeverity(l zapcore.Level) string { + switch l { + case zapcore.DebugLevel: + return "DEBUG" + case zapcore.InfoLevel: + return "INFO" + case zapcore.WarnLevel: + return "WARNING" + case zapcore.ErrorLevel: + return "ERROR" + case zapcore.DPanicLevel: + return "CRITICAL" + case zapcore.PanicLevel: + return "ALERT" + case zapcore.FatalLevel: + return "EMERGENCY" + case zapcore.InvalidLevel: + return "DEFAULT" + default: + return "DEFAULT" + } +} + +func gcpSeverityEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(gcpSeverity(l)) +} + +func gcpSeverityColorEncoder(l zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { + severity := gcpSeverity(l) + c := levelToColor(l) + enc.AppendString(c.Add(severity)) +} diff --git a/pkg/modules/logging/logging.go b/pkg/modules/logging/logging.go index d9d80023..36fc1ae2 100644 --- a/pkg/modules/logging/logging.go +++ b/pkg/modules/logging/logging.go @@ -34,9 +34,10 @@ const ( // Logging is a module which implements the [gotenberg.LoggerProvider] // interface. type Logging struct { - level string - format string - fieldsPrefix string + level string + format string + fieldsPrefix string + enableGcpSeverity bool } // Descriptor returns a [Logging]'s module descriptor. @@ -48,6 +49,7 @@ func (log *Logging) Descriptor() gotenberg.ModuleDescriptor { fs.String("log-level", infoLoggingLevel, fmt.Sprintf("Choose the level of logging detail. Options include %s, %s, %s, or %s", errorLoggingLevel, warnLoggingLevel, infoLoggingLevel, debugLoggingLevel)) fs.String("log-format", autoLoggingFormat, fmt.Sprintf("Specify the format of logging. Options include %s, %s, or %s", autoLoggingFormat, jsonLoggingFormat, textLoggingFormat)) fs.String("log-fields-prefix", "", "Prepend a specified prefix to each field in the logs") + fs.Bool("log-enable-gcp-severity", false, "Enable Google Cloud Platform severity mapping") return fs }(), @@ -62,6 +64,7 @@ func (log *Logging) Provision(ctx *gotenberg.Context) error { log.level = flags.MustString("log-level") log.format = flags.MustString("log-format") log.fieldsPrefix = flags.MustString("log-fields-prefix") + log.enableGcpSeverity = flags.MustBool("log-enable-gcp-severity") return nil } @@ -101,7 +104,7 @@ func (log *Logging) Logger(mod gotenberg.Module) (*zap.Logger, error) { return nil, fmt.Errorf("get log level: %w", err) } - encoder, err := newLogEncoder(log.format) + encoder, err := newLogEncoder(log.format, log.enableGcpSeverity) if err != nil { return nil, fmt.Errorf("get log encoder: %w", err) } @@ -166,7 +169,7 @@ func newLogLevel(level string) (zapcore.Level, error) { return lvl, nil } -func newLogEncoder(format string) (zapcore.Encoder, error) { +func newLogEncoder(format string, gcpSeverity bool) (zapcore.Encoder, error) { isTerminal := term.IsTerminal(int(os.Stdout.Fd())) encCfg := zap.NewProductionEncoderConfig() @@ -177,15 +180,25 @@ func newLogEncoder(format string) (zapcore.Encoder, error) { encoder.AppendString(ts.Local().Format("2006/01/02 15:04:05.000")) } - if format == textLoggingFormat || format == autoLoggingFormat { - encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder + if format == autoLoggingFormat { + format = textLoggingFormat } - } - if format == autoLoggingFormat && isTerminal { - format = textLoggingFormat - } else if format == autoLoggingFormat { - format = jsonLoggingFormat + if format == textLoggingFormat && gcpSeverity { + encCfg.EncodeLevel = gcpSeverityColorEncoder + } else if format == textLoggingFormat { + encCfg.EncodeLevel = zapcore.CapitalColorLevelEncoder + } else if gcpSeverity { + encCfg.EncodeLevel = gcpSeverityEncoder + } + } else { + if format == autoLoggingFormat { + format = jsonLoggingFormat + } + + if gcpSeverity { + encCfg.EncodeLevel = gcpSeverityEncoder + } } switch format {