@ranimontagna/agent-toolkit 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -277
- package/docs/assets/install-plan.svg +29 -0
- package/docs/assets/install-skill-packages.svg +31 -0
- package/docs/assets/install-status.svg +32 -0
- package/package.json +10 -9
- package/setup-agent-toolkit.sh +1 -1
- package/skills/backend/fastify-best-practices/LICENSE +21 -0
- package/skills/backend/fastify-best-practices/NOTICE.md +11 -0
- package/skills/backend/fastify-best-practices/SKILL.md +75 -0
- package/skills/backend/fastify-best-practices/rules/authentication.md +521 -0
- package/skills/backend/fastify-best-practices/rules/configuration.md +217 -0
- package/skills/backend/fastify-best-practices/rules/content-type.md +387 -0
- package/skills/backend/fastify-best-practices/rules/cors-security.md +445 -0
- package/skills/backend/fastify-best-practices/rules/database.md +320 -0
- package/skills/backend/fastify-best-practices/rules/decorators.md +416 -0
- package/skills/backend/fastify-best-practices/rules/deployment.md +423 -0
- package/skills/backend/fastify-best-practices/rules/error-handling.md +412 -0
- package/skills/backend/fastify-best-practices/rules/hooks.md +464 -0
- package/skills/backend/fastify-best-practices/rules/http-proxy.md +247 -0
- package/skills/backend/fastify-best-practices/rules/logging.md +402 -0
- package/skills/backend/fastify-best-practices/rules/performance.md +425 -0
- package/skills/backend/fastify-best-practices/rules/plugins.md +320 -0
- package/skills/backend/fastify-best-practices/rules/routes.md +467 -0
- package/skills/backend/fastify-best-practices/rules/schemas.md +585 -0
- package/skills/backend/fastify-best-practices/rules/serialization.md +475 -0
- package/skills/backend/fastify-best-practices/rules/testing.md +536 -0
- package/skills/backend/fastify-best-practices/rules/typescript.md +458 -0
- package/skills/backend/fastify-best-practices/rules/websockets.md +421 -0
- package/skills/backend/fastify-best-practices/tile.json +11 -0
- package/skills/core/agent-toolkit-maintainer/SKILL.md +16 -14
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: logging
|
|
3
|
+
description: Logging with Pino in Fastify
|
|
4
|
+
metadata:
|
|
5
|
+
tags: logging, pino, debugging, observability
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Logging with Pino
|
|
9
|
+
|
|
10
|
+
## Built-in Pino Integration
|
|
11
|
+
|
|
12
|
+
Fastify uses Pino for high-performance logging:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import Fastify from 'fastify';
|
|
16
|
+
|
|
17
|
+
const app = Fastify({
|
|
18
|
+
logger: true, // Enable default logging
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Or with configuration
|
|
22
|
+
const app = Fastify({
|
|
23
|
+
logger: {
|
|
24
|
+
level: 'info',
|
|
25
|
+
transport: {
|
|
26
|
+
target: 'pino-pretty',
|
|
27
|
+
options: {
|
|
28
|
+
colorize: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Log Levels
|
|
36
|
+
|
|
37
|
+
Available log levels (in order of severity):
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
app.log.trace('Detailed debugging');
|
|
41
|
+
app.log.debug('Debugging information');
|
|
42
|
+
app.log.info('General information');
|
|
43
|
+
app.log.warn('Warning messages');
|
|
44
|
+
app.log.error('Error messages');
|
|
45
|
+
app.log.fatal('Fatal errors');
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Request-Scoped Logging
|
|
49
|
+
|
|
50
|
+
Each request has its own logger with request context:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
app.get('/users/:id', async (request) => {
|
|
54
|
+
// Logs include request ID automatically
|
|
55
|
+
request.log.info('Fetching user');
|
|
56
|
+
|
|
57
|
+
const user = await db.users.findById(request.params.id);
|
|
58
|
+
|
|
59
|
+
if (!user) {
|
|
60
|
+
request.log.warn({ userId: request.params.id }, 'User not found');
|
|
61
|
+
return { error: 'Not found' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
request.log.info({ userId: user.id }, 'User fetched');
|
|
65
|
+
return user;
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Structured Logging
|
|
70
|
+
|
|
71
|
+
Always use structured logging with objects:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// GOOD - structured, searchable
|
|
75
|
+
request.log.info({
|
|
76
|
+
action: 'user_created',
|
|
77
|
+
userId: user.id,
|
|
78
|
+
email: user.email,
|
|
79
|
+
}, 'User created successfully');
|
|
80
|
+
|
|
81
|
+
request.log.error({
|
|
82
|
+
err: error,
|
|
83
|
+
userId: request.params.id,
|
|
84
|
+
operation: 'fetch_user',
|
|
85
|
+
}, 'Failed to fetch user');
|
|
86
|
+
|
|
87
|
+
// BAD - unstructured, hard to parse
|
|
88
|
+
request.log.info(`User ${user.id} created with email ${user.email}`);
|
|
89
|
+
request.log.error(`Failed to fetch user: ${error.message}`);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Logging Configuration by Environment
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
function getLoggerConfig() {
|
|
96
|
+
if (process.env.NODE_ENV === 'production') {
|
|
97
|
+
return {
|
|
98
|
+
level: 'info',
|
|
99
|
+
// JSON output for log aggregation
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (process.env.NODE_ENV === 'test') {
|
|
104
|
+
return false; // Disable logging in tests
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Development
|
|
108
|
+
return {
|
|
109
|
+
level: 'debug',
|
|
110
|
+
transport: {
|
|
111
|
+
target: 'pino-pretty',
|
|
112
|
+
options: {
|
|
113
|
+
colorize: true,
|
|
114
|
+
translateTime: 'HH:MM:ss Z',
|
|
115
|
+
ignore: 'pid,hostname',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const app = Fastify({
|
|
122
|
+
logger: getLoggerConfig(),
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Custom Serializers
|
|
127
|
+
|
|
128
|
+
Customize how objects are serialized:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const app = Fastify({
|
|
132
|
+
logger: {
|
|
133
|
+
level: 'info',
|
|
134
|
+
serializers: {
|
|
135
|
+
// Customize request serialization
|
|
136
|
+
req: (request) => ({
|
|
137
|
+
method: request.method,
|
|
138
|
+
url: request.url,
|
|
139
|
+
headers: {
|
|
140
|
+
host: request.headers.host,
|
|
141
|
+
'user-agent': request.headers['user-agent'],
|
|
142
|
+
},
|
|
143
|
+
remoteAddress: request.ip,
|
|
144
|
+
}),
|
|
145
|
+
|
|
146
|
+
// Customize response serialization
|
|
147
|
+
res: (response) => ({
|
|
148
|
+
statusCode: response.statusCode,
|
|
149
|
+
}),
|
|
150
|
+
|
|
151
|
+
// Custom serializer for users
|
|
152
|
+
user: (user) => ({
|
|
153
|
+
id: user.id,
|
|
154
|
+
email: user.email,
|
|
155
|
+
// Exclude sensitive fields
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Use custom serializer
|
|
162
|
+
request.log.info({ user: request.user }, 'User action');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Redacting Sensitive Data
|
|
166
|
+
|
|
167
|
+
Prevent logging sensitive information:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import Fastify from 'fastify';
|
|
171
|
+
|
|
172
|
+
const app = Fastify({
|
|
173
|
+
logger: {
|
|
174
|
+
level: 'info',
|
|
175
|
+
redact: {
|
|
176
|
+
paths: [
|
|
177
|
+
'req.headers.authorization',
|
|
178
|
+
'req.headers.cookie',
|
|
179
|
+
'body.password',
|
|
180
|
+
'body.creditCard',
|
|
181
|
+
'*.password',
|
|
182
|
+
'*.secret',
|
|
183
|
+
'*.token',
|
|
184
|
+
],
|
|
185
|
+
censor: '[REDACTED]',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Child Loggers
|
|
192
|
+
|
|
193
|
+
Create child loggers with additional context:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
app.addHook('onRequest', async (request) => {
|
|
197
|
+
// Add user context to all logs for this request
|
|
198
|
+
if (request.user) {
|
|
199
|
+
request.log = request.log.child({
|
|
200
|
+
userId: request.user.id,
|
|
201
|
+
userRole: request.user.role,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Service-level child logger
|
|
207
|
+
const userService = {
|
|
208
|
+
log: app.log.child({ service: 'UserService' }),
|
|
209
|
+
|
|
210
|
+
async create(data) {
|
|
211
|
+
this.log.info({ email: data.email }, 'Creating user');
|
|
212
|
+
// ...
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Request Logging Configuration
|
|
218
|
+
|
|
219
|
+
Customize automatic request logging:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const app = Fastify({
|
|
223
|
+
logger: true,
|
|
224
|
+
disableRequestLogging: true, // Disable default request/response logs
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Custom request logging
|
|
228
|
+
app.addHook('onRequest', async (request) => {
|
|
229
|
+
request.log.info({
|
|
230
|
+
method: request.method,
|
|
231
|
+
url: request.url,
|
|
232
|
+
query: request.query,
|
|
233
|
+
}, 'Request received');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
app.addHook('onResponse', async (request, reply) => {
|
|
237
|
+
request.log.info({
|
|
238
|
+
statusCode: reply.statusCode,
|
|
239
|
+
responseTime: reply.elapsedTime,
|
|
240
|
+
}, 'Request completed');
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Logging Errors
|
|
245
|
+
|
|
246
|
+
Properly log errors with stack traces:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
app.setErrorHandler((error, request, reply) => {
|
|
250
|
+
// Log error with full details
|
|
251
|
+
request.log.error({
|
|
252
|
+
err: error, // Pino serializes error objects properly
|
|
253
|
+
url: request.url,
|
|
254
|
+
method: request.method,
|
|
255
|
+
body: request.body,
|
|
256
|
+
query: request.query,
|
|
257
|
+
}, 'Request error');
|
|
258
|
+
|
|
259
|
+
reply.code(error.statusCode || 500).send({
|
|
260
|
+
error: error.message,
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// In handlers
|
|
265
|
+
app.get('/data', async (request) => {
|
|
266
|
+
try {
|
|
267
|
+
return await fetchData();
|
|
268
|
+
} catch (error) {
|
|
269
|
+
request.log.error({ err: error }, 'Failed to fetch data');
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Log Destinations
|
|
276
|
+
|
|
277
|
+
Configure where logs are sent:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { createWriteStream } from 'node:fs';
|
|
281
|
+
|
|
282
|
+
// File output
|
|
283
|
+
const app = Fastify({
|
|
284
|
+
logger: {
|
|
285
|
+
level: 'info',
|
|
286
|
+
stream: createWriteStream('./app.log'),
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Multiple destinations with pino.multistream
|
|
291
|
+
import pino from 'pino';
|
|
292
|
+
|
|
293
|
+
const streams = [
|
|
294
|
+
{ stream: process.stdout },
|
|
295
|
+
{ stream: createWriteStream('./app.log') },
|
|
296
|
+
{ level: 'error', stream: createWriteStream('./error.log') },
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
const app = Fastify({
|
|
300
|
+
logger: pino({ level: 'info' }, pino.multistream(streams)),
|
|
301
|
+
});
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Log Rotation
|
|
305
|
+
|
|
306
|
+
Use pino-roll for log rotation:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
node app.js | pino-roll --frequency daily --extension .log
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Or configure programmatically:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { createStream } from 'rotating-file-stream';
|
|
316
|
+
|
|
317
|
+
const stream = createStream('app.log', {
|
|
318
|
+
size: '10M', // Rotate every 10MB
|
|
319
|
+
interval: '1d', // Rotate daily
|
|
320
|
+
compress: 'gzip',
|
|
321
|
+
path: './logs',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const app = Fastify({
|
|
325
|
+
logger: {
|
|
326
|
+
level: 'info',
|
|
327
|
+
stream,
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Log Aggregation
|
|
333
|
+
|
|
334
|
+
Format logs for aggregation services:
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// For ELK Stack, Datadog, etc. - use default JSON format
|
|
338
|
+
const app = Fastify({
|
|
339
|
+
logger: {
|
|
340
|
+
level: 'info',
|
|
341
|
+
// Default JSON output works with most log aggregators
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Add service metadata
|
|
346
|
+
const app = Fastify({
|
|
347
|
+
logger: {
|
|
348
|
+
level: 'info',
|
|
349
|
+
base: {
|
|
350
|
+
service: 'user-api',
|
|
351
|
+
version: process.env.APP_VERSION,
|
|
352
|
+
environment: process.env.NODE_ENV,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Request ID Tracking
|
|
359
|
+
|
|
360
|
+
Use request IDs for distributed tracing:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
const app = Fastify({
|
|
364
|
+
logger: true,
|
|
365
|
+
requestIdHeader: 'x-request-id', // Use incoming header
|
|
366
|
+
genReqId: (request) => {
|
|
367
|
+
// Generate ID if not provided
|
|
368
|
+
return request.headers['x-request-id'] || crypto.randomUUID();
|
|
369
|
+
},
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Forward request ID to downstream services
|
|
373
|
+
app.addHook('onRequest', async (request) => {
|
|
374
|
+
request.requestId = request.id;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// Include in outgoing requests
|
|
378
|
+
const response = await fetch('http://other-service/api', {
|
|
379
|
+
headers: {
|
|
380
|
+
'x-request-id': request.id,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Performance Considerations
|
|
386
|
+
|
|
387
|
+
Pino is fast, but consider:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// Avoid string concatenation in log calls
|
|
391
|
+
// BAD
|
|
392
|
+
request.log.info('User ' + user.id + ' did ' + action);
|
|
393
|
+
|
|
394
|
+
// GOOD
|
|
395
|
+
request.log.info({ userId: user.id, action }, 'User action');
|
|
396
|
+
|
|
397
|
+
// Use appropriate log levels
|
|
398
|
+
// Don't log at info level in hot paths
|
|
399
|
+
if (app.log.isLevelEnabled('debug')) {
|
|
400
|
+
request.log.debug({ details: expensiveToCompute() }, 'Debug info');
|
|
401
|
+
}
|
|
402
|
+
```
|