From 255f1673c5f30868144aa7b395b76a6f7bcf0081 Mon Sep 17 00:00:00 2001 From: Louis FRADIN Date: Fri, 12 Jun 2026 18:18:11 +0200 Subject: [PATCH] feat(extensions): add LogInfosFromContext helper (#133) Custom loggers received the controller's contextual data (channel, correlation ID, direction, ...) only through individual context keys, making them tedious to extract. Add LogInfosFromContext to pull all of them into a []LogInfo in one call. Non-breaking and additive; the larger refactor of moving this off the context entirely is left for a separate change. Addresses #133 Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 11 +++++++++++ pkg/extensions/logger.go | 25 ++++++++++++++++++++++++ pkg/extensions/logger_test.go | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 pkg/extensions/logger_test.go diff --git a/README.md b/README.md index 4cb6a046..942f5be3 100644 --- a/README.md +++ b/README.md @@ -594,6 +594,17 @@ ctrl, _ := NewAppController( ) ``` +The controller stores contextual information (channel, correlation ID, +direction, …) in the `context.Context` passed to your logger. To retrieve it +without unwrapping each key manually, use `extensions.LogInfosFromContext`: + +```golang +func (logger SimpleLogger) Info(ctx context.Context, msg string, info ...extensions.LogInfo) { + info = append(info, extensions.LogInfosFromContext(ctx)...) + // ... log msg with info +} +``` + ### Versioning If you are in need to do a migration or support multiple versions of your diff --git a/pkg/extensions/logger.go b/pkg/extensions/logger.go index 31d1b721..ae9018c8 100644 --- a/pkg/extensions/logger.go +++ b/pkg/extensions/logger.go @@ -8,6 +8,31 @@ type LogInfo struct { Value any } +// LogInfosFromContext extracts the asyncapi-codegen values carried in the +// context (provider, channel, direction, correlation ID, broker message and +// specification version) into a slice of LogInfo. +// +// It is a convenience for custom Logger implementations that want this +// information without having to know and unwrap each context key individually. +func LogInfosFromContext(ctx context.Context) []LogInfo { + var infos []LogInfo + + add := func(key string, ctxKey ContextKey) { + IfContextSetWith(ctx, ctxKey, func(value any) { + infos = append(infos, LogInfo{Key: key, Value: value}) + }) + } + + add("version", ContextKeyIsVersion) + add("provider", ContextKeyIsProvider) + add("channel", ContextKeyIsChannel) + add("direction", ContextKeyIsDirection) + add("correlationID", ContextKeyIsCorrelationID) + add("brokerMessage", ContextKeyIsBrokerMessage) + + return infos +} + // Logger is the interface that must be implemented by a logger. type Logger interface { // Info logs information based on a message and key-value elements diff --git a/pkg/extensions/logger_test.go b/pkg/extensions/logger_test.go new file mode 100644 index 00000000..4debdc84 --- /dev/null +++ b/pkg/extensions/logger_test.go @@ -0,0 +1,36 @@ +package extensions + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestLogInfosFromContext ensures the helper extracts the asyncapi-codegen +// context values into LogInfo entries, and only the ones that are set (#133). +func TestLogInfosFromContext(t *testing.T) { + ctx := context.Background() + ctx = context.WithValue(ctx, ContextKeyIsChannel, "my-channel") + ctx = context.WithValue(ctx, ContextKeyIsCorrelationID, "corr-1") + + infos := LogInfosFromContext(ctx) + + got := make(map[string]any, len(infos)) + for _, info := range infos { + got[info.Key] = info.Value + } + + assert.Equal(t, "my-channel", got["channel"]) + assert.Equal(t, "corr-1", got["correlationID"]) + + // Values that were not set must not be present. + _, hasProvider := got["provider"] + assert.False(t, hasProvider) + assert.Len(t, infos, 2) +} + +// TestLogInfosFromContextEmpty ensures an empty context yields no LogInfo. +func TestLogInfosFromContextEmpty(t *testing.T) { + assert.Empty(t, LogInfosFromContext(context.Background())) +}