diff --git a/internal/encoders/config.go b/internal/encoders/config.go index 30e68dc..4a30baf 100644 --- a/internal/encoders/config.go +++ b/internal/encoders/config.go @@ -38,6 +38,31 @@ var OpenTelemetryConfig = zapcore.EncoderConfig{ EncodeCaller: zapcore.ShortCallerEncoder, } +var GCPConfig = zapcore.EncoderConfig{ + NameKey: "InstrumentationScope", + // https://cloud.google.com/logging/docs/agent/logging/configuration#timestamp-processing + TimeKey: "timestampNanos", + EncodeTime: zapcore.EpochNanosTimeEncoder, + // https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity + // https://cloud.google.com/logging/docs/agent/logging/configuration#special-fields + LevelKey: "severity", + EncodeLevel: zapcore.CapitalLevelEncoder, // most levels correspond to the OT level text + // https://cloud.google.com/logging/docs/agent/logging/configuration#special-fields + MessageKey: "message", + + // These don't really have an equivalent in the OT spec, and we can't stick it under + // Attributes because they are top-level traits in Zap, so we just capitalize them and + // hope for the best. + CallerKey: "Caller", + FunctionKey: "Function", + StacktraceKey: "Stacktrace", + + // Defaults + LineEnding: zapcore.DefaultLineEnding, + EncodeDuration: zapcore.SecondsDurationEncoder, + EncodeCaller: zapcore.ShortCallerEncoder, +} + // applyDevConfig applies options for dev environments to the encoder config func applyDevConfig(cfg zapcore.EncoderConfig) zapcore.EncoderConfig { // Nice colors based on log level @@ -75,6 +100,8 @@ func BuildEncoder(format output.Format, development bool) (enc zapcore.Encoder) return zapcore.NewConsoleEncoder(config) case output.FormatJSON: return zapcore.NewJSONEncoder(config) + case output.FormatJSONGCP: + return zapcore.NewJSONEncoder(GCPConfig) default: panic("unknown output format") } diff --git a/output/output.go b/output/output.go index 4f62ba4..2af58f8 100644 --- a/output/output.go +++ b/output/output.go @@ -7,6 +7,10 @@ const ( // FormatJSON encodes log entries to a machine-readable, OpenTelemetry-structured // format. FormatJSON Format = "json" + // FormatJSON encodes log entries to a machine-readable, GCP-structured format. + // It's similar to OpenTelemetry-structured format, but the severity field + // complies with https://cloud.google.com/logging/docs/structured-logging#special-payload-fields + FormatJSONGCP Format = "json_gcp" // FormatConsole encodes log entries to a human-readable format. FormatConsole Format = "console" ) @@ -16,16 +20,16 @@ const ( // log formats. func ParseFormat(format string) Format { switch format { - case string(FormatJSON), + case string(FormatJSONGCP): + return FormatJSON + case string(FormatJSON), "logfmt": // True 'logfmt' has significant limitations around certain field types: // https://github.com/jsternberg/zap-logfmt#limitations so since it implies a // desire for a somewhat structured format, we interpret it as OutputJSON. - "logfmt": return FormatJSON - case string(FormatConsole), + case string(FormatConsole), "condensed": // The previous 'condensed' format is optimized for local dev, so it serves the // same purpose as OutputConsole - "condensed": return FormatConsole }