Repository navigation
Logging & Error Handling
| Area | Library | Config |
|---|---|---|
| Backend |
structlog on top of the standard library logging
|
backend/app/core/logging.py |
| Backend errors | FastAPI exception handlers | backend/app/core/errors.py |
| Frontend | No library: a small wrapper over console
|
frontend/src/services/logger.ts |
| Frontend errors |
ApiError, ErrorMessage, ErrorBoundary
|
frontend/src/services/api.ts, frontend/src/components/
|
The standard library's logging is routed through the same structlog renderer, so logging.getLogger(__name__) and uvicorn's logs come out in the same JSON format.
from app.core.logging import get_logger, logged_run
logger = get_logger(__name__)
logger.info("organization_matched", organization_id=org.id, rule="domain")
with logged_run("sync", source="omerlo") as counts:
counts["fetched"] = len(records)- Event names are short snake_case verbs in the past tense; data goes in keyword arguments, not in the message.
-
logged_runcounts are masked like any other key, so count names must not contain a sensitive word (usecontacts_imported, notemails_imported), and must not be namedevent,level,timestamp,module,duration_ms,run_id,run_kindorsource. -
printis rejected by Ruff (T20);console.*is rejected by ESLint outside the logger.
| Level | Use for |
|---|---|
| DEBUG | Detail useful while developing (each page fetched, each rule tried) |
| INFO | Lifecycle events: startup, shutdown, each request, each run started and completed with counts |
| WARNING | Something recoverable: a client error, a skipped record, a retry |
| ERROR | A failure, always with the stack trace (logger.exception or exc_info=) |
Set with LOG_LEVEL (backend, default INFO) and VITE_LOG_LEVEL (frontend, default debug in development and warn in builds).
One JSON object per line on stdout:
{"event": "request_completed", "method": "GET", "path": "/api/health", "status_code": 200, "duration_ms": 1.4, "request_id": "5a52f95c30a64f5093310ef5bc03285e", "level": "info", "module": "app.core.request_context", "timestamp": "2026-10-20T03:28:59.875084Z"}-
request_idis taken from a safe incomingX-Request-IDheader or generated, and returned in the response header and in error bodies, so a user can quote it. -
run_idis bound for the whole of a sync or import run.
- Values under keys containing
password,secret,token,api_key,apikey,authorization,credential,cookie,emailorphonebecome[REDACTED]. Keys are matched lowercased with-read as_, so theX-Api-Keyheader is covered. - Emails and North American phone numbers inside any text, including stack traces, become
[EMAIL]and[PHONE](a number that is part of a decimal, such as1234567890.5, is not masked). - Only the request path is logged, never the query string (ADR D8). httpx logs are kept at WARNING because they include full URLs.
- Request validation errors are returned and logged without the rejected input.
| Exception | HTTP status | code |
|---|---|---|
DataValidationError |
422 | validation_error |
NotFoundError |
404 | not_found |
ConnectorError(source=...) |
502 | connector_error |
PulseError (base) |
500 | internal_error |
| Any other exception | 500 |
internal_error, generic message, stack trace only in the logs |
Every error response has the body {"error": {"code", "message", "details", "request_id"}}. Messages of PulseError subclasses are shown to users, so write them for a user and keep personal data out of them. In the frontend, internal_error, service_unavailable, network_error (the server could not be reached) and any 5xx http_error (a server error without a standard body) show a friendly generic text; other error messages are shown as written.
PULSE Wiki
- Home
- Team Structure
- Product Roadmap
- Meeting Minutes
- Presentations
- Risks
- User Consent and EULA
- Legal and Ethical Issues
- PULSE Identity and Business Orientation
- Personas
- Diversity Statement
- Overall Architecture and Class Diagrams
- Architecture Decision Records
- Infrastructure and Tools
- Beyond-CRUD Flows
- Naming Conventions
- Code Style
- Testing Plan and Continuous Integration
- Security
- Performance
- Deployment Plan and Infrastructure
- Missing Knowledge and Independent Learning
-
Iteration and Release Notes
- Iteration 1 (Oct 6, 2026)
- Iteration 2 (Oct 20, 2026)
- Iteration 3 (Nov 3, 2026)
- Iteration 4 (Nov 17, 2026)
- Iteration 5 (Dec 1, 2026)
- Iteration 6 (Dec 15, 2026)
- Iteration 7 (Jan 19, 2027)
- Iteration 8 (Feb 5, 2027)
- Iteration 9 (Feb 16, 2027)
- Iteration 10 (Mar 2, 2027)
- Iteration 11 (Mar 16, 2027)
- Iteration 12 (Mar 30, 2027)
- Iteration 13 (Apr 13, 2027)