Skills pino

install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/pino" ~/.claude/skills/terminalskills-skills-pino && rm -rf "$T"
manifest: skills/pino/SKILL.md
safety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
  • references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content

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

// 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

// 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

// 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.