Skip to content

Add IVS enhanced logging via EventBridge#10

Merged
stevethomas merged 25 commits intomainfrom
seth/lpx-476-setup-enhanced-logging-for-ivs
Mar 31, 2026
Merged

Add IVS enhanced logging via EventBridge#10
stevethomas merged 25 commits intomainfrom
seth/lpx-476-setup-enhanced-logging-for-ivs

Conversation

@SethSharp
Copy link
Copy Markdown
Member

@SethSharp SethSharp commented Mar 18, 2026

Hey, I made a thing! 🥳

Great! Now please answer the following questions to help out your assigned reviewer:

What problems are you solving?

  • IVS events aren't being logged anywhere, making it hard to debug live stream issues
  • Sets up EventBridge rules + CloudWatch Logs so all IVS events (stream start/stop/failure, recording state, health, etc.) are captured automatically

Is there anything the reviewer needs to know to deploy this?

  • This is fully opt-in — users need to add aws.logging.ivs: true to their yolo.yml to enable it
  • Can also be run standalone via yolo sync:logging <environment>
  • Optional aws.logging.ivs.log-retention-days config (defaults to 14 days)
  • No impact on existing deployments — all steps return SKIPPED when IVS is not enabled
  • Guard uses Manifest::get() (truthiness check) so ivs: false correctly disables the feature

Config example:

environments:
  production:
    aws:
      logging:
        ivs:
          log-retention-days: 14  # optional, defaults to 14

Or minimal:

environments:
  production:
    aws:
      logging:
        ivs: true

What does it provision?

  1. CloudWatch Log Group (/aws/ivs/{keyed-name}) with configurable retention
  2. CloudWatch resource policy granting events.amazonaws.com write access
  3. EventBridge rule matching all aws.ivs events
  4. EventBridge target wiring the rule to the log group

Architecture:

  • New sync:logging command (follows Network/Storage/Compute/IAM pattern)
  • Steps live in Steps/Logging/ with Ivs prefix (e.g. SyncIvsCloudWatchLogGroupStep)
  • New UsesCloudWatchLogs and UsesEventBridge concerns for resource lookups

Coauthor Jarvis 🤖

🤖 Generated with Claude Code

@SethSharp SethSharp requested a review from stevethomas March 18, 2026 22:33
SethSharp and others added 3 commits March 20, 2026 09:36
Replaces raw Manifest::get('aws.ivs') boolean checks with the new
isIvsSupported() method and uses explicit (bool) cast.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SethSharp SethSharp marked this pull request as ready for review March 19, 2026 23:42
Comment thread src/Concerns/UsesEventBridge.php Outdated
Comment thread src/Steps/Ivs/SyncEventBridgeTargetStep.php Outdated
Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php
- UsesEventBridge: catch EventBridgeException from describeRule instead
  of unreachable empty check
- SyncEventBridgeTargetStep: handle missing rule gracefully on dry-run
- SyncCloudWatchLogGroupStep: check and update retention policy on
  existing log groups

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/Steps/Ivs/SyncEventBridgeTargetStep.php Outdated
When listTargetsByRule throws because the rule doesn't exist yet,
fall through to the creation logic instead of returning CREATED
without actually creating the target.

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

@stevethomas stevethomas left a comment

Choose a reason for hiding this comment

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

Review Summary

Verdict: ❌ Changes Requested

Risk Assessment

Category Status
Data Integrity
Security ⚠️
Breaking Changes
Operational
Test Coverage ⚠️

Findings

❌ Blockers

src/Steps/Ivs/SyncCloudWatchLogGroupStep.php:59 — Missing CloudWatch Logs resource policy. EventBridge requires a resource-based policy on the log group granting logs:CreateLogStream and logs:PutLogEvents to events.amazonaws.com. Without this, putTargets succeeds but events silently fail to deliver — the entire IVS logging pipeline will appear configured but produce zero logs.

In src/Steps/Ivs/SyncCloudWatchLogGroupStep.php, after creating the
log group and setting its retention policy, add a
putResourcePolicy call granting events.amazonaws.com permission
to write to the log group. Apply on both create AND sync paths.
See EventBridge → CloudWatch Logs target docs.

⚠️ Warnings

src/Steps/Ivs/SyncEventBridgeTargetStep.php:34 — Target drift: if the target exists but the ARN has changed (e.g. log group rename), the step returns SYNCED without updating. Events would route to a stale log group.

In SyncEventBridgeTargetStep, instead of just checking the target
ID, also verify the ARN matches. If the ID exists but ARN differs,
fall through to putTargets to update it.

src/Steps/Ivs/SyncCloudWatchLogGroupStep.php — No unit tests for the 3 new IVS steps. Consistent with repo patterns (only arch tests exist), but these steps have real branching logic that would benefit from coverage. Not blocking since it's a repo-wide gap.


🤖 Reviewed by Claude Code

Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php
Comment thread src/Steps/Logging/SyncIvsEventBridgeTargetStep.php
Comment thread src/Commands/SyncCommand.php Outdated
Comment thread src/Commands/SyncIvsCommand.php Outdated
Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php
Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php
Comment thread src/Aws.php Outdated
Comment thread src/Manifest.php Outdated
SethSharp and others added 3 commits March 24, 2026 15:38
- Add CloudWatch Logs resource policy granting events.amazonaws.com
  write access (blocker: without this EventBridge silently fails to deliver)
