Skip to content

Commit 17eb13a

Browse files
authored
feat: add structured logging with Pino (#73)
* feat: add structured logging with Pino - Implemented Pino logger with environment-based configuration - Added automatic sensitive data redaction for security - Created child logger support for module-specific logging - Pretty-print in development, JSON in production - Added comprehensive test coverage - Updated documentation Closes #66 * fix: improve test coverage for logger module to 100% - Added comprehensive tests for all environment configurations - Added tests for production, development, and test modes - Added tests for correlation ID handling - Added tests for sensitive data redaction patterns - Fixed TypeScript type imports - Fixed unbound method issue - Achieved 100% code coverage * fix: ensure withContext method is preserved on child loggers - Addressed PR review feedback from GitHub Copilot - Implemented recursive wrapper to ensure all child loggers have withContext method - Added test to verify withContext preservation through multiple levels of nesting - Prevents potential runtime errors when chaining withContext calls * docs: update changeset to reflect PR improvements - Added note about 100% test coverage achievement - Added note about withContext preservation fix from PR feedback - Updated child logger description to mention proper method preservation * docs: add comprehensive user documentation for logging - Added usage examples and configuration details to README.md - Created ADR documenting the decision to use Pino for structured logging - Added logging guidelines to CONTRIBUTING.md for contributors - Included code examples showing proper logging patterns - Documented security features and environment configurations * docs: add OpenTelemetry integration documentation and roadmap - Created OBSERVABILITY.md documenting the OpenTelemetry roadmap - Added detailed comments about future OpenTelemetry integration points - Documented migration path and configuration examples - Listed compatible observability backends - Enhanced inline documentation in logger module with OpenTelemetry examples * docs: update changeset to include documentation additions - Added documentation section listing all docs created - Noted OpenTelemetry migration path documentation - Included all user-facing documentation updates
1 parent abca75d commit 17eb13a

File tree

11 files changed

+1079
-11
lines changed

11 files changed

+1079
-11
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'agentic-node-ts-starter': minor
3+
---
4+
5+
feat: add structured logging with Pino
6+
7+
Implements structured logging foundation with Pino logger:
8+
9+
- Environment-based configuration (development, production, test)
10+
- Automatic sensitive data redaction for security
11+
- Child logger creation for module-specific logging with proper method preservation
12+
- Pretty-print formatting in development, JSON in production
13+
- OpenTelemetry integration placeholders with documented migration path
14+
- Comprehensive test coverage (100%) for all logger functionality
15+
- Fixed withContext method preservation on child loggers (addresses PR feedback)
16+
17+
Documentation:
18+
19+
- Added usage examples and configuration guide to README.md
20+
- Created Architecture Decision Record (ADR) for logging choice
21+
- Added logging guidelines to CONTRIBUTING.md
22+
- Created OBSERVABILITY.md with OpenTelemetry roadmap
23+
- Enhanced inline documentation with implementation examples
24+
25+
Closes #66

CLAUDE.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,24 @@ Common properties to test:
105105
- Idempotence: `f(f(a)) === f(a)`
106106
- Round-trip: `decode(encode(a)) === a`
107107

108-
#### 3. Import Extensions
108+
#### 3. Structured Logging Pattern
109+
110+
Use Pino logger for structured logging:
111+
112+
```typescript
113+
import { createChildLogger } from './logger.js';
114+
115+
const logger = createChildLogger('module-name');
116+
117+
// Log with context
118+
logger.info({ userId: '123', action: 'login' }, 'User logged in');
119+
120+
// Sensitive data is automatically redacted
121+
logger.info({ password: 'secret', safe: 'data' }, 'Request processed');
122+
// Output will redact password field
123+
```
124+
125+
#### 4. Import Extensions
109126

110127
Always use `.js` extension in imports for ES modules:
111128

CONTRIBUTING.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,35 @@ git checkout -b fix/your-fix-name
5050
- Add tests for new functionality
5151
- Update documentation as needed
5252

53-
### 3. Run Quality Checks
53+
### 3. Use Structured Logging
54+
55+
When adding logging to your code:
56+
57+
```typescript
58+
import { logger, createChildLogger } from './logger.js';
59+
60+
// Use module-specific loggers
61+
const log = createChildLogger('module-name');
62+
63+
// Include context in logs
64+
log.info({ userId, action }, 'User action performed');
65+
66+
// Handle errors properly
67+
log.error({ err: error, context }, 'Operation failed');
68+
69+
// Never log sensitive data directly
70+
// These fields are automatically redacted: password, token, api_key, etc.
71+
```
72+
73+
**Logging Guidelines:**
74+
75+
- Use appropriate log levels (debug, info, warn, error, fatal)
76+
- Include relevant context as structured data (first parameter)
77+
- Keep log messages descriptive but concise (second parameter)
78+
- Use child loggers for module-specific logging
79+
- Never use console.log in production code
80+
81+
### 4. Run Quality Checks
5482

5583
Before committing, you can manually run all checks:
5684

@@ -67,7 +95,7 @@ Individual checks:
6795
- `pnpm format` - Prettier formatting check
6896
- `pnpm test` - Run tests with coverage
6997

70-
### 4. Add a Changeset
98+
### 5. Add a Changeset
7199

72100
**Required:** Every PR must include a changeset. The CI will fail without one.
73101

@@ -100,7 +128,7 @@ For changes that don't affect users (like CI updates, tests, internal refactorin
100128
pnpm changeset --empty
101129
```
102130

103-
### 5. Commit Your Changes
131+
### 6. Commit Your Changes
104132

105133
This project uses [Conventional Commits](https://www.conventionalcommits.org/):
106134

@@ -131,7 +159,7 @@ Pre-commit hooks will automatically:
131159

132160
Note: The pre-commit hook runs `pnpm precommit` which executes ALL quality checks. This ensures code quality but may take longer than typical pre-commit hooks.
133161

134-
### 6. Push and Create a Pull Request
162+
### 7. Push and Create a Pull Request
135163

136164
```bash
137165
git push origin your-branch-name

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ A **batteries-included** starting point for building software with an **agentic
1515
- **Testing:** Vitest 3.2+ with V8 coverage
1616
- **Property Testing:** fast-check 4.2+
1717
- **Validation:** Zod 4.1+ for runtime type safety
18+
- **Logging:** Pino 9.9+ for structured logging with environment-based configuration
1819

1920
### Code Quality
2021

@@ -52,6 +53,48 @@ pnpm verify
5253
pnpm test:watch
5354
```
5455

56+
## 🔧 Usage Examples
57+
58+
### Logging
59+
60+
The project includes structured logging with Pino, configured for different environments:
61+
62+
```typescript
63+
import { logger, createChildLogger } from './logger.js';
64+
65+
// Basic logging
66+
logger.info('Application started');
67+
logger.error({ err: error }, 'An error occurred');
68+
69+
// Module-specific logging
70+
const moduleLogger = createChildLogger('auth-module');
71+
moduleLogger.info({ userId: '123' }, 'User authenticated');
72+
73+
// Context-aware logging
74+
const requestLogger = logger.withContext({
75+
requestId: 'abc-123',
76+
userId: 'user-456',
77+
});
78+
requestLogger.info('Processing request');
79+
80+
// Sensitive data is automatically redacted
81+
logger.info(
82+
{
83+
username: 'john',
84+
password: 'secret123', // Will be redacted
85+
apiKey: 'key-789', // Will be redacted
86+
},
87+
'User login attempt',
88+
);
89+
```
90+
91+
#### Logging Configuration
92+
93+
- **Development**: Pretty-printed, colorized output for readability
94+
- **Production**: Structured JSON logs with correlation ID support
95+
- **Test**: Silent by default, configurable via `LOG_LEVEL` env var
96+
- **Security**: Automatic redaction of sensitive fields (password, token, api_key, etc.)
97+
5598
## 📚 Available Scripts
5699

57100
### Development

docs/OBSERVABILITY.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Observability Roadmap
2+
3+
## Current State
4+
5+
The project currently implements structured logging with Pino, which provides:
6+
7+
- Structured JSON logging in production
8+
- Correlation ID support via environment variables
9+
- Context propagation through child loggers
10+
- Performance-optimized logging
11+
12+
## OpenTelemetry Integration (Future)
13+
14+
The logger module has been designed with OpenTelemetry integration in mind. The following placeholders and patterns are ready for future implementation:
15+
16+
### Trace Context Integration
17+
18+
The `withTraceContext` helper function is ready to integrate with OpenTelemetry:
19+
20+
```typescript
21+
import { logger, withTraceContext } from './logger.js';
22+
import { trace } from '@opentelemetry/api';
23+
24+
// Future implementation example
25+
const span = trace.getActiveSpan();
26+
const spanContext = span?.spanContext();
27+
28+
const tracedLogger = withTraceContext(logger, spanContext?.traceId, spanContext?.spanId);
29+
30+
tracedLogger.info('Operation with trace context');
31+
```
32+
33+
### Correlation ID from OpenTelemetry
34+
35+
Currently, correlation IDs come from environment variables. In the future, they will be extracted from OpenTelemetry context:
36+
37+
```typescript
38+
// Current (placeholder)
39+
correlationId: process.env.CORRELATION_ID;
40+
41+
// Future (with OpenTelemetry)
42+
correlationId: trace.getActiveSpan()?.spanContext().traceId;
43+
```
44+
45+
### Planned OpenTelemetry Features
46+
47+
1. **Automatic Trace Context Propagation**
48+
- Extract trace and span IDs from active OpenTelemetry context
49+
- Automatically attach to all log entries
50+
- Correlate logs with distributed traces
51+
52+
2. **Metrics Integration**
53+
- Export custom metrics alongside logs
54+
- Track application performance indicators
55+
- Integrate with Prometheus/Grafana
56+
57+
3. **Distributed Tracing**
58+
- Instrument HTTP requests/responses
59+
- Track database queries
60+
- Monitor external service calls
61+
- Support for W3C Trace Context propagation
62+
63+
4. **Exporters Configuration**
64+
65+
```typescript
66+
// Future configuration example
67+
import { NodeSDK } from '@opentelemetry/sdk-node';
68+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
69+
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
70+
71+
const sdk = new NodeSDK({
72+
traceExporter: new OTLPTraceExporter({
73+
url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
74+
}),
75+
metricReader: new PeriodicExportingMetricReader({
76+
exporter: new OTLPMetricExporter({
77+
url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
78+
}),
79+
}),
80+
});
81+
```
82+
83+
## Migration Path
84+
85+
When implementing OpenTelemetry:
86+
87+
1. **Install OpenTelemetry packages**
88+
89+
```bash
90+
pnpm add @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
91+
```
92+
93+
2. **Initialize OpenTelemetry SDK**
94+
- Create `src/telemetry.ts` for SDK initialization
95+
- Configure exporters (OTLP, Jaeger, Zipkin, etc.)
96+
- Set up auto-instrumentation
97+
98+
3. **Update Logger Integration**
99+
- Modify `getLoggerConfig()` to extract correlation IDs from OpenTelemetry context
100+
- Update `withTraceContext()` to use active span context
101+
- Add span events for important log entries
102+
103+
4. **Instrument Application Code**
104+
- Add custom spans for business operations
105+
- Track custom metrics
106+
- Implement baggage propagation for metadata
107+
108+
## Environment Variables
109+
110+
Future OpenTelemetry configuration will use standard environment variables:
111+
112+
```bash
113+
# Service identification
114+
OTEL_SERVICE_NAME=agentic-node-ts-starter
115+
OTEL_SERVICE_VERSION=1.0.0
116+
117+
# Exporter configuration
118+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
119+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
120+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics
121+
122+
# Resource attributes
123+
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.namespace=myapp
124+
125+
# Sampling
126+
OTEL_TRACES_SAMPLER=parentbased_traceidratio
127+
OTEL_TRACES_SAMPLER_ARG=0.1
128+
```
129+
130+
## Benefits of Future Integration
131+
132+
1. **Unified Observability**: Logs, traces, and metrics in one platform
133+
2. **Root Cause Analysis**: Correlate logs with specific trace spans
134+
3. **Performance Monitoring**: Track latency and throughput across services
135+
4. **Error Tracking**: Automatically capture and trace errors
136+
5. **Service Dependencies**: Visualize service communication patterns
137+
6. **SLO/SLI Tracking**: Monitor service level objectives with metrics
138+
139+
## Compatible Backends
140+
141+
The OpenTelemetry integration will support various observability backends:
142+
143+
- **Cloud Providers**
144+
- AWS X-Ray
145+
- Google Cloud Trace
146+
- Azure Application Insights
147+
148+
- **Open Source**
149+
- Jaeger
150+
- Zipkin
151+
- Grafana Tempo
152+
- SigNoz
153+
154+
- **Commercial**
155+
- Datadog
156+
- New Relic
157+
- Honeycomb
158+
- Dynatrace
159+
- Splunk
160+
161+
## References
162+
163+
- [OpenTelemetry JavaScript Documentation](https://opentelemetry.io/docs/instrumentation/js/)
164+
- [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/)
165+
- [W3C Trace Context](https://www.w3.org/TR/trace-context/)
166+
- [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/)

0 commit comments

Comments
 (0)