Replace or extend your existing Winston logger with LogFlow in minutes. Send structured logs, use child loggers for request context, and see everything in the LogFlow dashboard.
If your Node.js application already uses Winston, you can add LogFlow as a second transport — your existing console/file logs keep working, and LogFlow receives every log automatically.
npm install @getlogflow/js
Open wherever you create your Winston logger and add LogFlowWinstonTransport:
import winston from 'winston'
import { LogFlowWinstonTransport } from '@getlogflow/js/transports/winston'
export const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
// Keep your existing transports
new winston.transports.Console({
format: winston.format.simple(),
}),
// Add LogFlow
new LogFlowWinstonTransport({
apiKey: process.env.LOGFLOW_API_KEY!,
service: 'api', // rename to your service
environment: process.env.NODE_ENV,
release: process.env.APP_VERSION, // optional
}),
],
})
Add your API key to .env:
LOGFLOW_API_KEY=lf_your_api_key_here
That's it — every logger.info(...), logger.error(...), etc. now flows to LogFlow automatically. No other code changes needed.
Winston supports passing metadata as the second argument. LogFlow preserves all of it:
logger.info('User signed up', { userId: 'u_123', plan: 'starter', source: 'google' })
logger.warn('Rate limit hit', { ip: req.ip, path: req.path, limit: 100 })
logger.error('Payment failed', { orderId: 'ord_456', code: 'CARD_DECLINED', amount: 4999 })
In LogFlow's Logs Explorer, click any log to expand its attributes.
Instead of manually passing requestId to every log call, create a per-request child logger. Child loggers merge fixed attributes automatically:
app.use((req, res, next) => {
// Create a child logger for this request
req.log = logger.child({
traceId: req.headers['x-trace-id'] || crypto.randomUUID(),
requestId: req.id,
})
next()
})
app.post('/orders', async (req, res) => {
req.log.info('Order request received') // traceId + requestId included
const order = await createOrder(req.body)
req.log.info('Order created', { orderId: order.id }) // traceId + requestId + orderId
res.json(order)
})
In LogFlow, click View trace on any log to see the full request timeline grouped by traceId.
process.on('uncaughtException', (err) => {
logger.error('Uncaught exception', { stack: err.stack, message: err.message })
process.exit(1)
})
process.on('unhandledRejection', (reason) => {
logger.error('Unhandled rejection', { reason: String(reason) })
})
Start your server and trigger a few requests. Open LogFlow → Live Tail — logs should appear within seconds.
Filter by service: type service:api in the Logs Explorer search bar.
Will this slow down my app?
No. The SDK batches logs in the background — your logger.info(...) calls return immediately without waiting for the HTTP request.
What happens if LogFlow is unreachable?
The SDK retries up to 3 times with exponential back-off (1s → 2s → 4s). If all retries fail, logs are dropped and your onError callback is invoked if set.
What about log levels?
Winston levels are mapped to LogFlow levels: error → error, warn → warn, info → info, verbose/debug → debug, silly → trace.
Free plan available. No credit card required. Up and running in 2 minutes.
Get started freePino Logging Integration
Connect Pino (the fastest Node.js logger) to LogFlow in under 5 minutes.
Add Logging to a Next.js App
Set up LogFlow in a Next.js project — capture server errors, API route logs, and frontend exceptions in under 15 minutes.
Structured Logging in Express.js
Add production-ready structured logging to any Express.js app in under 10 minutes.