Log in Node.js with Pino. Use when a user asks to add structured logging, improve logging performance, configure log levels, format logs for production, or replace console.log with proper logging.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add TerminalSkills/skills --skill pino --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Pino?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/terminalskills-pino)More formats (shields.io, HTML) on the badges page.
---
name: pino
description: >-
Log in Node.js with Pino. Use when a user asks to add structured logging,
improve logging performance, configure log levels, format logs for
production, or replace console.log with proper logging.
license: Apache-2.0
compatibility: 'Node.js 16+, Express, Fastify, Next.js'
metadata:
author: terminal-skills
version: 1.0.0
category: devops
tags:
- pino
- logging
- nodejs
- structured
- observability
---
# Pino
## Overview
Pino is the fastest Node.js logger — 5x faster than Winston. It outputs structured JSON logs by default, making them parseable by log aggregators (Datadog, Loki, ELK). Async logging ensures logging never blocks the event loop.
## Instructions
### Step 1: Basic Setup
```typescript
// lib/logger.ts — Pino configuration
import pino from 'pino'
export const logger = pino({
level: process.env.LOG_LEVEL || 'info',
transport: process.env.NODE_ENV === 'development'
? { target: 'pino-pretty', options: { colorize: true } } // pretty-print in dev
: undefined, // JSON in production
formatters: {
level: (label) => ({ level: label }), // "level": "info" not "level": 30
},
base: {
service: 'api',
version: process.env.npm_package_version,
},
})
// Usage
logger.info('Server started')
logger.info({ port: 3000, env: 'production' }, 'Server listening')
logger.error({ err, userId: '123' }, 'Payment processing failed')
logger.warn({ latencyMs: 2500, endpoint: '/api/reports' }, 'Slow request detected')
```
### Step 2: Express Integration
```typescript
// server.ts — Request logging middleware
import express from 'express'
import pinoHttp from 'pino-http'
import { logger } from './lib/logger'
const app = express()
app.use(pinoHttp({
logger,
autoLogging: true,
customLogLevel: (req, res, err) => {
if (res.statusCode >= 500 || err) return 'error'
if (res.statusCode >= 400) return 'warn'
return 'info'
},
customSuccessMessage: (req, res) =>
`${req.method} ${req.url} ${res.statusCode}`,
serializers: {
req: (req) => ({
method: req.method,
url: req.url,
query: req.query,
userAgent: req.headers['user-agent'],
}),
res: (res) => ({
statusCode: res.statusCode,
}),
},
}))
```
### Step 3: Child Loggers
```typescript
// Add context that persists across a request lifecycle
function createRequestLogger(req) {
return logger.child({
requestId: req.headers['x-request-id'] || crypto.randomUUID(),
userId: req.user?.id,
ip: req.ip,
})
}
// Every log from this logger includes requestId, userId, ip
const reqLogger = createRequestLogger(req)
reqLogger.info('Processing order')
reqLogger.info({ orderId: 'ord_123', amount: 99.99 }, 'Order created')
// Output: {"level":"info","requestId":"abc-123","userId":"user-456","orderId":"ord_123","amount":99.99,"msg":"Order created"}
```
## Guidelines
- Always use structured JSON logging in production — not string interpolation.
- Context first, message second: `logger.info({ orderId }, 'Order created')` not `logger.info('Order created for ' + orderId)`.
- Use child loggers to add request context (requestId, userId) that propagates to all logs.
- Pino's async mode (`pino({ transport })`) prevents logging from blocking the event loop.
- Use `pino-pretty` only in development — JSON logs are for machines, not humans.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!