opentelemetry-go-extra
opentelemetry-go-extra copied to clipboard
feat(otelzap): add dynamic field value resolution
This PR aims to add a possibility to dynamically resolve fields from the provided context and fix #59.
Our use case is that Datadog requires us to provide both the spanId and the traceId to the logged message in the keys dd.span_id and dd.trace_id. In addition, Datadog expects the trace information to be in another format for traces and logs to be correlated. This change will make it possible for us to configure this transformation in the logger itself instead of providing the transformation in every log message.
Below is a sample implementation for Datadog with this change that will print the converted trace information:
ERROR example/main.go:29 hello from zap {"dd.trace_id": "2730366003796952559", "dd.span_id": "2730366003796952559"}
func main() {
l, err := zap.NewDevelopment()
if err != nil {
panic(err)
}
logger = otelzap.New(l, WithDatadogFields())
ctx, span := tracer.Start(context.Background(), "root")
defer span.End()
logger.Ctx(ctx).Error("hello from zap")
}
func WithDatadogFields() otelzap.Option {
convertTraceId := func(id string) string {
if len(id) < 16 {
return ""
}
if len(id) > 16 {
id = id[16:]
}
intValue, err := strconv.ParseUint(id, 16, 64)
if err != nil {
return ""
}
return strconv.FormatUint(intValue, 10)
}
return otelzap.WithDynamicFields(func(ctx context.Context) []zap.Field {
fields := make([]zap.Field, 0)
span := trace.SpanFromContext(ctx)
if span.IsRecording() {
fields = append(fields, zap.String("dd.trace_id", convertTraceId(span.SpanContext().TraceID().String())))
fields = append(fields, zap.String("dd.span_id", convertTraceId(span.SpanContext().SpanID().String())))
}
return fields
})
}
@vmihailenco I just want to reach out to hear your opinion related to this PR. Is it a functionality that you see is feasible to be included in this project or is it more suitable to wrap this library and add the dynamic fields as part of the wrapped implementation?
I have no strong opinion, even though it could be a nice developer experience to not have to wrap the library, but instead give the implementor the freedom to adjust it after their need without adding an extra burden on the maintainers to keep adding support for each possible use case when they arise.
The package now uses Otel Logs API so there is no need for custom trace_id/span_id fields.