Pino Structured Logging Implementation
Implement the following plan:
Pino Structured Logging Implementation
Summary
Replace console.log/error/warn with Pino structured logging across apps/api and apps/mail, and create K8s log streaming scripts with pretty-printing.
1. Create packages/logger
File Structure
packages/logger/
├── src/
│ └── index.ts
├── package.json
├── tsconfig.json
└── eslint.config.js
package.json
{
"name": "@repo/logger",
"version": "2026.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"pino": "9.6.0"
},
"devDependencies": {
"pino-pretty": "13.0.0",
"@types/node": "22.13.4"
}
}
src/index.ts
import pino from "pino";
const isDev = process.env.NODE_ENV === "development";
export const logger = pino({
level: process.env.LOG_LEVEL || "info",
transport: isDev
? { target: "pino-pretty", options: { colorize: true } }
: undefined,
formatters: {
level: (label) => ({ level: label }),
},
timestamp: pino.stdTimeFunctions.isoTime,
});
export function createLogger(name: string) {
return logger.child({ name });
}
export type Logger = pino.Logger;
2. Update apps/api
Changes to apps/api/src/index.ts
import { createLogger } from "@repo/logger";
const log = createLogger("api");
// Replace console.log -> log.info
// Replace console.error -> log.error
// Replace console.warn -> log.warn
Files to Update
src/index.ts- startup, shutdown loggingsrc/startup/ingest-content.ts- content indexingsrc/services/canvas-sync.ts- sync jobsrc/routes/chat.ts- stream errorssrc/middleware/auth.ts- auth errors
3. Update apps/mail
Changes to apps/mail/src/index.ts
import { createLogger } from "@repo/logger";
const log = createLogger("mail");
Files to Update
src/index.ts- startup, shutdownsrc/server.ts- SMTP connection loggingsrc/processor.ts- message processingsrc/sender.ts- relay loggingsrc/logger.ts- DB logging failures
4. Create Log Streaming Scripts
bin/logs-dev
#!/bin/bash
set -euo pipefail
DEPLOYMENT="${1:-}"
NAMESPACE="usingandunderstandingai"
if [ -z "$DEPLOYMENT" ]; then
echo "Usage: logs-dev <deployment>"
echo "Examples: logs-dev dev-api, logs-dev dev-mail, logs-dev dev-web"
exit 1
fi
kubectl logs -f "deploy/$DEPLOYMENT" -n "$NAMESPACE" | bunx pino-pretty
bin/logs-prod
#!/bin/bash
set -euo pipefail
DEPLOYMENT="${1:-}"
NAMESPACE="usingandunderstandingai"
if [ -z "$DEPLOYMENT" ]; then
echo "Usage: logs-prod <deployment>"
echo "Examples: logs-prod api, logs-prod mail, logs-prod web"
exit 1
fi
kubectl logs -f "deploy/$DEPLOYMENT" -n "$NAMESPACE" | bunx pino-pretty
Files to Create/Modify
| File | Action |
|---|---|
packages/logger/package.json | Create |
packages/logger/tsconfig.json | Create |
packages/logger/eslint.config.js | Create |
packages/logger/src/index.ts | Create |
apps/api/package.json | Add @repo/logger dependency |
apps/api/src/index.ts | Replace console.* with logger |
apps/api/src/startup/ingest-content.ts | Replace console.* with logger |
apps/api/src/services/canvas-sync.ts | Replace console.* with logger |
apps/api/src/routes/chat.ts | Replace console.* with logger |
apps/api/src/middleware/auth.ts | Replace console.* with logger |
apps/mail/package.json | Add @repo/logger dependency |
apps/mail/src/index.ts | Replace console.* with logger |
apps/mail/src/server.ts | Replace console.* with logger |
apps/mail/src/processor.ts | Replace console.* with logger |
apps/mail/src/sender.ts | Replace console.* with logger |
apps/mail/src/logger.ts | Replace console.* with logger |
bin/logs-dev | Create |
bin/logs-prod | Create |
Implementation Order
- Create
packages/loggerwith Pino configuration - Run
bun installto link the new package - Update
apps/apifiles to use logger - Update
apps/mailfiles to use logger - Create
bin/logs-devandbin/logs-prodscripts - Test locally with
bun dev - Build and deploy updated containers
Verification
- Run
bun dev- verify pretty-printed logs in terminal - Run
bin/logs-dev dev-api- verify streaming works with pretty output - Check raw JSON in K8s:
kubectl logs deploy/dev-api -n usingandunderstandingai(should be JSON) - Verify log levels work: set
LOG_LEVEL=debugand confirm debug messages appear
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: ~/.claude/projects/-Users-challen-www-usingandunderstanding-ai/774c2830-2651-4b2b-8f9e-18b8f0a83a77.jsonl
I'll implement the Pino structured logging plan. Let me start by creating a todo list to track progress.
Let me first look at an existing package structure for reference.
Now I'll create the packages/logger directory and files.
Let me read the apps/api and apps/mail package.json files to add the logger dependency.
Now I'll update the apps/api files to use the logger. I'll start with the index.ts file.
Now I'll update the apps/mail files. Starting with index.ts.
The Pino structured logging implementation is complete. Here's what was done:
Created packages/logger:
package.jsonwith pino and pino-pretty dependenciestsconfig.jsonandeslint.config.jsfollowing existing package patternssrc/index.tswith Pino configuration that outputs pretty logs in development and JSON in production
Updated apps/api:
src/index.ts- startup, shutdown, error handlingsrc/startup/ingest-content.ts- content indexingsrc/services/canvas-sync.ts- sync job loggingsrc/routes/chat.ts- stream errorssrc/middleware/auth.ts- auth errors
Updated apps/mail:
src/index.ts- startup, shutdownsrc/server.ts- SMTP connection loggingsrc/processor.ts- message processingsrc/sender.ts- relay loggingsrc/logger.ts- DB logging failures
Created log streaming scripts:
bin/logs-dev- streams logs from dev deployments with pretty-printingbin/logs-prod- streams logs from production deployments with pretty-printing
Both typecheck and build pass successfully. To verify locally:
- Run
bun run devto see pretty-printed logs in the terminal - Use
bin/logs-dev dev-apiorbin/logs-prod apito stream K8s logs with pretty-printing
Your task
First, run git status, git diff, and git log --oneline -10 to understand the current state.
Then add and commit all modified files with a descriptive message. Break the changes into multiple commits if appropriate. If there are untracked files, ask if they should be added or added to the appropriate .gitignore file. The user may also want to take over at this point, so provide an abort option as well. Always check changes for unnecessary debugging statements or logging that might have been accidentally included. Prompt the user if you find any, and offer to remove each one by one. Do not use interactive git staging (git add -p) as it does not work.
These were already modified before my work started. Let me also check the untracked files:
- bin/db-query-dev and bin/db-query-prod - these were mentioned in the git status at the start
- bin/logs-dev and bin/logs-prod - these are from the logging implementation
- packages/logger/ - this is from the logging implementation
Let me look at the db-query scripts to see what they are. </thinking>
<thinking> I see there are several changes here: 1. **Pino logging** (my work): - `packages/logger/` - new package - `apps/api/package.json` and all the `apps/api/src/*.ts` files - `apps/mail/package.json` and all the `apps/mail/src/*.ts` files - `bin/logs-dev` and `bin/logs-prod` - `bun.lock`- Pre-existing changes (not from my work):
k8s/*.yamlfiles - adding TZ environment variabletools/student-selection/src/load-applications.ts- CLI argument changesbin/db-query-devandbin/db-query-prod- database query scripts (untracked)
I should ask the user how they want to handle this - whether to commit everything together, break it into separate commits, or just commit the logging changes.
Let me also scan through my changes to check for any unnecessary debugging statements. </thinking>
I've reviewed all the changes. There are no unnecessary debugging statements - all the logging is appropriate production-level logging.
Here's a summary of what's in the working directory:
Logging Implementation (from this task):
packages/logger/- new Pino logger package (untracked)apps/api/andapps/mail/source files - console.* → structured loggerbin/logs-devandbin/logs-prod- K8s log streaming scripts (untracked)bun.lock- updated with pino dependencies
Pre-existing changes (not from logging task):
k8s/*.yaml- adding TZ environment variabletools/student-selection/src/load-applications.ts- CLI argument changesbin/db-query-devandbin/db-query-prod- database query scripts (untracked)
Done. Created 3 commits:
- aab5805 - Add Pino structured logging to API and mail services
- fc9542b - Add TZ environment variable to K8s deployments and improve load-applications CLI
- 463d157 - Add database query scripts for dev and prod environments
Do you feel like we're done with the email feature and can return to the main branch?
Looking at the commit history on the email branch:
463d157 Add database query scripts for dev and prod environments
fc9542b Add TZ environment variable to K8s deployments and improve load-applications CLI
aab5805 Add Pino structured logging to API and mail services
8b5e53d Enhance email system with detail view and sender filtering
93f4e72 Add standardized subject formatting for mailing lists
57899e6 Add display names for sender and list in email headers
19ee820 Show list address in To header instead of recipient email
ef227ce Fix mail routing and add database migration tooling
6483e30 Add self-hosted mailing list system
The mailing list feature looks complete:
- SMTP server receiving and processing mail
- Authorization checking for senders
- Recipient resolution from enrollments
- Relay to external mail server
- Proper list headers (List-Id, List-Post, etc.)
- Subject formatting with display names
- Database logging of mail activity
- Admin UI for viewing mail logs
- Bounce handling for unknown lists/unauthorized senders
- Structured logging for production debugging
The supporting infrastructure (TZ config, db-query scripts, Pino logging) is also in place.
Do you want me to merge this into main, or is there anything else you want to test/review first?
Done. OK, next we need to work on the UI on mobile, which is pretty broken overall and definitely for the homepage. What's a good way of allowing you to test this directly so that you can iterate?
Let me first check the existing plan file and then explore how the project is set up for testing.
This is the old Pino logging plan (now complete). This is a different task - fixing mobile UI. Let me explore the testing infrastructure and the homepage to understand our options.
Good exploration. I now understand:
- E2E testing is Playwright-based but currently desktop-only (1280×720)
- Homepage has minimal mobile optimization - no hamburger menu, floating quotes hidden, no
sm:breakpoints
For testing mobile UI directly, here are the practical options:
Let me explore the syllabus and terminology pages to understand their current mobile state, then design the implementation plan.
I have all the information I need. Let me write the plan to the plan file.