diff --git a/CHANGELOG.md b/CHANGELOG.md index f0fedffccc5..8922fe55a3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Add `Values` method to `HeaderCarrier` to implement the new `ValuesGetter` interface in `go.opentelemetry.io/otel/propagation`. (#5973) - Update `Baggage` in `go.opentelemetry.io/otel/propagation` to retrieve multiple values for a key when the carrier implements `ValuesGetter`. (#5973) - Add `AssertEqual` function in `go.opentelemetry.io/otel/log/logtest`. (#6662) +- Add `Transform` option in `go.opentelemetry.io/otel/log/logtest`. (#6794) ### Removed diff --git a/log/logtest/assert.go b/log/logtest/assert.go index 859c170d94a..4bb95212f7e 100644 --- a/log/logtest/assert.go +++ b/log/logtest/assert.go @@ -25,11 +25,16 @@ type testingT interface { Errorf(format string, args ...any) } -func assertEqual[T Recording | Record](t testingT, want, got T, _ ...AssertOption) bool { +func assertEqual[T Recording | Record](t testingT, want, got T, opts ...AssertOption) bool { if h, ok := t.(interface{ Helper() }); ok { h.Helper() } + var cfg assertConfig + for _, opt := range opts { + cfg = opt.apply(cfg) + } + cmpOpts := []cmp.Option{ cmp.Comparer(func(x, y context.Context) bool { return x == y }), // Compare context. cmpopts.SortSlices( @@ -37,6 +42,7 @@ func assertEqual[T Recording | Record](t testingT, want, got T, _ ...AssertOptio ), // Unordered compare of the key values. cmpopts.EquateEmpty(), // Empty and nil collections are equal. } + cmpOpts = append(cmpOpts, cfg.cmpOpts...) if diff := cmp.Diff(want, got, cmpOpts...); diff != "" { t.Errorf("mismatch (-want +got):\n%s", diff) @@ -45,9 +51,27 @@ func assertEqual[T Recording | Record](t testingT, want, got T, _ ...AssertOptio return true } -type assertConfig struct{} +type assertConfig struct { + cmpOpts []cmp.Option +} // AssertOption allows for fine grain control over how AssertEqual operates. type AssertOption interface { apply(cfg assertConfig) assertConfig } + +type fnOption func(cfg assertConfig) assertConfig + +func (fn fnOption) apply(cfg assertConfig) assertConfig { + return fn(cfg) +} + +// Transform applies a transformation f function that +// converts values of a certain type into that of another. +// f must not mutate A in any way. +func Transform[A, B any](f func(A) B) AssertOption { + return fnOption(func(cfg assertConfig) assertConfig { + cfg.cmpOpts = append(cfg.cmpOpts, cmp.Transformer("", f)) + return cfg + }) +} diff --git a/log/logtest/assert_test.go b/log/logtest/assert_test.go index bf3e4b499d1..9eb27a41361 100644 --- a/log/logtest/assert_test.go +++ b/log/logtest/assert_test.go @@ -155,6 +155,22 @@ func TestAssertEqualRecord(t *testing.T) { }, want: false, }, + { + name: "Transform to ignore timestamps", + a: Record{ + Attributes: []log.KeyValue{log.Int("n", 1), log.String("foo", "bar")}, + }, + b: Record{ + Timestamp: y2k, + Attributes: []log.KeyValue{log.String("foo", "bar"), log.Int("n", 1)}, + }, + opts: []AssertOption{ + Transform(func(time.Time) time.Time { + return time.Time{} + }), + }, + want: true, + }, } for _, tc := range tests { diff --git a/log/logtest/example_test.go b/log/logtest/example_test.go new file mode 100644 index 00000000000..3ff69d460f1 --- /dev/null +++ b/log/logtest/example_test.go @@ -0,0 +1,55 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package logtest_test + +import ( + "context" + "testing" + "time" + + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/logtest" +) + +func Example() { + t := &testing.T{} // Provided by testing framework. + + // Create a recorder. + rec := logtest.NewRecorder() + + // Emit a log record (code under test). + l := rec.Logger("Example") + ctx := context.Background() + r := log.Record{} + r.SetTimestamp(time.Now()) + r.SetSeverity(log.SeverityInfo) + r.SetBody(log.StringValue("Hello there")) + r.AddAttributes(log.String("foo", "bar")) + r.AddAttributes(log.Int("n", 1)) + l.Emit(ctx, r) + + // Verify that the expected and actual log records match. + want := logtest.Recording{ + logtest.Scope{Name: "Example"}: []logtest.Record{ + { + Context: context.Background(), + Severity: log.SeverityInfo, + Body: log.StringValue("Hello there"), + Attributes: []log.KeyValue{ + log.Int("n", 1), + log.String("foo", "bar"), + }, + }, + }, + } + got := rec.Result() + logtest.AssertEqual(t, want, got, + // Ignore Timestamps. + logtest.Transform(func(time.Time) time.Time { + return time.Time{} + }), + ) + // Output: + // +}