From b9441a0c8df6046ba9b07affa371b485d5f4309e Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Thu, 9 Jan 2025 09:19:50 +0100 Subject: [PATCH 1/8] Add log.Value marshaller --- telemetry/dd/json/lval.go | 56 ++++++++++++++++++++++++++++++++++ telemetry/dd/json/lval_test.go | 42 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 telemetry/dd/json/lval.go create mode 100644 telemetry/dd/json/lval_test.go diff --git a/telemetry/dd/json/lval.go b/telemetry/dd/json/lval.go new file mode 100644 index 0000000..f1af8e4 --- /dev/null +++ b/telemetry/dd/json/lval.go @@ -0,0 +1,56 @@ +package json + +import ( + "encoding/json" + "errors" + + "go.opentelemetry.io/otel/log" +) + +type value struct { + log.Value +} + +// MarshalJSON implements a custom marshal function to encode log.Value. +func (v value) MarshalJSON() ([]byte, error) { + var val any + switch v.Kind() { + case log.KindString: + val = v.AsString() + case log.KindInt64: + val = v.AsInt64() + case log.KindFloat64: + val = v.AsFloat64() + case log.KindBool: + val = v.AsBool() + case log.KindBytes: + val = v.AsBytes() + case log.KindMap: + m := v.AsMap() + values := make(map[string]json.RawMessage, len(m)) + for _, kv := range m { + data, err := newValue(kv.Value).MarshalJSON() + if err != nil { + return nil, err + } + values[kv.Key] = data + } + val = values + case log.KindSlice: + s := v.AsSlice() + values := make([]json.RawMessage, 0, len(s)) + for _, e := range s { + data, err := newValue(e).MarshalJSON() + if err != nil { + return nil, err + } + values = append(values, data) + } + val = values + case log.KindEmpty: + val = nil + default: + return nil, errors.New("invalid Kind") + } + return json.Marshal(val) +} diff --git a/telemetry/dd/json/lval_test.go b/telemetry/dd/json/lval_test.go new file mode 100644 index 0000000..d0663bd --- /dev/null +++ b/telemetry/dd/json/lval_test.go @@ -0,0 +1,42 @@ +package json + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/log" +) + +func Test_value_MarshalJSON(t *testing.T) { + tests := []struct { + name string + value log.Value + want string + wantErr assert.ErrorAssertionFunc + }{ + {"String", log.StringValue("string"), `"string"`, assert.NoError}, + {"Int", log.IntValue(1), `1`, assert.NoError}, + {"-Int", log.IntValue(-1), `-1`, assert.NoError}, + {"Int64", log.Int64Value(1), `1`, assert.NoError}, + {"-Int64", log.Int64Value(-1), `-1`, assert.NoError}, + {"Bool", log.BoolValue(true), `true`, assert.NoError}, + {"Bytes", log.BytesValue([]byte("bytes")), `"Ynl0ZXM="`, assert.NoError}, + {"Map", log.MapValue(log.String("key", "value")), `{"key":"value"}`, assert.NoError}, + {"Map of map", log.MapValue(log.Map("key1", log.String("key2", "value"))), `{"key1":{"key2":"value"}}`, assert.NoError}, + {"Slice", log.SliceValue(log.StringValue("value"), log.IntValue(1234)), `["value",1234]`, assert.NoError}, + {"Slice of slice", log.SliceValue(log.SliceValue(log.StringValue("value")), log.IntValue(1234)), `[["value"],1234]`, assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := value{ + Value: tt.value, + } + got, err := v.MarshalJSON() + if !tt.wantErr(t, err, fmt.Sprintf("MarshalJSON()")) { + return + } + assert.Equalf(t, tt.want, string(got), "MarshalJSON()") + }) + } +} From 5418b45b47b9cdb5987f0c5561b8969f9e7fe037 Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 15:16:55 +0100 Subject: [PATCH 2/8] First json exporter --- telemetry/dd/json/attr.go | 18 +++ telemetry/dd/json/attr_test.go | 117 +++++++++++++++++++ telemetry/dd/json/ddjson.go | 121 +++++++++++++++++++ telemetry/dd/json/ddjson_test.go | 193 +++++++++++++++++++++++++++++++ telemetry/dd/json/lval.go | 4 + telemetry/dd/json/lval_test.go | 1 + telemetry/dd/json/time.go | 12 ++ telemetry/dd/provider.go | 12 +- telemetry/dd/span.go | 12 +- 9 files changed, 479 insertions(+), 11 deletions(-) create mode 100644 telemetry/dd/json/attr.go create mode 100644 telemetry/dd/json/attr_test.go create mode 100644 telemetry/dd/json/ddjson.go create mode 100644 telemetry/dd/json/ddjson_test.go create mode 100644 telemetry/dd/json/time.go diff --git a/telemetry/dd/json/attr.go b/telemetry/dd/json/attr.go new file mode 100644 index 0000000..1061c13 --- /dev/null +++ b/telemetry/dd/json/attr.go @@ -0,0 +1,18 @@ +package json + +import "go.opentelemetry.io/otel/attribute" + +func attrSliceToMap(attributes []attribute.KeyValue) *map[string]any { + if len(attributes) == 0 { + return nil + } + attrs := make(map[string]any, len(attributes)) + for _, kv := range attributes { + attrs[string(kv.Key)] = kv.Value.AsInterface() + } + return &attrs +} + +func attrSetToMap(attributes attribute.Set) *map[string]any { + return attrSliceToMap(attributes.ToSlice()) +} diff --git a/telemetry/dd/json/attr_test.go b/telemetry/dd/json/attr_test.go new file mode 100644 index 0000000..9baef29 --- /dev/null +++ b/telemetry/dd/json/attr_test.go @@ -0,0 +1,117 @@ +package json + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/attribute" +) + +func Test_attrSliceToMap(t *testing.T) { + type args struct { + attributes []attribute.KeyValue + } + tests := []struct { + name string + args args + want *map[string]any + }{ + { + name: "string attribute", + args: args{attributes: []attribute.KeyValue{ + attribute.String("key", "value"), + }}, + want: &map[string]any{ + "key": "value", + }, + }, + { + name: "int64 attribute", + args: args{attributes: []attribute.KeyValue{ + attribute.Int64("key", 123), + }}, + want: &map[string]any{ + "key": int64(123), + }, + }, + { + name: "bool attribute", + args: args{attributes: []attribute.KeyValue{ + attribute.Bool("key", true), + }}, + want: &map[string]any{ + "key": true, + }, + }, + { + name: "float64 attribute", + args: args{attributes: []attribute.KeyValue{ + attribute.Float64("key", 123.456), + }}, + want: &map[string]any{ + "key": 123.456, + }, + }, + { + name: "multiple attributes", + args: args{attributes: []attribute.KeyValue{ + attribute.String("str", "value"), + attribute.Int64("int", 123), + attribute.Bool("bool", true), + attribute.Float64("float", 123.456), + }}, + want: &map[string]any{ + "str": "value", + "int": int64(123), + "bool": true, + "float": 123.456, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.EqualValuesf(t, tt.want, attrSliceToMap(tt.args.attributes), "attrSliceToMap(%v)", tt.args.attributes) + }) + } +} + +func Test_attrSetToMap(t *testing.T) { + type args struct { + attributes attribute.Set + } + tests := []struct { + name string + args args + want *map[string]any + }{ + { + name: "set with single attribute", + args: args{attributes: attribute.NewSet( + attribute.String("key", "value"), + )}, + want: &map[string]any{ + "key": "value", + }, + }, + { + name: "set with multiple attributes", + args: args{attributes: attribute.NewSet( + attribute.String("str", "value"), + attribute.Int64("int", 123), + attribute.Bool("bool", true), + attribute.Float64("float", 123.456), + )}, + want: &map[string]any{ + "str": "value", + "int": int64(123), + "bool": true, + "float": 123.456, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, attrSetToMap(tt.args.attributes), "attrSetToMap(%v)", tt.args.attributes) + }) + } +} diff --git a/telemetry/dd/json/ddjson.go b/telemetry/dd/json/ddjson.go new file mode 100644 index 0000000..390b14d --- /dev/null +++ b/telemetry/dd/json/ddjson.go @@ -0,0 +1,121 @@ +package json + +import ( + "bufio" + "context" + "encoding/binary" + "encoding/json" + "io" + "strconv" + + "go.opentelemetry.io/otel/log" + sdklog "go.opentelemetry.io/otel/sdk/log" + "go.opentelemetry.io/otel/sdk/resource" +) + +var _ sdklog.Exporter = &jsonExporter{} + +type jsonExporter struct { + flush func() error + encoder json.Encoder +} + +func NewJsonExporter(w io.Writer) sdklog.Exporter { + b := bufio.NewWriter(w) + return &jsonExporter{ + flush: b.Flush, + encoder: *json.NewEncoder(b), + } +} + +// Export implements log.Exporter. +func (j *jsonExporter) Export(_ context.Context, records []sdklog.Record) error { + for _, record := range records { + err := j.encoder.Encode(convert(record)) + if err != nil { + return err + } + } + return nil +} + +func convert(record sdklog.Record) jsonRecord { + entry := jsonRecord{ + Msg: record.Body().AsString(), + Level: record.Severity().String(), + Time: LogTime(record.Timestamp()), + ObservedTime: LogTime(record.ObservedTimestamp()), + Scope: jsonScope{ + SchemaURL: record.InstrumentationScope().SchemaURL, + Name: record.InstrumentationScope().Name, + Version: record.InstrumentationScope().Version, + Attributes: attrSetToMap(record.InstrumentationScope().Attributes), + }, + } + + res := record.Resource() + if &res != resource.Empty() { + entry.Resource = &jsonResource{ + SchemaURL: res.SchemaURL(), + } + entry.Resource.Attributes = attrSliceToMap(res.Attributes()) + } + + if record.TraceID().IsValid() { + id := record.TraceID() + entry.DDTraceID = strconv.FormatUint(binary.BigEndian.Uint64(id[8:]), 10) + } + + if record.SpanID().IsValid() { + id := record.SpanID() + entry.DDSpanID = strconv.FormatUint(binary.BigEndian.Uint64(id[:]), 10) + } + + if record.AttributesLen() > 0 { + attrs := make(map[string]value, record.AttributesLen()) + record.WalkAttributes(func(kv log.KeyValue) bool { + attrs[kv.Key] = newValue(kv.Value) + return true + }) + entry.Attributes = &attrs + } + return entry +} + +type jsonScope struct { + SchemaURL string `json:"schema_url"` + Name string `json:"name"` + Version string `json:"version"` + Attributes *map[string]any `json:"attributes,omitempty"` +} + +type jsonRecord struct { + Level string `json:"level"` + Msg string `json:"msg"` + Time LogTime `json:"time,omitempty"` + ObservedTime LogTime `json:"observed_time,omitempty"` + + Scope jsonScope `json:"scope,omitempty"` + Resource *jsonResource `json:"resource,omitempty"` + + DDTraceID string `json:"dd.trace_id,omitempty"` + DDSpanID string `json:"dd.span_id,omitempty"` + + Attributes *map[string]value `json:"attributes,omitempty"` +} + +type jsonResource struct { + SchemaURL string `json:"schema.url"` + Attributes *map[string]any `json:"attributes,omitempty"` +} + +// ForceFlush implements log.Exporter. +func (j *jsonExporter) ForceFlush(_ context.Context) error { + return j.flush() +} + +// Shutdown implements log.Exporter. +func (j *jsonExporter) Shutdown(_ context.Context) error { + j.encoder = *json.NewEncoder(io.Discard) + return j.flush() +} diff --git a/telemetry/dd/json/ddjson_test.go b/telemetry/dd/json/ddjson_test.go new file mode 100644 index 0000000..b4043f6 --- /dev/null +++ b/telemetry/dd/json/ddjson_test.go @@ -0,0 +1,193 @@ +package json + +import ( + "context" + "encoding/binary" + "encoding/json" + "fmt" + "log/slog" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/swaggest/assertjson" + "go.opentelemetry.io/contrib/bridges/otelslog" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/log" + sdklog "go.opentelemetry.io/otel/sdk/log" + "go.opentelemetry.io/otel/sdk/log/logtest" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + trace2 "go.opentelemetry.io/otel/trace" +) + +func JsonEqual(t *testing.T, expected, actual string) bool { + return assertjson.Equal(t, []byte(expected), []byte(actual)) +} + +func TestJsonExporter_Export(t *testing.T) { + logger, flush := setupLogger(t, resource.Default()) + t.Run("support simple logs", func(t *testing.T) { + logger.Info("info") + JsonEqual(t, ` +{ + "level": "INFO", + "msg": "info", + "time": "", + "observed_time": "", + "scope": "", + "resource": "", + "attributes": "" +}`, flush()) + }) + + t.Run("supports attributes", func(t *testing.T) { + logger.Info("info", + "string", "value", + "map", map[string]string{"key": "value"}, + "int", 1234, + "float", 123.456) + res := flush() + JsonEqual(t, ` +{ + "level": "INFO", + "msg": "info", + "time": "", + "observed_time": "", + "scope": "", + "resource": "", + "attributes": { + "code.filepath": "/Users/hadrien/Projects/member-lib/telemetry/dd/json/ddjson_test.go", + "code.function": "func2", + "code.lineno": "", + "code.namespace": "github.com/coopnorge/member-lib/telemetry/dd/json.TestJsonExporter_Export", + "float": 123.456, + "int": 1234, + "map": { + "key": "value" + }, + "string": "value" + } +}`, res) + }) + + t.Run("supports trace and span id from context", func(t *testing.T) { + otel.SetTracerProvider(sdktrace.NewTracerProvider()) + tracer := otel.Tracer("test-tracer") + ctx, span := tracer.Start(context.Background(), "span-name") + logger.DebugContext(ctx, "debug") + span.End() + JsonEqual(t, fmt.Sprintf(` + { + "dd.span_id": "%d", + "dd.trace_id": "%d", + "time": "", + "observed_time": "", + "attributes": "", + "resource": "", + "scope": "", + "level": "DEBUG", + "msg": "debug" + }`, datadogSpanID(span.SpanContext().SpanID()), datadogTraceID(span.SpanContext().TraceID())), flush()) + }) +} + +func datadogSpanID(id trace2.SpanID) uint64 { + return binary.BigEndian.Uint64(id[:]) +} + +func datadogTraceID(id trace2.TraceID) uint64 { + return binary.BigEndian.Uint64(id[8:]) +} + +func setupLogger(t *testing.T, res *resource.Resource) (*slog.Logger, func() string) { + var buf strings.Builder + exporter := NewJsonExporter(&buf) + provider := sdklog.NewLoggerProvider( + sdklog.WithResource(res), + sdklog.WithProcessor(sdklog.NewSimpleProcessor(exporter)), + ) + logger := otelslog.NewLogger("test", otelslog.WithLoggerProvider(provider), otelslog.WithSource(true)) + return logger, func() string { + require.NoError(t, exporter.ForceFlush(context.Background())) + defer buf.Reset() + return buf.String() + } +} + +func Test_jsonRecord(t *testing.T) { + r := jsonRecord{ + Level: "LEVEL", + Msg: "MSD", + Time: LogTime(time.UnixMilli(123456789)), + ObservedTime: LogTime(time.UnixMilli(987654321)), + DDTraceID: "", + DDSpanID: "", + Attributes: &map[string]value{ + "test": newValue(log.MapValue(log.Int("key", 123)))}, + } + + var buf strings.Builder + e := json.NewEncoder(&buf) + e.SetIndent("", " ") + + err := e.Encode(r) + require.NoError(t, err) + t.Log("json result:", buf.String()) +} + +func Test_convert(t *testing.T) { + r := logtest.RecordFactory{ + Timestamp: time.UnixMilli(12340), + ObservedTimestamp: time.UnixMilli(43210), + Severity: log.SeverityError, + SeverityText: "Error", + Body: log.StringValue("message"), + Attributes: []log.KeyValue{ + log.Int("int", 1234), + log.Int64("int64", 4321), + log.Float64("float64", 3.1415), + log.Bool("bool", true), + log.Empty("empty"), + log.Map("map", log.Int("int", 1234)), + log.Slice("slice", log.IntValue(1234)), + }, + }.NewRecord() + + val := convert(r) + var buf strings.Builder + e := json.NewEncoder(&buf) + e.SetIndent("", " ") + + err := e.Encode(val) + require.NoError(t, err) + JsonEqual(t, ` +{ + "level": "ERROR", + "msg": "message", + "time": "1970-01-01T00:00:12Z", + "observed_time": "1970-01-01T00:00:43Z", + "scope": { + "schema_url": "", + "name": "", + "version": "" + }, + "resource": { + "schema.url": "" + }, + "attributes": { + "bool": true, + "empty": null, + "float64": 3.1415, + "int": 1234, + "int64": 4321, + "map": { + "int": 1234 + }, + "slice": [ + 1234 + ] + } + }`, buf.String()) +} diff --git a/telemetry/dd/json/lval.go b/telemetry/dd/json/lval.go index f1af8e4..a8e78e3 100644 --- a/telemetry/dd/json/lval.go +++ b/telemetry/dd/json/lval.go @@ -11,6 +11,10 @@ type value struct { log.Value } +func newValue(v log.Value) value { + return value{Value: v} +} + // MarshalJSON implements a custom marshal function to encode log.Value. func (v value) MarshalJSON() ([]byte, error) { var val any diff --git a/telemetry/dd/json/lval_test.go b/telemetry/dd/json/lval_test.go index d0663bd..7b8bdb9 100644 --- a/telemetry/dd/json/lval_test.go +++ b/telemetry/dd/json/lval_test.go @@ -24,6 +24,7 @@ func Test_value_MarshalJSON(t *testing.T) { {"Bytes", log.BytesValue([]byte("bytes")), `"Ynl0ZXM="`, assert.NoError}, {"Map", log.MapValue(log.String("key", "value")), `{"key":"value"}`, assert.NoError}, {"Map of map", log.MapValue(log.Map("key1", log.String("key2", "value"))), `{"key1":{"key2":"value"}}`, assert.NoError}, + {"Map of map of map", log.MapValue(log.Map("key1", log.Map("key2", log.String("key3", "value")))), `{"key1":{"key2":{"key3":"value"}}}`, assert.NoError}, {"Slice", log.SliceValue(log.StringValue("value"), log.IntValue(1234)), `["value",1234]`, assert.NoError}, {"Slice of slice", log.SliceValue(log.SliceValue(log.StringValue("value")), log.IntValue(1234)), `[["value"],1234]`, assert.NoError}, } diff --git a/telemetry/dd/json/time.go b/telemetry/dd/json/time.go new file mode 100644 index 0000000..316d94d --- /dev/null +++ b/telemetry/dd/json/time.go @@ -0,0 +1,12 @@ +package json + +import ( + "fmt" + "time" +) + +type LogTime time.Time + +func (t LogTime) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, time.Time(t).UTC().Format(time.RFC3339))), nil +} diff --git a/telemetry/dd/provider.go b/telemetry/dd/provider.go index 70c96f8..a7f7b3d 100644 --- a/telemetry/dd/provider.go +++ b/telemetry/dd/provider.go @@ -4,14 +4,12 @@ import ( "context" "errors" "fmt" - - "github.com/DataDog/datadog-go/v5/statsd" - "os" "strings" "time" - "go.opentelemetry.io/otel/exporters/stdout/stdoutlog" + "github.com/DataDog/datadog-go/v5/statsd" + ljson "github.com/coopnorge/member-lib/telemetry/dd/json" sdklog "go.opentelemetry.io/otel/sdk/log" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" @@ -86,11 +84,7 @@ func Exporters(_ context.Context, res *resource.Resource, traceURL, metricURL st return nil, nil, nil, err } - le, err = stdoutlog.New() - if err != nil { - return nil, nil, nil, err - } - + le = ljson.NewJsonExporter(os.Stderr) return te, me, le, nil } diff --git a/telemetry/dd/span.go b/telemetry/dd/span.go index 9578c78..89b799f 100644 --- a/telemetry/dd/span.go +++ b/telemetry/dd/span.go @@ -8,15 +8,23 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" ) +// Passing a "fake" child relationship so that the trace id generation +// doesn't kick in. If not, the datadog lib cannot connect the traces. func getParent(span trace.ReadOnlySpan) ddtrace.SpanContextW3C { if parent := span.Parent(); parent.IsValid() { return ctxWrapper{otelCtx: parent} } - // Passing a "fake" child relationship so that the trace id generation - // doesn't kick in. If not, the datadog lib return noParent{ctxWrapper{span.SpanContext()}} } +func DatadogSpanID(id trace2.SpanID) uint64 { + return binary.BigEndian.Uint64(id[8:]) +} + +func DatadogTraceID(id trace2.TraceID) uint64 { + return binary.BigEndian.Uint64(id[:]) +} + var _ ddtrace.SpanContextW3C = &ctxWrapper{} // ctxWrapper converts open telemetry span context to ddtrace.SpanContextW3C. From c64b55ea5d787ce7eb23af41504f6163e7983c82 Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 15:18:54 +0100 Subject: [PATCH 3/8] Remove unused code --- telemetry/dd/span.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/telemetry/dd/span.go b/telemetry/dd/span.go index 89b799f..9c6c9d0 100644 --- a/telemetry/dd/span.go +++ b/telemetry/dd/span.go @@ -17,14 +17,6 @@ func getParent(span trace.ReadOnlySpan) ddtrace.SpanContextW3C { return noParent{ctxWrapper{span.SpanContext()}} } -func DatadogSpanID(id trace2.SpanID) uint64 { - return binary.BigEndian.Uint64(id[8:]) -} - -func DatadogTraceID(id trace2.TraceID) uint64 { - return binary.BigEndian.Uint64(id[:]) -} - var _ ddtrace.SpanContextW3C = &ctxWrapper{} // ctxWrapper converts open telemetry span context to ddtrace.SpanContextW3C. From 25a7941fcbec7e3e5bef5875d72b2b33505ad5c2 Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 15:24:01 +0100 Subject: [PATCH 4/8] Fix linter errors --- telemetry/dd/json/attr.go | 6 +++--- telemetry/dd/json/attr_test.go | 18 ++++++++--------- telemetry/dd/json/ddjson.go | 24 +++++++++++------------ telemetry/dd/json/ddjson_test.go | 6 +++--- telemetry/dd/json/time.go | 2 +- telemetry/dd/provider.go | 2 +- telemetry/go.mod | 21 +++++++++++++++----- telemetry/go.sum | 33 ++++++++++++++++++++++++++++++++ 8 files changed, 78 insertions(+), 34 deletions(-) diff --git a/telemetry/dd/json/attr.go b/telemetry/dd/json/attr.go index 1061c13..12b017e 100644 --- a/telemetry/dd/json/attr.go +++ b/telemetry/dd/json/attr.go @@ -2,7 +2,7 @@ package json import "go.opentelemetry.io/otel/attribute" -func attrSliceToMap(attributes []attribute.KeyValue) *map[string]any { +func attrSliceToMap(attributes []attribute.KeyValue) map[string]any { if len(attributes) == 0 { return nil } @@ -10,9 +10,9 @@ func attrSliceToMap(attributes []attribute.KeyValue) *map[string]any { for _, kv := range attributes { attrs[string(kv.Key)] = kv.Value.AsInterface() } - return &attrs + return attrs } -func attrSetToMap(attributes attribute.Set) *map[string]any { +func attrSetToMap(attributes attribute.Set) map[string]any { return attrSliceToMap(attributes.ToSlice()) } diff --git a/telemetry/dd/json/attr_test.go b/telemetry/dd/json/attr_test.go index 9baef29..e1f6909 100644 --- a/telemetry/dd/json/attr_test.go +++ b/telemetry/dd/json/attr_test.go @@ -14,14 +14,14 @@ func Test_attrSliceToMap(t *testing.T) { tests := []struct { name string args args - want *map[string]any + want map[string]any }{ { name: "string attribute", args: args{attributes: []attribute.KeyValue{ attribute.String("key", "value"), }}, - want: &map[string]any{ + want: map[string]any{ "key": "value", }, }, @@ -30,7 +30,7 @@ func Test_attrSliceToMap(t *testing.T) { args: args{attributes: []attribute.KeyValue{ attribute.Int64("key", 123), }}, - want: &map[string]any{ + want: map[string]any{ "key": int64(123), }, }, @@ -39,7 +39,7 @@ func Test_attrSliceToMap(t *testing.T) { args: args{attributes: []attribute.KeyValue{ attribute.Bool("key", true), }}, - want: &map[string]any{ + want: map[string]any{ "key": true, }, }, @@ -48,7 +48,7 @@ func Test_attrSliceToMap(t *testing.T) { args: args{attributes: []attribute.KeyValue{ attribute.Float64("key", 123.456), }}, - want: &map[string]any{ + want: map[string]any{ "key": 123.456, }, }, @@ -60,7 +60,7 @@ func Test_attrSliceToMap(t *testing.T) { attribute.Bool("bool", true), attribute.Float64("float", 123.456), }}, - want: &map[string]any{ + want: map[string]any{ "str": "value", "int": int64(123), "bool": true, @@ -82,14 +82,14 @@ func Test_attrSetToMap(t *testing.T) { tests := []struct { name string args args - want *map[string]any + want map[string]any }{ { name: "set with single attribute", args: args{attributes: attribute.NewSet( attribute.String("key", "value"), )}, - want: &map[string]any{ + want: map[string]any{ "key": "value", }, }, @@ -101,7 +101,7 @@ func Test_attrSetToMap(t *testing.T) { attribute.Bool("bool", true), attribute.Float64("float", 123.456), )}, - want: &map[string]any{ + want: map[string]any{ "str": "value", "int": int64(123), "bool": true, diff --git a/telemetry/dd/json/ddjson.go b/telemetry/dd/json/ddjson.go index 390b14d..5ac7027 100644 --- a/telemetry/dd/json/ddjson.go +++ b/telemetry/dd/json/ddjson.go @@ -20,7 +20,7 @@ type jsonExporter struct { encoder json.Encoder } -func NewJsonExporter(w io.Writer) sdklog.Exporter { +func NewJSONExporter(w io.Writer) sdklog.Exporter { b := bufio.NewWriter(w) return &jsonExporter{ flush: b.Flush, @@ -30,8 +30,8 @@ func NewJsonExporter(w io.Writer) sdklog.Exporter { // Export implements log.Exporter. func (j *jsonExporter) Export(_ context.Context, records []sdklog.Record) error { - for _, record := range records { - err := j.encoder.Encode(convert(record)) + for i := 0; i < len(records); i++ { + err := j.encoder.Encode(convert(&records[i])) if err != nil { return err } @@ -39,7 +39,7 @@ func (j *jsonExporter) Export(_ context.Context, records []sdklog.Record) error return nil } -func convert(record sdklog.Record) jsonRecord { +func convert(record *sdklog.Record) jsonRecord { entry := jsonRecord{ Msg: record.Body().AsString(), Level: record.Severity().String(), @@ -77,16 +77,16 @@ func convert(record sdklog.Record) jsonRecord { attrs[kv.Key] = newValue(kv.Value) return true }) - entry.Attributes = &attrs + entry.Attributes = attrs } return entry } type jsonScope struct { - SchemaURL string `json:"schema_url"` - Name string `json:"name"` - Version string `json:"version"` - Attributes *map[string]any `json:"attributes,omitempty"` + SchemaURL string `json:"schema_url"` + Name string `json:"name"` + Version string `json:"version"` + Attributes map[string]any `json:"attributes,omitempty"` } type jsonRecord struct { @@ -101,12 +101,12 @@ type jsonRecord struct { DDTraceID string `json:"dd.trace_id,omitempty"` DDSpanID string `json:"dd.span_id,omitempty"` - Attributes *map[string]value `json:"attributes,omitempty"` + Attributes map[string]value `json:"attributes,omitempty"` } type jsonResource struct { - SchemaURL string `json:"schema.url"` - Attributes *map[string]any `json:"attributes,omitempty"` + SchemaURL string `json:"schema.url"` + Attributes map[string]any `json:"attributes,omitempty"` } // ForceFlush implements log.Exporter. diff --git a/telemetry/dd/json/ddjson_test.go b/telemetry/dd/json/ddjson_test.go index b4043f6..d8380b5 100644 --- a/telemetry/dd/json/ddjson_test.go +++ b/telemetry/dd/json/ddjson_test.go @@ -103,7 +103,7 @@ func datadogTraceID(id trace2.TraceID) uint64 { func setupLogger(t *testing.T, res *resource.Resource) (*slog.Logger, func() string) { var buf strings.Builder - exporter := NewJsonExporter(&buf) + exporter := NewJSONExporter(&buf) provider := sdklog.NewLoggerProvider( sdklog.WithResource(res), sdklog.WithProcessor(sdklog.NewSimpleProcessor(exporter)), @@ -124,7 +124,7 @@ func Test_jsonRecord(t *testing.T) { ObservedTime: LogTime(time.UnixMilli(987654321)), DDTraceID: "", DDSpanID: "", - Attributes: &map[string]value{ + Attributes: map[string]value{ "test": newValue(log.MapValue(log.Int("key", 123)))}, } @@ -155,7 +155,7 @@ func Test_convert(t *testing.T) { }, }.NewRecord() - val := convert(r) + val := convert(&r) var buf strings.Builder e := json.NewEncoder(&buf) e.SetIndent("", " ") diff --git a/telemetry/dd/json/time.go b/telemetry/dd/json/time.go index 316d94d..3b3ec76 100644 --- a/telemetry/dd/json/time.go +++ b/telemetry/dd/json/time.go @@ -8,5 +8,5 @@ import ( type LogTime time.Time func (t LogTime) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%s"`, time.Time(t).UTC().Format(time.RFC3339))), nil + return []byte(fmt.Sprintf(`%q`, time.Time(t).UTC().Format(time.RFC3339))), nil } diff --git a/telemetry/dd/provider.go b/telemetry/dd/provider.go index a7f7b3d..6a1f777 100644 --- a/telemetry/dd/provider.go +++ b/telemetry/dd/provider.go @@ -84,7 +84,7 @@ func Exporters(_ context.Context, res *resource.Resource, traceURL, metricURL st return nil, nil, nil, err } - le = ljson.NewJsonExporter(os.Stderr) + le = ljson.NewJSONExporter(os.Stderr) return te, me, le, nil } diff --git a/telemetry/go.mod b/telemetry/go.mod index 63858a4..4aa7bc3 100644 --- a/telemetry/go.mod +++ b/telemetry/go.mod @@ -5,20 +5,30 @@ go 1.23 require ( github.com/DataDog/datadog-go/v5 v5.5.0 github.com/iancoleman/strcase v0.3.0 - github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel v1.32.0 + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/otel v1.33.0 go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 - go.opentelemetry.io/otel/log v0.8.0 + go.opentelemetry.io/otel/log v0.9.0 go.opentelemetry.io/otel/sdk v1.32.0 go.opentelemetry.io/otel/sdk/log v0.8.0 go.opentelemetry.io/otel/sdk/metric v1.32.0 - go.opentelemetry.io/otel/trace v1.32.0 + go.opentelemetry.io/otel/trace v1.33.0 gopkg.in/DataDog/dd-trace-go.v1 v1.69.1 ) +require ( + github.com/bool64/shared v0.1.5 // indirect + github.com/iancoleman/orderedmap v0.3.0 // indirect + github.com/sergi/go-diff v1.1.0 // indirect + github.com/swaggest/assertjson v1.9.0 // indirect + github.com/yudai/gojsondiff v1.0.0 // indirect + github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect +) + require ( github.com/DataDog/appsec-internal-go v1.8.0 // indirect github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect @@ -48,8 +58,9 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect github.com/tinylib/msgp v1.2.1 // indirect + go.opentelemetry.io/contrib/bridges/otelslog v0.8.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/mod v0.18.0 // indirect diff --git a/telemetry/go.sum b/telemetry/go.sum index d7f879f..d035f23 100644 --- a/telemetry/go.sum +++ b/telemetry/go.sum @@ -19,6 +19,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= +github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -63,12 +65,17 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9 github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= +github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -103,6 +110,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -113,17 +122,32 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ= +github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/tinylib/msgp v1.2.1 h1:6ypy2qcCznxpP4hpORzhtXyTqrBs7cfM9MCCWY8zsmU= github.com/tinylib/msgp v1.2.1/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/bridges/otelslog v0.8.0 h1:G3sKsNueSdxuACINFxKrQeimAIst0A5ytA2YJH+3e1c= +go.opentelemetry.io/contrib/bridges/otelslog v0.8.0/go.mod h1:ptJm3wizguEPurZgarDAwOeX7O0iMR7l+QvIVenhYdE= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= @@ -136,8 +160,12 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg= go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/log v0.9.0 h1:0OiWRefqJ2QszpCiqwGO0u9ajMPe17q6IscQvvp3czY= +go.opentelemetry.io/otel/log v0.9.0/go.mod h1:WPP4OJ+RBkQ416jrFCQFuFKtXKD6mOoYCQm6ykK8VaU= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= @@ -146,6 +174,8 @@ go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiy go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -208,8 +238,11 @@ google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt gopkg.in/DataDog/dd-trace-go.v1 v1.69.1 h1:grTElrPaCfxUsrJjyPLHlVPbmlKVzWMxVdcBrGZSzEk= gopkg.in/DataDog/dd-trace-go.v1 v1.69.1/go.mod h1:U9AOeBHNAL95JXcd/SPf4a7O5GNeF/yD13sJtli/yaU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 149c83e641696f82f59a890b5b76039e4d9e85b0 Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 17:13:09 +0100 Subject: [PATCH 5/8] Fix assertion --- telemetry/dd/json/ddjson_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telemetry/dd/json/ddjson_test.go b/telemetry/dd/json/ddjson_test.go index d8380b5..4255cff 100644 --- a/telemetry/dd/json/ddjson_test.go +++ b/telemetry/dd/json/ddjson_test.go @@ -58,7 +58,7 @@ func TestJsonExporter_Export(t *testing.T) { "scope": "", "resource": "", "attributes": { - "code.filepath": "/Users/hadrien/Projects/member-lib/telemetry/dd/json/ddjson_test.go", + "code.filepath": "", "code.function": "func2", "code.lineno": "", "code.namespace": "github.com/coopnorge/member-lib/telemetry/dd/json.TestJsonExporter_Export", From 4d89807cd4153a0d247e5bdac4c77c9575ec9bf4 Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 18:43:27 +0100 Subject: [PATCH 6/8] Flush after one batch --- telemetry/dd/json/ddjson.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telemetry/dd/json/ddjson.go b/telemetry/dd/json/ddjson.go index 5ac7027..7ea4fa7 100644 --- a/telemetry/dd/json/ddjson.go +++ b/telemetry/dd/json/ddjson.go @@ -36,7 +36,7 @@ func (j *jsonExporter) Export(_ context.Context, records []sdklog.Record) error return err } } - return nil + return j.flush() } func convert(record *sdklog.Record) jsonRecord { From 9685fe7c73a6d9fac93efcb45504f10790c753fe Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 18:43:59 +0100 Subject: [PATCH 7/8] Remove processor --- telemetry/dd/log.go | 39 --------------------------------------- telemetry/dd/provider.go | 5 ++--- 2 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 telemetry/dd/log.go diff --git a/telemetry/dd/log.go b/telemetry/dd/log.go deleted file mode 100644 index f21abe8..0000000 --- a/telemetry/dd/log.go +++ /dev/null @@ -1,39 +0,0 @@ -package dd - -import ( - "context" - "strconv" - - "go.opentelemetry.io/otel/trace" - - "go.opentelemetry.io/otel/log" - sdklog "go.opentelemetry.io/otel/sdk/log" -) - -var _ sdklog.Processor = &ddProcessor{} - -type ddProcessor struct { -} - -func (p *ddProcessor) Shutdown(_ context.Context) error { - return nil -} - -func (p *ddProcessor) ForceFlush(_ context.Context) error { - return nil -} - -func (p *ddProcessor) OnEmit(ctx context.Context, record *sdklog.Record) error { - if span := trace.SpanFromContext(ctx); span.IsRecording() { - osCtx := ctxWrapper{span.SpanContext()} - record.AddAttributes( - log.String("dd.span_id", strconv.FormatUint(osCtx.SpanID(), 10)), - log.String("dd.trace_id", strconv.FormatUint(osCtx.TraceID(), 10)), - ) - } - return nil -} - -func NewDatadogProcessor() sdklog.Processor { - return &ddProcessor{} -} diff --git a/telemetry/dd/provider.go b/telemetry/dd/provider.go index 6a1f777..3f92bc5 100644 --- a/telemetry/dd/provider.go +++ b/telemetry/dd/provider.go @@ -26,7 +26,7 @@ func Providers(ctx context.Context, res *resource.Resource, traceURL, metricURL tp = sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), - sdktrace.WithBatcher(te, sdktrace.WithBatchTimeout(2*time.Second)), + sdktrace.WithBatcher(te, sdktrace.WithBatchTimeout(1*time.Second)), sdktrace.WithResource(res), ) @@ -36,8 +36,7 @@ func Providers(ctx context.Context, res *resource.Resource, traceURL, metricURL ) lp = sdklog.NewLoggerProvider( - sdklog.WithProcessor(NewDatadogProcessor()), - sdklog.WithProcessor(sdklog.NewBatchProcessor(le)), + sdklog.WithProcessor(sdklog.NewBatchProcessor(le, sdklog.WithExportInterval(1*time.Second))), sdklog.WithResource(res), ) From dd151d968a1f6900d904cdd0eb27f15ba5c6eedc Mon Sep 17 00:00:00 2001 From: Hadrien Kohl Date: Tue, 14 Jan 2025 18:44:34 +0100 Subject: [PATCH 8/8] Rename ddjson.go to exporter.go --- telemetry/dd/json/{ddjson.go => exporter.go} | 0 telemetry/dd/json/{ddjson_test.go => exporter_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename telemetry/dd/json/{ddjson.go => exporter.go} (100%) rename telemetry/dd/json/{ddjson_test.go => exporter_test.go} (100%) diff --git a/telemetry/dd/json/ddjson.go b/telemetry/dd/json/exporter.go similarity index 100% rename from telemetry/dd/json/ddjson.go rename to telemetry/dd/json/exporter.go diff --git a/telemetry/dd/json/ddjson_test.go b/telemetry/dd/json/exporter_test.go similarity index 100% rename from telemetry/dd/json/ddjson_test.go rename to telemetry/dd/json/exporter_test.go