@@ -13,7 +13,10 @@ import (
1313 "go.uber.org/zap/zapcore"
1414 "golang.org/x/exp/slices"
1515
16+ otellog "go.opentelemetry.io/otel/log"
17+
1618 "github.com/smartcontractkit/chainlink-common/pkg/logger"
19+ "github.com/smartcontractkit/chainlink-common/pkg/logger/otelzap"
1720)
1821
1922// HCLogLogger returns an [hclog.Logger] backed by the given [logger.Logger].
@@ -162,13 +165,37 @@ func (h *hclSinkAdapter) Accept(_ string, level hclog.Level, msg string, args ..
162165
163166// NewLogger returns a new [logger.Logger] configured to encode [hclog] compatible JSON.
164167func NewLogger () (logger.Logger , error ) {
165- return logger .NewWith (func (cfg * zap.Config ) {
166- cfg .Level .SetLevel (zap .DebugLevel )
167- cfg .EncoderConfig .LevelKey = "@level"
168- cfg .EncoderConfig .MessageKey = "@message"
169- cfg .EncoderConfig .TimeKey = "@timestamp"
170- cfg .EncoderConfig .EncodeTime = zapcore .TimeEncoderOfLayout ("2006-01-02T15:04:05.000000Z07:00" )
171- })
168+ return logger .NewWith (configureHCLogEncoder )
169+ }
170+
171+ // configureHCLogEncoder mutates cfg to use hclog-compatible field names and timestamp format.
172+ // NOTE: It also sets the log level to Debug to preserve prior behavior where each caller
173+ // manually set Debug before applying identical encoder tweaks. Centralizing avoids drift.
174+ // If a different level is desired, callers should override cfg.Level AFTER calling this helper.
175+ func configureHCLogEncoder (cfg * zap.Config ) {
176+ cfg .Level .SetLevel (zap .DebugLevel )
177+ cfg .EncoderConfig .LevelKey = "@level"
178+ cfg .EncoderConfig .MessageKey = "@message"
179+ cfg .EncoderConfig .TimeKey = "@timestamp"
180+ cfg .EncoderConfig .EncodeTime = zapcore .TimeEncoderOfLayout ("2006-01-02T15:04:05.000000Z07:00" )
181+ }
182+
183+ // NewOtelLogger returns a logger with two cores:
184+ // 1. The primary JSON core configured via cfgFn (encoder keys changed to @level, @message, @timestamp).
185+ // 2. The otel core (otelzap.NewCore) which receives the raw zap.Entry and fields.
186+ //
187+ // Important:
188+ // The cfgFn only mutates the encoder config used to build the first core.
189+ // otelzap.NewCore implements zapcore.Core and does NOT use that encoder; it derives attributes from the zap.Entry
190+ // (Message, Level, Time, etc.) and zap.Fields directly. Therefore changing encoder keys here does NOT affect how
191+ // the otel core extracts data, and only the first core's JSON output format is altered.
192+ // This preserves backward compatibility for OTEL export while allowing hclog-compatible key names in the primary output.
193+ func NewOtelLogger (otelLogger otellog.Logger ) (logger.Logger , error ) {
194+ primaryCore , err := logger .NewCore (configureHCLogEncoder )
195+ if err != nil {
196+ return nil , err
197+ }
198+ return logger .NewWithCores (primaryCore , otelzap .NewCore (otelLogger )), nil
172199}
173200
174201// onceValue returns a function that invokes f only once and returns the value
0 commit comments