initial commit
This commit is contained in:
131
docs/architecture/19-monitoring-observability.md
Normal file
131
docs/architecture/19-monitoring-observability.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# 19. Monitoring & Observability
|
||||
|
||||
## Structured Logging (Pino)
|
||||
|
||||
```typescript
|
||||
// src/lib/server/logger.ts
|
||||
|
||||
import pino from 'pino';
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
|
||||
export const logger = pino({
|
||||
level: process.env.LOG_LEVEL ?? (isDev ? 'debug' : 'info'),
|
||||
|
||||
transport: isDev
|
||||
? {
|
||||
target: 'pino-pretty',
|
||||
options: {
|
||||
colorize: true,
|
||||
translateTime: 'HH:MM:ss',
|
||||
ignore: 'pid,hostname',
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
|
||||
formatters: {
|
||||
level: (label) => ({ level: label }),
|
||||
},
|
||||
|
||||
base: {
|
||||
env: process.env.NODE_ENV,
|
||||
version: process.env.APP_VERSION ?? '0.0.0',
|
||||
},
|
||||
|
||||
redact: {
|
||||
paths: ['password', 'token', 'authorization'],
|
||||
censor: '[REDACTED]',
|
||||
},
|
||||
});
|
||||
|
||||
export const dbLogger = logger.child({ module: 'database' });
|
||||
export const apiLogger = logger.child({ module: 'api' });
|
||||
```
|
||||
|
||||
## Health Check Endpoint
|
||||
|
||||
```typescript
|
||||
// src/server/functions/health.ts
|
||||
|
||||
import { createServerFn } from '@tanstack/react-start/server';
|
||||
import { db } from '@/lib/server/db';
|
||||
|
||||
interface HealthStatus {
|
||||
status: 'healthy' | 'degraded' | 'unhealthy';
|
||||
timestamp: string;
|
||||
version: string;
|
||||
checks: {
|
||||
database: boolean;
|
||||
cache: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const getHealth = createServerFn({ method: 'GET' }).handler(
|
||||
async (): Promise<HealthStatus> => {
|
||||
const checks = {
|
||||
database: false,
|
||||
cache: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await db.$queryRaw`SELECT 1`;
|
||||
checks.database = true;
|
||||
} catch {
|
||||
// Database check failed
|
||||
}
|
||||
|
||||
const allHealthy = Object.values(checks).every(Boolean);
|
||||
|
||||
return {
|
||||
status: allHealthy ? 'healthy' : 'degraded',
|
||||
timestamp: new Date().toISOString(),
|
||||
version: process.env.APP_VERSION ?? '0.0.0',
|
||||
checks,
|
||||
};
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Application Metrics
|
||||
|
||||
```typescript
|
||||
// src/lib/server/metrics.ts
|
||||
|
||||
class MetricsCollector {
|
||||
private startTime = Date.now();
|
||||
private data = {
|
||||
requests: { total: 0, errors: 0 },
|
||||
database: { queries: 0, slowQueries: 0 },
|
||||
cache: { hits: 0, misses: 0 },
|
||||
};
|
||||
|
||||
incrementRequest(isError = false): void {
|
||||
this.data.requests.total++;
|
||||
if (isError) this.data.requests.errors++;
|
||||
}
|
||||
|
||||
incrementDbQuery(isSlow = false): void {
|
||||
this.data.database.queries++;
|
||||
if (isSlow) this.data.database.slowQueries++;
|
||||
}
|
||||
|
||||
incrementCacheHit(): void {
|
||||
this.data.cache.hits++;
|
||||
}
|
||||
|
||||
incrementCacheMiss(): void {
|
||||
this.data.cache.misses++;
|
||||
}
|
||||
|
||||
getMetrics() {
|
||||
return {
|
||||
...this.data,
|
||||
uptime: Math.floor((Date.now() - this.startTime) / 1000),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const metrics = new MetricsCollector();
|
||||
```
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user