- Fix target drift in SyncEventBridgeTargetStep by verifying ARN match
- Remove SyncIvsCommand, fold IVS steps into SyncIamCommand
- Replace Manifest::isIvsSupported() with inline Manifest::has('aws.ivs')
- Fix alphabetical ordering in Aws.php (elb before eventbridge)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/Steps/Logging/SyncIvsEventBridgeTargetStep.php
@SethSharp SethSharp requested a review from stevethomas March 25, 2026 21:50
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php Outdated
Manifest::has() only checks if the key exists via Arr::has(), so
ivs: false would still pass the guard and provision resources.
Switch to Manifest::get() which returns the actual value — falsy
for false/null/missing, truthy for true or config objects.

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

@stevethomas stevethomas left a comment

Choose a reason for hiding this comment

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

Review Summary

Verdict: ✅ Approved (with warnings)

Context

Found and fixed the Manifest::has vs Manifest::get bug during review — ivs: false would have bypassed the guard and provisioned resources. Fix pushed in dde6e2f.

Risk Assessment

Category Status
Data Integrity ⚠️
Security
Breaking Changes
Operational ⚠️
Test Coverage ⚠️

Findings

⚠️ Warnings

src/Steps/Logging/SyncIvsEventBridgeTargetStep.php:41catch (EventBridgeException) swallows ALL errors (throttling, auth, network), not just "rule not found". A permissions error gets silently ignored and putTargets fails with a misleading error instead.

In SyncIvsEventBridgeTargetStep.php line 41, check
$e->getAwsErrorCode() for ResourceNotFoundException
specifically and re-throw anything else.

src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php:82 — CloudWatch Logs has a hard limit of 10 resource policies per region per account. Each YOLO app creates its own policy via keyedResourceName. Multiple apps with IVS in the same account will exhaust this. Consider a shared policy name or read-then-merge approach.

Check whether the per-app policyName in putResourcePolicy()
will scale within the 10-per-account CloudWatch Logs resource
policy limit. Consider a shared policy that merges log group ARNs.

src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php:89 — Resource policy lacks aws:SourceAccount condition — any EventBridge rule in any account could write to the log group.

Add Condition => StringEquals => aws:SourceAccount to the
resource policy to prevent cross-account confused deputy.

📝 Notes

  • describeLogGroups uses prefix matching without pagination — if 50+ log groups share the prefix, exact match on page 2+ would be missed. Low risk with /aws/ivs/ prefix but worth noting for the generic trait.
  • putResourcePolicy and putRule are called on every sync even when nothing changed — idempotent but noisy.
  • putRule call is duplicated between try/catch branches in SyncIvsEventBridgeRuleStep — could extract to reduce duplication.
  • No unit tests for the 3 new step classes. The has/get bug demonstrates the kind of issue tests would catch. Project convention is arch tests only for steps, but consider adding coverage for the branching logic.

🤖 Reviewed by Claude Code

if ($existingTarget && $existingTarget['Arn'] === $expectedArn) {
return StepResult::SYNCED;
}
} catch (EventBridgeException) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Finding: catch (EventBridgeException) swallows ALL EventBridge errors — throttling, auth, network — not just "rule not found". A permissions error would be silently ignored, and putTargets below would fail with a confusing error instead of surfacing the real cause.
Severity: WARNING

Suggested Fix

Check $e->getAwsErrorCode() for ResourceNotFoundException specifically and re-throw anything else so real errors (throttling, permissions) surface immediately.

Prompt
In src/Steps/Logging/SyncIvsEventBridgeTargetStep.php line 41,
the catch block catches all EventBridgeException types. Change
it to check $e->getAwsErrorCode() === 'ResourceNotFoundException'
and re-throw any other exception type so throttling, auth, and
network errors surface with their real error message.

Comment thread src/Steps/Logging/SyncIvsCloudWatchLogGroupStep.php
stevethomas and others added 4 commits March 31, 2026 16:08
Single policy name (yolo-ivs-eventbridge-policy) with wildcard
resource ARN /aws/ivs/* instead of per-app policies. Avoids
hitting the 10 resource policies per account limit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EventBridge rule captures all aws.ivs events account-wide, so
the rule, log group, and resource policy should all be shared
(exclusive: false) rather than per-app. Renamed log group from
live-events to ivs-logs for consistency with the ivs-* prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only the resource policy needs to be shared (10-per-account limit).
Rule and log group stay exclusive so each app gets isolated IVS
logging. Log group simplified to base keyed name under /aws/ivs/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stevethomas and others added 7 commits March 31, 2026 16:59
Follows the existing codebase pattern where steps catch
ResourceDoesNotExistException and AWS SDK exceptions are
caught/converted in the Uses* concern traits. Adds
eventBridgeRuleTargets() to UsesEventBridge trait.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests verify steps skip when IVS is not configured or explicitly
disabled, and that resource names follow naming conventions.
Also simplifies target step to use AwsResources::eventBridgeRule()
as guard instead of catching raw EventBridgeException.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Limited guard-only coverage isn't worth maintaining as a
standalone test file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests keyedResourceName (exclusive/non-exclusive, separators,
suffixes) and payloadHasDifferences (nested diffs, missing keys,
type mismatches, extra keys ignored).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test files no longer need their own beforeAll setup. Pest.php
creates a temp yolo.yml and binds the environment globally.
writeManifest() available for tests that need custom config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers Aws::tags() (both output formats), Manifest has/get/apex/
tenants/timezone/multitenancy (including edge cases and validation),
and Paths building (base, yolo, build, artefact, yoloDir, logDir).

Bootstrap uses testing environment — no AWS clients instantiated,
fully isolated via temp manifest in /tmp.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@stevethomas stevethomas merged commit 4b83960 into main Mar 31, 2026
3 checks passed
@stevethomas stevethomas deleted the seth/lpx-476-setup-enhanced-logging-for-ivs branch March 31, 2026 07:18
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