|
|
@ -1,21 +1,48 @@ |
|
|
|
--- |
|
|
|
globs: *.vue,*.ts,*.tsx |
|
|
|
alwaysApply: false |
|
|
|
--- |
|
|
|
# Agent Contract — TimeSafari Logging (Unified, MANDATORY) |
|
|
|
|
|
|
|
**Scope:** Applies to all diffs and generated code in this workspace unless explicitly exempted below. |
|
|
|
**Goal:** One consistent, rest-parameter logging style using the project logger; no `console.*` in production code. |
|
|
|
**Author**: Matthew Raymer |
|
|
|
**Date**: 2025-08-15 |
|
|
|
**Status**: 🎯 **ACTIVE** - Mandatory logging standards |
|
|
|
|
|
|
|
## Overview |
|
|
|
|
|
|
|
This document defines unified logging standards for the TimeSafari project, |
|
|
|
ensuring consistent, rest-parameter logging style using the project logger. |
|
|
|
No `console.*` methods are allowed in production code. |
|
|
|
|
|
|
|
## Scope and Goals |
|
|
|
|
|
|
|
**Scope**: Applies to all diffs and generated code in this workspace unless |
|
|
|
explicitly exempted below. |
|
|
|
|
|
|
|
**Goal**: One consistent, rest-parameter logging style using the project |
|
|
|
logger; no `console.*` in production code. |
|
|
|
|
|
|
|
## Non‑Negotiables (DO THIS) |
|
|
|
- You **MUST** use the project logger; **DO NOT** use any `console.*` methods. |
|
|
|
|
|
|
|
- You **MUST** use the project logger; **DO NOT** use any `console.*` |
|
|
|
methods. |
|
|
|
- Import exactly as: |
|
|
|
- `import {{ logger }} from '@/utils/logger'` |
|
|
|
- If `@` alias is unavailable, compute the correct relative path (do not fail). |
|
|
|
- `import { logger } from '@/utils/logger'` |
|
|
|
- If `@` alias is unavailable, compute the correct relative path (do not |
|
|
|
fail). |
|
|
|
- Call signatures use **rest parameters**: `logger.info(message, ...args)` |
|
|
|
- Prefer primitives/IDs and small objects in `...args`; **never build a throwaway object** just to “wrap context”. |
|
|
|
- Production defaults: Web = `warn+`, Electron = `error`, Dev/Capacitor = `info+` (override via `VITE_LOG_LEVEL`). |
|
|
|
- **Database persistence:** `info|warn|error` are persisted; `debug` is not. Use `logger.toDb(msg, level?)` for DB-only. |
|
|
|
- Prefer primitives/IDs and small objects in `...args`; **never build a |
|
|
|
throwaway object** just to "wrap context". |
|
|
|
- Production defaults: Web = `warn+`, Electron = `error`, Dev/Capacitor = |
|
|
|
`info+` (override via `VITE_LOG_LEVEL`). |
|
|
|
- **Database persistence**: `info|warn|error` are persisted; `debug` is not. |
|
|
|
Use `logger.toDb(msg, level?)` for DB-only. |
|
|
|
|
|
|
|
## Available Logger API (Authoritative) |
|
|
|
- `logger.debug(message, ...args)` — verbose internals, timings, input/output shapes |
|
|
|
- `logger.log(message, ...args)` — synonym of `info` for general info |
|
|
|
|
|
|
|
- `logger.debug(message, ...args)` — verbose internals, timings, input/output |
|
|
|
shapes |
|
|
|
- `logger.log(message, ...args)` — synonym of `info` for general info |
|
|
|
- `logger.info(message, ...args)` — lifecycle, state changes, success paths |
|
|
|
- `logger.warn(message, ...args)` — recoverable issues, retries, degraded mode |
|
|
|
- `logger.error(message, ...args)` — failures, thrown exceptions, aborts |
|
|
@ -24,103 +51,157 @@ |
|
|
|
- `logger.withContext(componentName)` — returns a scoped logger |
|
|
|
|
|
|
|
## Level Guidelines (Use These Heuristics) |
|
|
|
|
|
|
|
### DEBUG |
|
|
|
Use for method entry/exit, computed values, filters, loops, retries, and external call payload sizes. |
|
|
|
```ts |
|
|
|
|
|
|
|
Use for method entry/exit, computed values, filters, loops, retries, and |
|
|
|
external call payload sizes. |
|
|
|
|
|
|
|
```typescript |
|
|
|
logger.debug('[HomeView] reloadFeedOnChange() called'); |
|
|
|
logger.debug('[HomeView] Current filter settings', settings.filterFeedByVisible, settings.filterFeedByNearby, settings.searchBoxes?.length ?? 0); |
|
|
|
logger.debug('[FeedFilters] Toggling nearby filter', this.isNearby, this.settingChanged, this.activeDid); |
|
|
|
logger.debug('[HomeView] Current filter settings', |
|
|
|
settings.filterFeedByVisible, |
|
|
|
settings.filterFeedByNearby, |
|
|
|
settings.searchBoxes?.length ?? 0); |
|
|
|
logger.debug('[FeedFilters] Toggling nearby filter', |
|
|
|
this.isNearby, this.settingChanged, this.activeDid); |
|
|
|
``` |
|
|
|
**Avoid:** Vague messages (`'Processing data'`). |
|
|
|
|
|
|
|
**Avoid**: Vague messages (`'Processing data'`). |
|
|
|
|
|
|
|
### INFO |
|
|
|
|
|
|
|
Use for user-visible lifecycle and completed operations. |
|
|
|
```ts |
|
|
|
|
|
|
|
```typescript |
|
|
|
logger.info('[StartView] Component mounted', process.env.VITE_PLATFORM); |
|
|
|
logger.info('[StartView] User selected new seed generation'); |
|
|
|
logger.info('[SearchAreaView] Search box stored', searchBox.name, searchBox.bbox); |
|
|
|
logger.info('[ContactQRScanShowView] Contact registration OK', contact.did); |
|
|
|
logger.info('[SearchAreaView] Search box stored', |
|
|
|
searchBox.name, searchBox.bbox); |
|
|
|
logger.info('[ContactQRScanShowView] Contact registration OK', |
|
|
|
contact.did); |
|
|
|
``` |
|
|
|
**Avoid:** Diagnostic details that belong in `debug`. |
|
|
|
|
|
|
|
**Avoid**: Diagnostic details that belong in `debug`. |
|
|
|
|
|
|
|
### WARN |
|
|
|
|
|
|
|
Use for recoverable issues, fallbacks, unexpected-but-handled conditions. |
|
|
|
```ts |
|
|
|
logger.warn('[ContactQRScanShowView] Invalid scan result – no value', resultType); |
|
|
|
|
|
|
|
```typescript |
|
|
|
logger.warn('[ContactQRScanShowView] Invalid scan result – no value', |
|
|
|
resultType); |
|
|
|
logger.warn('[ContactQRScanShowView] Invalid QR format – no JWT in URL'); |
|
|
|
logger.warn('[ContactQRScanShowView] JWT missing "own" field'); |
|
|
|
``` |
|
|
|
**Avoid:** Hard failures (those are `error`). |
|
|
|
|
|
|
|
**Avoid**: Hard failures (those are `error`). |
|
|
|
|
|
|
|
### ERROR |
|
|
|
Use for unrecoverable failures, data integrity issues, and thrown exceptions. |
|
|
|
```ts |
|
|
|
|
|
|
|
Use for unrecoverable failures, data integrity issues, and thrown |
|
|
|
exceptions. |
|
|
|
|
|
|
|
```typescript |
|
|
|
logger.error('[HomeView Settings] initializeIdentity() failed', err); |
|
|
|
logger.error('[StartView] Failed to load initialization data', error); |
|
|
|
logger.error('[ContactQRScanShowView] Error processing contact QR', error, rawValue); |
|
|
|
logger.error('[ContactQRScanShowView] Error processing contact QR', |
|
|
|
error, rawValue); |
|
|
|
``` |
|
|
|
**Avoid:** Expected user cancels (use `info`/`debug`). |
|
|
|
|
|
|
|
**Avoid**: Expected user cancels (use `info`/`debug`). |
|
|
|
|
|
|
|
## Context Hygiene (Consistent, Minimal, Helpful) |
|
|
|
- **Component context:** Prefer scoped logger. |
|
|
|
```ts |
|
|
|
const log = logger.withContext('UserService'); |
|
|
|
log.info('User created', userId); |
|
|
|
log.error('Failed to create user', error); |
|
|
|
``` |
|
|
|
If not using `withContext`, prefix message with `[ComponentName]`. |
|
|
|
- **Emojis:** Optional and minimal for visual scanning. Recommended set: |
|
|
|
- Start/finish: 🚀 / ✅ • Retry/loop: 🔄 • External call: 📡 • Data/metrics: 📊 • Inspection: 🔍 |
|
|
|
- **Sensitive data:** Never log secrets (tokens, keys, passwords) or payloads >10KB. Prefer IDs over objects; redact/hash when needed. |
|
|
|
|
|
|
|
- **Component context**: Prefer scoped logger. |
|
|
|
|
|
|
|
```typescript |
|
|
|
const log = logger.withContext('UserService'); |
|
|
|
log.info('User created', userId); |
|
|
|
log.error('Failed to create user', error); |
|
|
|
``` |
|
|
|
|
|
|
|
If not using `withContext`, prefix message with `[ComponentName]`. |
|
|
|
|
|
|
|
- **Emojis**: Optional and minimal for visual scanning. Recommended set: |
|
|
|
- Start/finish: 🚀 / ✅ |
|
|
|
- Retry/loop: 🔄 |
|
|
|
- External call: 📡 |
|
|
|
- Data/metrics: 📊 |
|
|
|
- Inspection: 🔍 |
|
|
|
|
|
|
|
- **Sensitive data**: Never log secrets (tokens, keys, passwords) or |
|
|
|
payloads >10KB. Prefer IDs over objects; redact/hash when needed. |
|
|
|
|
|
|
|
## Migration — Auto‑Rewrites (Apply Every Time) |
|
|
|
|
|
|
|
- Exact transforms: |
|
|
|
- `console.debug(...)` → `logger.debug(...)` |
|
|
|
- `console.log(...)` → `logger.log(...)` (or `logger.info(...)` when clearly stateful) |
|
|
|
- `console.info(...)` → `logger.info(...)` |
|
|
|
- `console.warn(...)` → `logger.warn(...)` |
|
|
|
- `console.log(...)` → `logger.log(...)` (or `logger.info(...)` when |
|
|
|
clearly stateful) |
|
|
|
- `console.info(...)` → `logger.info(...)` |
|
|
|
- `console.warn(...)` → `logger.warn(...)` |
|
|
|
- `console.error(...)` → `logger.error(...)` |
|
|
|
|
|
|
|
- Multi-arg handling: |
|
|
|
- First arg becomes `message` (stringify safely if non-string). |
|
|
|
- Remaining args map 1:1 to `...args`: |
|
|
|
- Remaining args map 1:1 to `...args`: |
|
|
|
`console.info(msg, a, b)` → `logger.info(String(msg), a, b)` |
|
|
|
|
|
|
|
- Sole `Error`: |
|
|
|
- `console.error(err)` → `logger.error(err.message, err)` |
|
|
|
- **Object-wrapping cleanup:** Replace `{{ userId, meta }}` wrappers with separate args: |
|
|
|
|
|
|
|
- **Object-wrapping cleanup**: Replace `{{ userId, meta }}` wrappers with |
|
|
|
separate args: |
|
|
|
`logger.info('User signed in', userId, meta)` |
|
|
|
|
|
|
|
## DB Logging Rules |
|
|
|
|
|
|
|
- `debug` **never** persists automatically. |
|
|
|
- `info|warn|error` persist automatically. |
|
|
|
- For DB-only events (no console), call `logger.toDb('Message', 'info'|'warn'|'error')`. |
|
|
|
- For DB-only events (no console), call `logger.toDb('Message', |
|
|
|
'info'|'warn'|'error')`. |
|
|
|
|
|
|
|
## Exceptions (Tightly Scoped) |
|
|
|
|
|
|
|
Allowed paths (still prefer logger): |
|
|
|
|
|
|
|
- `**/*.test.*`, `**/*.spec.*` |
|
|
|
- `scripts/dev/**`, `scripts/migrate/**` |
|
|
|
|
|
|
|
To intentionally keep `console.*`, add a pragma on the previous line: |
|
|
|
```ts |
|
|
|
|
|
|
|
```typescript |
|
|
|
// cursor:allow-console reason="short justification" |
|
|
|
console.log('temporary output'); |
|
|
|
``` |
|
|
|
|
|
|
|
Without the pragma, rewrite to `logger.*`. |
|
|
|
|
|
|
|
## CI & Diff Enforcement |
|
|
|
- Do not introduce `console.*` anywhere outside allowed, pragma’d spots. |
|
|
|
- If an import is missing, insert it and resolve alias/relative path correctly. |
|
|
|
- Enforce rest-parameter call shape in reviews; replace object-wrapped context. |
|
|
|
- Ensure environment log level rules remain intact (`VITE_LOG_LEVEL` respected). |
|
|
|
|
|
|
|
- Do not introduce `console.*` anywhere outside allowed, pragma'd spots. |
|
|
|
- If an import is missing, insert it and resolve alias/relative path |
|
|
|
correctly. |
|
|
|
- Enforce rest-parameter call shape in reviews; replace object-wrapped |
|
|
|
context. |
|
|
|
- Ensure environment log level rules remain intact (`VITE_LOG_LEVEL` |
|
|
|
respected). |
|
|
|
|
|
|
|
## Quick Before/After |
|
|
|
**Before** |
|
|
|
```ts |
|
|
|
|
|
|
|
### **Before** |
|
|
|
|
|
|
|
```typescript |
|
|
|
console.log('User signed in', user.id, meta); |
|
|
|
console.error('Failed to update profile', err); |
|
|
|
console.info('Filter toggled', this.hasVisibleDid); |
|
|
|
``` |
|
|
|
**After** |
|
|
|
```ts |
|
|
|
import {{ logger }} from '@/utils/logger'; |
|
|
|
|
|
|
|
### **After** |
|
|
|
|
|
|
|
```typescript |
|
|
|
import { logger } from '@/utils/logger'; |
|
|
|
|
|
|
|
logger.info('User signed in', user.id, meta); |
|
|
|
logger.error('Failed to update profile', err); |
|
|
@ -128,11 +209,17 @@ logger.debug('[FeedFilters] Filter toggled', this.hasVisibleDid); |
|
|
|
``` |
|
|
|
|
|
|
|
## Checklist (for every PR) |
|
|
|
- [ ] No `console.*` (or properly pragma’d in the allowed locations) |
|
|
|
|
|
|
|
- [ ] No `console.*` (or properly pragma'd in the allowed locations) |
|
|
|
- [ ] Correct import path for `logger` |
|
|
|
- [ ] Rest-parameter call shape (`message, ...args`) |
|
|
|
- [ ] Right level chosen (debug/info/warn/error) |
|
|
|
- [ ] No secrets / oversized payloads / throwaway context objects |
|
|
|
- [ ] Component context provided (scoped logger or `[Component]` prefix) |
|
|
|
|
|
|
|
_Unified on: 2025-08-15 08:11:46Z_ |
|
|
|
--- |
|
|
|
|
|
|
|
**Status**: Active and enforced |
|
|
|
**Last Updated**: 2025-08-15 08:11:46Z |
|
|
|
**Version**: 1.0 |
|
|
|
**Maintainer**: Matthew Raymer |
|
|
|