Skip to content

feat: add observability abstractions and wire to runtime - #2147

Draft
nborges-aws wants to merge 1 commit into
refactorfrom
logs-impl
Draft

feat: add observability abstractions and wire to runtime#2147
nborges-aws wants to merge 1 commit into
refactorfrom
logs-impl

Conversation

@nborges-aws

Copy link
Copy Markdown
Contributor

Description

This PR adds reusable observability infrastructure. The setup is built to apply generally across our primitives, while allowing for resource-specific customization where necessary. This PR wires runtime to the infrastructure. Remaining primitives wiring will be released as a follow pending alignment on the abstractions added here.

  • Adds a reusable, logs-only ObservabilityClient as the shared API entry point
  • Adds a source resolver registry, with runtime-specific log-group resolution
  • Adds a primitive-agnostic CloudWatch source reader for search and live tail
  • Adds a shared observability handler factory and mounts logs under Runtime
  • Normalizes provider events into a generic LogRecord

Architecture

Runtime handler → ObservabilityClient → RuntimeSourceResolver → CloudWatchSourceReader → LogRecord

The resolver owns resource-to-log-group translation. The source reader owns CloudWatch mechanics without ever needing knowledge of our resource types. The client is responsible for the orchestration of these layers.

Commands

Tail logs:

agentcore runtime logs --id <runtime-id> --tail

Search logs:

agentcore runtime logs \
    --id <runtime-id> \
    --since 1h \
    --until now \
    --level error \
    --query '"timed out"' \
    --limit 100

--qualifier selects a non-default Runtime endpoint.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

How have you tested the change?

  • bun run test (2322 pass, 0 fail)
  • I ran npm run test:unit and npm run test:integ
  • I ran npm run typecheck
  • I ran npm run lint
  • If I modified src/assets/, I ran npm run test:update-snapshots and committed the updated snapshots

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.

@github-actions github-actions Bot added the size/xl PR size: XL label Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Aug 31, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 31, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.33%. Comparing base (3d449c5) to head (b5e008a).

Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2147      +/-   ##
============================================
+ Coverage     97.29%   97.33%   +0.03%     
============================================
  Files           479      486       +7     
  Lines         29673    30066     +393     
============================================
+ Hits          28871    29264     +393     
  Misses          802      802              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AgentCore Harness Review

Verdict: Looks good

Nice, well-factored change. The ObservabilityClient / SourceReader split cleanly separates identity resolution from CloudWatch I/O and leaves an obvious extension point for future resource kinds. A few things I looked at carefully:

  • AWS SDK mocking: sourceReader.test.ts and client.test.ts mock at the SDK client boundary (send) rather than the pieces of internal state, which matches the guidance in the review criteria. No excessive mocking.
  • Pagination + limit: CloudWatchSourceReader.searchLogs correctly caps the per-page limit at query.limit - yielded, terminates when nextToken === requestToken, and short-circuits on limit <= 0. Edge cases (limit=1, single page, empty page with token) all look right.
  • Live Tail: tailLogs handles both the in-band SessionTimeoutException event and the thrown variant, reconnects only when timed out, and exits cleanly on abort. Legacy arn:...:* suffix stripping is guarded and covered.
  • Missing log group: nice consistent ResourceNotFoundError translation with actionable message in both search and tail paths (including the pre-flight DescribeLogGroups case for tail).
  • Handler wiring: input validation (--tail vs --since/--until, --limit outside search mode, --since > --until) all raise typed InputValidationErrors and are covered in tests. withUserCancellation propagates the abort into the SDK calls.
  • Telemetry: this codebase currently instruments telemetry at the top-level command run in src/index.ts rather than per-handler, so no per-feature instrumentation is missing here.

Non-blocking observations if you want to iterate later:

  • --tail is effectively a no-op flag when neither --since nor --until is passed (tailing is already the default in that case). Consider either making search the default with --tail required for streaming, or documenting the current behavior in the flag help. Either is fine, just be intentional.
  • ResourceFlagValues in handlers/observability/types.ts duplicates the existing FlagsOf in router/handler.tsx. Could reuse or export the router one to keep a single source of truth.
  • The log group naming convention /aws/bedrock-agentcore/runtimes/<id>-<qualifier> is hard-coded; if the service ever exposes this via an API, worth switching to that to avoid a lurking coupling.

Nothing here blocks merge. Ship it.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 31, 2026

@AlexanderRichey AlexanderRichey left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There is some good stuff here but I think we can simplify a bit. I like the idea of creating a function that creates these handlers for us, but I think this can be done with a little less abstraction and more directly. It seems like what you want is something like:

const createLogsHandler = (client: ObservabilityClient, io: AppIO) => createHandler(...)

Then use this for Runtime and Harness:

export function createRuntimeHandler(...): Router {
    ...
    runtime.handler(createLogsHandler(obsC, io))
    ...
}

} from "./resolver";
import type { LogSearchQuery, LogTailQuery, RawLogRecord, SourceReader } from "./sourceReader";

export interface LogRecord {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this importable from a package somewhere? Is this what all log records look like in AC?

logs: readonly LogSource[];
}

export interface ObservabilitySourceResolver<R extends ObservableResourceRef> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we really need all of these complex types?

* Builds reusable logs command behavior. Primitive routers contribute only
* identity flags and conversion to an ObservableResourceRef.
*/
export class ObservabilityHandlerFactory implements ObservabilityHandlerFactories {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Interesting. Did you consider writing a function that returns a handler function? That might be simpler.

const timestamp = Date.parse(trimmed);
if (!Number.isNaN(timestamp)) return timestamp;

throw new InputValidationError(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any reason we're not using date-fns?

toResource(flags: ResourceFlagValues<F>): Extract<ObservableResourceRef, { kind: K }>;
}

export interface ObservabilityHandlerFactories {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the upshot of an interface for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants