Skip to content

HYPERFLEET-1492 - feat: add WithStackTrace option for selective stack trace capture - #5

Open
kuudori wants to merge 1 commit into
openshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-1492-configurable-stack-trace
Open

HYPERFLEET-1492 - feat: add WithStackTrace option for selective stack trace capture#5
kuudori wants to merge 1 commit into
openshift-hyperfleet:mainfrom
kuudori:HYPERFLEET-1492-configurable-stack-trace

Conversation

@kuudori

@kuudori kuudori commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds WithStackTrace(func(ctx context.Context, r slog.Record) bool) Option to NewHandler. The filter is consulted only at slog.LevelError or above; it decides whether that record gets a stack_trace field.
  • No implicit default: with no filter registered, no record ever gets a stack trace, regardless of level.
  • Fixes a real gap: the shared handler currently attaches a stack trace to every ERROR+ record unconditionally. That matches Sentinel's original (pre-extraction) logger, so it's not a regression for Sentinel - but it doesn't generalize. The Adapter's own logger classifies some errors as expected/routine (K8s NotFound/Conflict, network blips, HyperFleet API 4xx/5xx) and skips stack traces for those specifically, while still logging at ERROR level. That's a per-log-call decision, not a per-handler-instance one, so a simple on/off flag can't express it without forcing every such consumer to hand-write a full wrapping slog.Handler. A caller-supplied filter, consulted directly inside Handle(), solves this with no extra plumbing required from consumers.
  • Also adds the Makefile and pinned tools/go.mod this repo's hyperfleet-hooks pre-commit config (hyperfleet-gofmt, hyperfleet-golangci-lint, hyperfleet-go-vet) requires - both were missing, so the pre-commit hooks could never actually run in this repo.
  • Fixes a pre-existing errcheck lint violation in pool.Get(), surfaced once golangci-lint could actually run via make lint.

Test plan

  • go build ./...
  • go vet ./...
  • go test ./... -race -count=1 (40/40 passing)
  • make lint (0 issues via pinned tools/go.mod golangci-lint)
  • gofmt -l . clean
  • New tests cover: no-filter-never-captures, filter-returns-false, filter-returns-true, filter-not-consulted-below-error, filter-receives-record-for-classification, and a regression test proving the filter survives .With()/.WithGroup() handler chaining (verified this test actually fails without the corresponding fix in WithAttrs/WithGroup)

… trace capture

The shared handler previously attached a stack trace to every ERROR-level
record unconditionally, with no way to opt out. Consumers like the Adapter
classify some errors as expected (K8s NotFound/Conflict, network blips,
HyperFleet API 4xx/5xx) and want to skip stack traces for those specifically,
which is a per-log-call decision that a construction-time flag can't express.
WithStackTrace registers a filter, consulted only at slog.LevelError or
above, that decides whether a given record gets a stack trace - with no
implicit default, so each consumer opts in explicitly.

Also adds the Makefile and pinned tools/go.mod required by this repo's
hyperfleet-hooks pre-commit configuration (gofmt/lint/go-vet targets),
which were missing, and fixes a pre-existing errcheck lint violation in
pool.Get() surfaced once golangci-lint could actually run via make lint.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added configurable stack-trace capture for error-level logs through WithStackTrace.
    • Stack traces can now be selectively captured based on the logging context and record contents.
    • Configuration remains active when creating derived loggers.
  • Bug Fixes

    • Improved resilience when retrieving pooled values, avoiding failures for unexpected value types.
  • Documentation

    • Updated usage examples and API documentation to reflect opt-in stack-trace behavior and the new configuration option.

Walkthrough

The handler now captures error-level stack traces only when WithStackTrace is configured and its predicate accepts the record. The predicate remains available on derived handlers. Tests cover filtering, severity, record inspection, and chaining. Documentation describes the opt-in behavior. A Makefile and Go tools module add linting, formatting, vetting, testing, and hook-installation commands.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Logger
  participant Handler
  participant StackTraceFilter
  participant LogOutput
  Logger->>Handler: Emit record
  Handler->>StackTraceFilter: Evaluate error record
  StackTraceFilter-->>Handler: Return capture decision
  Handler->>LogOutput: Attach stack trace when accepted
Loading
🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding the WithStackTrace option for selective stack-trace capture.
Description check ✅ Passed The description directly explains the stack-trace filter, supporting tests, tooling changes, and lint fix in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Sec-02: Secrets In Log Output ✅ Passed No eligible log statement contains token, password, credential, or secret fields or interpolations; sensitive-term scanning found only a pre-commit comment (CWE-532 not triggered).
No Hardcoded Secrets ✅ Passed No hardcoded credentials, private keys, credential URLs, or secret-named literals were added; tools/go.sum contains only standard h1 checksums, not secrets (CWE-798).
No Weak Cryptography ✅ Passed No crypto imports, banned primitives, custom cryptography, or secret comparisons appear in the added lines or tracked Go source; no CWE-327, CWE-328, or CWE-208 finding.
No Injection Vectors ✅ Passed No added production code uses SQL query construction, exec.Command, template.HTML, or yaml.Unmarshal; fmt.Sprintf only formats errors/stack frames, and tool dependencies have go.sum checksums.
No Privileged Containers ✅ Passed The repository contains no Kubernetes/OpenShift manifests, Helm templates, or Dockerfiles, and no tracked privilege-related settings were found.
No Pii Or Sensitive Data In Logs ✅ Passed No changed logging statement emits email, SSN, card data, session IDs, request/response bodies, or credentialed hostnames; the new stack trace contains only function names and file basenames.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="Running error: context loading failed: no go files to analyze: running go mod tidy may solve the problem"


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@handler.go`:
- Around line 250-255: Update the generic pool method Get so a type mismatch
does not return the zero value when that value may be nil and later
dereferenced. For pcsPool callers such as captureStackTrace, allocate and return
a fresh valid pooled value of the expected type on mismatch, while preserving
the existing successful type-assertion path.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 5a288180-ff3e-4dd5-8de4-8cd2b4400cb5

📥 Commits

Reviewing files that changed from the base of the PR and between c3cad87 and 1aa0889.

⛔ Files ignored due to path filters (1)
  • tools/go.sum is excluded by !**/*.sum, !**/go.sum
📒 Files selected for processing (6)
  • Makefile
  • README.md
  • doc.go
  • handler.go
  • handler_test.go
  • tools/go.mod
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • openshift-hyperfleet/architecture (manual)
  • openshift-hyperfleet/hyperfleet-api (manual)
  • openshift-hyperfleet/hyperfleet-sentinel (manual)
  • openshift-hyperfleet/hyperfleet-adapter (manual)
  • openshift-hyperfleet/hyperfleet-broker (manual)

Comment thread handler.go
Comment on lines +250 to +255
func (p *pool[T]) Get() T {
if v, ok := p.p.Get().(T); ok {
return v
}
var zero T
return zero

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Return a fresh pooled value after a type mismatch.

For pcsPool, the zero value is a nil *[]uintptr. captureStackTrace dereferences it at line 271. An unexpected pool value now causes a nil-pointer panic instead of being tolerated. This is CWE-476.

Proposed fix
 func (p *pool[T]) Get() T {
-	if v, ok := p.p.Get().(T); ok {
+	v := p.p.Get()
+	if v, ok := v.(T); ok {
 		return v
 	}
-	var zero T
-	return zero
+	return p.p.New().(T)
 }

As per path instructions, flag nil access without guards.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (p *pool[T]) Get() T {
if v, ok := p.p.Get().(T); ok {
return v
}
var zero T
return zero
func (p *pool[T]) Get() T {
v := p.p.Get()
if v, ok := v.(T); ok {
return v
}
return p.p.New().(T)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@handler.go` around lines 250 - 255, Update the generic pool method Get so a
type mismatch does not return the zero value when that value may be nil and
later dereferenced. For pcsPool callers such as captureStackTrace, allocate and
return a fresh valid pooled value of the expected type on mismatch, while
preserving the existing successful type-assertion path.

Source: Path instructions

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.

1 participant