@fabbahiense/pulsar-pino-transport
v0.3.0
Published
Pino transport for Pulsar observability platform + Express request logger middleware
Maintainers
Readme
@fabbahiense/pulsar-pino-transport
Pino transport for Pulsar — send your application logs to Pulsar in real-time.
Install
npm install @fabbahiense/pulsar-pino-transportUsage
import pino from 'pino'
const logger = pino({
transport: {
target: '@fabbahiense/pulsar-pino-transport',
options: {
url: 'https://your-pulsar-instance.com',
apiKey: 'psk_your_api_key',
source: 'my-api', // optional, default: 'default'
batchSize: 50, // optional, default: 50
flushInterval: 2000, // optional, default: 2000ms
},
},
})
logger.info('Server started')
logger.error({ traceId: 'abc-123' }, 'Something went wrong')Features
- Batched log delivery (configurable batch size)
- Automatic retry with exponential backoff (3 attempts)
- In-memory queue — no logs lost on temporary network failures
- Zero dependencies (uses native
fetch) - TypeScript types included
- Dual CJS/ESM package
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| url | string | — | Pulsar server URL (required) |
| apiKey | string | — | API key for authentication (required) |
| source | string | 'default' | Service name in Pulsar |
| batchSize | number | 50 | Logs buffered before flush |
| flushInterval | number | 2000 | Flush interval in ms |
Trace ID
The transport automatically picks up trace IDs from these fields:
traceIdtrace_idrequestIdreq.id
logger.info({ traceId: 'my-trace-id' }, 'Processing request')Capturing request/response bodies (with pino-http)
The transport itself doesn't intercept HTTP — pino is a logger, not a middleware. To get rich request/response logs in Pulsar, pair it with pino-http:
npm install pino-httpimport express from 'express'
import pino from 'pino'
import pinoHttp from 'pino-http'
const logger = pino({
transport: {
target: '@fabbahiense/pulsar-pino-transport',
options: { url: process.env.PULSAR_URL, apiKey: process.env.PULSAR_API_KEY },
},
})
const app = express()
app.use(express.json())
app.use(pinoHttp({
logger,
// Serialize request body
serializers: {
req(req) {
return {
method: req.method,
url: req.url,
headers: redact(req.headers),
requestBody: req.raw?.body, // Pulsar UI recognizes this key
query: req.query,
params: req.params,
}
},
res(res) {
return {
statusCode: res.statusCode,
headers: redact(res.getHeaders?.() ?? {}),
// responseBody requires intercepting res.write/res.end — see below
}
},
},
customLogLevel(_req, res, err) {
if (err || res.statusCode >= 500) return 'error'
if (res.statusCode >= 400) return 'warn'
return 'info'
},
}))
function redact(headers: Record<string, unknown>): Record<string, unknown> {
const out: Record<string, unknown> = {}
const drop = new Set(['authorization', 'cookie', 'set-cookie', 'x-api-key'])
for (const [k, v] of Object.entries(headers)) {
out[k] = drop.has(k.toLowerCase()) ? '[REDACTED]' : v
}
return out
}No painel lateral do Pulsar isso aparece em seções dedicadas: Request body, Headers, Query, Path params, status, duration.
Capturando response body (avançado)
Pino-http não captura response body nativamente. Para capturar, intercepte res.write/res.end:
app.use((req, res, next) => {
const chunks: Buffer[] = []
const originalWrite = res.write.bind(res)
const originalEnd = res.end.bind(res)
res.write = function (chunk: any) { if (chunk) chunks.push(Buffer.from(chunk)); return originalWrite(chunk) }
res.end = function (chunk: any) { if (chunk) chunks.push(Buffer.from(chunk)); return originalEnd(chunk) }
res.on('finish', () => {
try {
const body = Buffer.concat(chunks).toString('utf8').slice(0, 10240)
logger.info({ responseBody: tryParseJson(body), statusCode: res.statusCode, traceId: req.id }, `${req.method} ${req.url} ${res.statusCode}`)
} catch {}
})
next()
})
function tryParseJson(s: string) { try { return JSON.parse(s) } catch { return s } }Mais simples? Se você puder, considere
@fabbahiense/pulsar-nodeque já vem comPulsar.middleware({ captureResponseBody: true })configurado.
Recognized metadata keys
Pulsar UI auto-detects these key names and renders dedicated sections:
| Section | Recognized keys |
|---|---|
| Request body | body, requestBody, reqBody, payload, input |
| Response body | response, responseBody, output, result, data |
| Request headers | headers, requestHeaders, reqHeaders |
| Response headers | responseHeaders, resHeaders |
| Query | query, queryParams, qs |
| Path params | params, pathParams |
License
MIT
