Skip to content

feat: add ContextKey[T], flow.Logger and log interceptors#78

Merged
xuxife merged 3 commits into
mainfrom
contrib-logger
May 15, 2026
Merged

feat: add ContextKey[T], flow.Logger and log interceptors#78
xuxife merged 3 commits into
mainfrom
contrib-logger

Conversation

@xuxife
Copy link
Copy Markdown
Collaborator

@xuxife xuxife commented May 15, 2026

Summary

Introduces a tiny, type-safe convention for flowing cross-cutting values (logger, identity, k8s client, ...) through `context.Context` across go-workflow and any user / contrib code that participates in a Workflow.

  • `ContextKey[T]` — generic helper with `With` / `From` / `FromOr` methods. Each value type declares one canonical package-level `ContextKey[T]` var; uniqueness is by `T`, so two such vars of the same `T` deliberately share the same key.
  • `flow.Logger` — canonical `ContextKey[*slog.Logger]` published by the main package so contrib code does not need to invent its own key for the conventional "the logger lives in ctx" pattern.
  • `flow.LogStepFields(extra...)` — `StepInterceptor` that derives the ctx logger with `step=<flow.String(step)>` plus any caller-supplied extras, then re-injects so `Steper.Do` reads the enriched logger via `flow.Logger.FromOr`.
  • `flow.LogAttemptField()` — `AttemptInterceptor` counterpart that binds `attempt=N` inside the retry loop.

Motivation: `contrib/azure`-style packages should not each invent their own context key for logger / identity / etc.; this gives the whole ecosystem one shared, type-safe convention with zero boilerplate per value type.

What's in the diff

File Purpose
`context.go` + `context_test.go` Generic `ContextKey[T]` helper
`logger.go` + `logger_test.go` `flow.Logger` key + the two interceptors
`example/14_context_values_test.go` Three runnable `Example*` functions covering custom `ContextKey`, the logger key, and the interceptors
`openspec/specs/context-values/spec.md` New capability spec (passes `openspec validate --strict`)
`README.md` Short "Passing values through `context.Context`" section pointing at godoc + example

Test plan

  • `go test ./...` passes (existing tests untouched)
  • New `Example*` functions verify exact output via `// Output:` blocks
  • `openspec validate context-values --specs --strict` passes
  • CI green

Out of scope

The other 9 capability specs in `openspec/specs/` fail `--strict` validation (missing `## Purpose` and/or `SHALL`/`MUST` keywords), but those are pre-existing issues unrelated to this change and should be addressed in a separate PR.

🤖 Generated with Claude Code

Xingfei Xu and others added 3 commits May 15, 2026 02:24
Introduce a tiny, type-safe convention for flowing cross-cutting values
(logger, identity, k8s client, ...) through context.Context across
go-workflow and any user / contrib code that participates in a Workflow:

- ContextKey[T] generic helper with With / From / FromOr methods. Each
  value type declares one canonical package-level ContextKey[T] var;
  uniqueness is by T so two such vars of the same T deliberately share
  the same key.
- flow.Logger: canonical ContextKey[*slog.Logger] published by the main
  package so contrib code does not have to invent its own key for the
  conventional "the logger lives in ctx" pattern.
- flow.LogStepFields(extra...) StepInterceptor: derives the ctx logger
  with step=<flow.String(step)> plus any caller-supplied extras, then
  re-injects so Steper.Do reads the enriched logger via flow.Logger.FromOr.
- flow.LogAttemptField() AttemptInterceptor: same pattern, binds
  attempt=N inside the retry loop.

Also adds the corresponding openspec capability (context-values) and a
runnable example file (example/14_context_values_test.go) demonstrating
the three usages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trim 14_context_values_test.go down to the two minimal scenarios that
actually motivate the API: a string-typed UserKey to demonstrate the
ContextKey[T] pattern itself, and a single Logger example wiring both
LogStepFields and LogAttemptField. Drops the JSON-capture helper, the
contrived Identity struct, and the third Example that overlapped with
the second one — the point of these examples is "passing values through
ctx is the everyday pattern in go-workflow", not Azure / k8s specifics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nciple

Passing values through ctx is one of the most common things users do in
go-workflow — almost every step in a real workflow reaches for a logger,
a request ID or a client out of the ctx, often before it ever cares about
retries or sub-workflows. The example file therefore belongs near the
front of the learning path, paired with 03_data_flow as the two ways
data enters a Step.Do (typed point-to-point Input vs. graph-wide ctx).

- Move 14_context_values_test.go to 04, shifting the previous 04..13
  down to 05..14 (callbacks, conditions, branching, retry, sub-workflow,
  options, observability, debugging, testing, mutators).
- Update every "see NN_xxx" cross-reference inside example files and the
  one reference in the main README.md.
- Reorganize example/README.md to reflect the new numbering, group
  04_context_values under "Move data into a Step" alongside 03_data_flow,
  and add a new "How this path is ordered" section that documents the
  rule explicitly: examples are sorted by frequency-of-use to build the
  user's mental model in the right order, and the file numbers are
  ordering hints — not stable identifiers — so future additions should
  be placed where they fit, even if it pushes later files down by one.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@XiangyuKuangMSFT XiangyuKuangMSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xuxife xuxife added this pull request to the merge queue May 15, 2026
Merged via the queue into main with commit fc60b72 May 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants