Skip to content

Converted remaining outbox and welcome email logs to structured logs#26660

Merged
cmraible merged 8 commits intomainfrom
chris-ny-1020-remaining-structured-logs
Mar 10, 2026
Merged

Converted remaining outbox and welcome email logs to structured logs#26660
cmraible merged 8 commits intomainfrom
chris-ny-1020-remaining-structured-logs

Conversation

@cmraible
Copy link
Copy Markdown
Collaborator

@cmraible cmraible commented Mar 3, 2026

closes https://linear.app/ghost/issue/NY-1020/convert-outbox-logs-to-structured-logs

Summary

  • Follows up on Converted initial outbox logs to structured logging #26599 to convert the remaining logging statements in the outbox and member welcome email services to structured logging with system.event fields
  • Adds structured metadata (entry IDs, event types, batch stats, durations) to each log for easier dashboard filtering and debugging
  • No behavioral changes — only logging call formats are changed

New structured events

Event Level File
outbox.processing.error error outbox/index.js
outbox.entry.no_handler warn process-entries.js
outbox.entry.payload_parse_failed error process-entries.js
outbox.entry.send_failed error process-entries.js
outbox.entry.delete_failed error process-entries.js
outbox.init.failed error process-outbox.js
outbox.batch.complete info process-outbox.js
outbox.member_created.no_automated_email warn member-created.js
member_welcome_email.sending info service.js

Note for reviewer

The logging.info(statusMessage) in outbox/index.js is left as-is — processOutbox() returns a string that the caller logs. Converting this one would require changing the return value contract. Worth discussing whether the return-for-logging pattern should be refactored in a separate PR.

Test plan

  • All 11 outbox unit tests passing (3 index + 4 member-created + 4 new process-entries)
  • Lint passes on all modified files
  • Integration test process-outbox.test.js still passes (exercises full pipeline)

ref https://linear.app/ghost/issue/NY-1020


Note

Low Risk
Logging-only changes across outbox processing and member welcome emails; low risk aside from potential differences in log output/alerting and added metadata payload size.

Overview
Converts remaining outbox processing and member welcome email log lines to structured logs with system.event fields, and attaches useful metadata such as member_status, slug, event_type, entry_id, and batch timing/counts.

Standardizes error/warn reporting in outbox/index.js, process-outbox.js, process-entries.js, handlers/member-created.js, and member-welcome-emails/service.js (including passing the caught err object), and expands unit tests to assert the new structured events (plus adds a new process-entries unit test suite).

Written by Cursor Bugbot for commit dd730e7. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Replaces plain-string log messages with structured, object-based logs across member-welcome-email, outbox services, and outbox job files. New logs include a system object with event identifiers and contextual fields (e.g., event, member_status, slug, entry_id, entries_processed, entries_failed, duration_ms). Control flow, return values, and public APIs remain unchanged. Unit tests were added or updated to assert the new structured log payloads and to cover parse, send, delete, and initialization error scenarios.

Possibly related PRs

  • PR converting outbox logging to structured system events and updating tests to assert structured log payloads in outbox-related files.

Suggested reviewers

  • troyciesco
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: converting remaining outbox and welcome email logs to structured logging format.
Description check ✅ Passed The PR description clearly explains the changes: converting remaining outbox and member welcome email logs to structured logging with system.event fields, lists all new structured events, provides a test plan, and references the related linear issue NY-1020.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chris-ny-1020-remaining-structured-logs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@ghost/core/core/server/services/outbox/jobs/lib/process-entries.js`:
- Around line 65-79: The check that treats falsy payloads as parse failures is
causing valid JSON falsy values to be misclassified; update processEntries logic
to introduce an explicit parseSuccess (or similar) boolean set only when
JSON.parse (or payload parsing) succeeds, then use parseSuccess === false to log
'outbox.entry.payload_parse_failed' via logging.error (referencing
handler.LOG_KEY and entry.id), otherwise proceed to the handler call and log
'outbox.entry.send_failed' on handler failure (using
handler.getLogInfo(payload)); ensure you stop relying on !payload to decide
parse failure.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4147e8 and 02cfff4.

📒 Files selected for processing (8)
  • ghost/core/core/server/services/member-welcome-emails/service.js
  • ghost/core/core/server/services/outbox/handlers/member-created.js
  • ghost/core/core/server/services/outbox/index.js
  • ghost/core/core/server/services/outbox/jobs/lib/process-entries.js
  • ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js
  • ghost/core/test/unit/server/services/outbox/handlers/member-created.test.js
  • ghost/core/test/unit/server/services/outbox/index.test.js
  • ghost/core/test/unit/server/services/outbox/jobs/lib/process-entries.test.js

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Free Tier Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Missing documented outbox.job.complete structured log event
    • Converted job completion logging from returned strings to inline structured logging with the outbox.job.complete event, matching the documented behavior and the pattern used for outbox.batch.complete.

Create PR

Or push these changes by commenting:

@cursor push 0d08dfd982
Preview (0d08dfd982)
diff --git a/ghost/core/core/server/services/outbox/index.js b/ghost/core/core/server/services/outbox/index.js
--- a/ghost/core/core/server/services/outbox/index.js
+++ b/ghost/core/core/server/services/outbox/index.js
@@ -34,8 +34,7 @@
         this.processing = true;
 
         try {
-            const statusMessage = await processOutbox();
-            logging.info(statusMessage);
+            await processOutbox();
         } catch (e) {
             logging.error({
                 system: {

diff --git a/ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js b/ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js
--- a/ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js
+++ b/ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js
@@ -88,10 +88,25 @@
     const durationMs = Date.now() - jobStartMs;
 
     if (totalProcessed + totalFailed === 0) {
-        return `${OUTBOX_LOG_KEY} ${MESSAGES.NO_ENTRIES}`;
+        logging.info({
+            system: {
+                event: 'outbox.job.complete',
+                entries_processed: 0,
+                entries_failed: 0,
+                duration_ms: durationMs
+            }
+        }, `${OUTBOX_LOG_KEY} ${MESSAGES.NO_ENTRIES}`);
+        return;
     }
 
-    return `${OUTBOX_LOG_KEY} Job complete: Processed ${totalProcessed} outbox entries, ${totalFailed} failed in ${(durationMs / 1000).toFixed(2)}s`;
+    logging.info({
+        system: {
+            event: 'outbox.job.complete',
+            entries_processed: totalProcessed,
+            entries_failed: totalFailed,
+            duration_ms: durationMs
+        }
+    }, `${OUTBOX_LOG_KEY} Job complete: Processed ${totalProcessed} outbox entries, ${totalFailed} failed in ${(durationMs / 1000).toFixed(2)}s`);
 }
 
 module.exports = processOutbox;
This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ghost/core/test/unit/server/services/outbox/index.test.js (1)

59-71: LGTM!

The test correctly verifies the structured error logging behavior when processOutbox throws. It follows the established pattern from the startProcessing guard test above.

Consider also asserting that the error object is captured in the log for completeness:

assert.ok(errorLog.err);
assert.equal(errorLog.err.message, 'Unexpected failure');

This would ensure the error details are available for debugging, but it's optional since the current assertion style is consistent with the existing test at lines 51-55.

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/server/services/outbox/index.test.js` around lines 59 -
71, Add assertions to verify the captured error object is present and contains
the expected message after startProcessing triggers a thrown error: after
locating the log with findByEvent(logCapture.output, 'outbox.processing.error')
(used already) assert that errorLog.err exists and that errorLog.err.message
equals 'Unexpected failure' so the test ensures the error details from
processOutboxStub rejection are logged; keep using the existing
processOutboxStub and service.startProcessing setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ghost/core/test/unit/server/services/outbox/index.test.js`:
- Around line 59-71: Add assertions to verify the captured error object is
present and contains the expected message after startProcessing triggers a
thrown error: after locating the log with findByEvent(logCapture.output,
'outbox.processing.error') (used already) assert that errorLog.err exists and
that errorLog.err.message equals 'Unexpected failure' so the test ensures the
error details from processOutboxStub rejection are logged; keep using the
existing processOutboxStub and service.startProcessing setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cd8712b-1e81-4c32-910b-ebff2eadac80

📥 Commits

Reviewing files that changed from the base of the PR and between 5963c47 and b295c14.

📒 Files selected for processing (3)
  • ghost/core/core/server/services/outbox/index.js
  • ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js
  • ghost/core/test/unit/server/services/outbox/index.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • ghost/core/core/server/services/outbox/index.js

cmraible added 6 commits March 5, 2026 16:51
ref https://linear.app/ghost/issue/NY-1020
Follows up on #26599 to convert the remaining logging statements in the
outbox service, process-entries, process-outbox, and member welcome
email service to use structured logging with system.event fields
The !payload check could misclassify failures if JSON.parse returned a
falsy value. Using a payloadParsed boolean makes the intent clearer.
Kept the existing pattern where processOutbox() returns a status
message string and the caller logs it, to avoid behavioral changes
beyond the structured logging conversion
Clearer than using a flag to distinguish the two failure modes
Restored original control flow structure — only the logging calls
are changed to structured format, no other behavioral changes
@cmraible cmraible force-pushed the chris-ny-1020-remaining-structured-logs branch from 20a6641 to 19651ff Compare March 6, 2026 00:51
@cmraible cmraible requested a review from troyciesco March 6, 2026 01:22
entry_id: entry.id
},
err
}, `${handler.LOG_KEY} Failed to send to ${handler.getLogInfo(payload)}: ${errorMessage}`);
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.

It looks like logging.error actually automatically appends the error message here

so that means it gets duplicated - this is what i get locally:

[2026-03-06 15:14:18] ERROR [OUTBOX][MEMBER-WELCOME-EMAIL] Failed to send to faesdvf@afewsdv.com: Manual verification error Manual verification error

so maybe we don't need to append the errorMessage here (and elsewhere in outbox/welcome email log errors) ourselves? though we would have to leave the colon to make it read better, which is kinda clunky lol

ironic this pops up after i suggested last week that we shouldn't assert the error message in tests lol. though i think those tests were passing, and didn't have the duplication 🤔 so idk if it woulda caught this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Oh, nice find! I'll update all the instances here to not append the errorMessage, and maybe change the : to . so it makes a bit more sense with or without the actual error message.

GhostLogger auto-appends err.message to the log output, so including
errorMessage in the message string caused duplication. Changed trailing
colons to periods so the message reads naturally with the appended error.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Now that the error message is auto-appended by GhostLogger via the
err object, the manually extracted errorMessage/cleanupError variables
are no longer needed for the log calls
@cmraible cmraible requested a review from troyciesco March 9, 2026 22:20
@cmraible cmraible merged commit 8df2622 into main Mar 10, 2026
31 checks passed
@cmraible cmraible deleted the chris-ny-1020-remaining-structured-logs branch March 10, 2026 17:08
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