A JavaScript client library for sending structured logs to the Streamlogia Log Ingestor API.
- Structured log ingestion (DEBUG, INFO, WARN, ERROR)
- Asynchronous batching with configurable flush interval and batch size
- Winston transport integration
- Pino destination stream integration
- Express middleware for automatic request/response logging (method, path, status, duration, response size)
- Next.js Edge Runtime compatible
- Works in Node.js 18+ with native
fetch— no extra dependencies required
Node.js 18 or later (uses native fetch). For older Node.js, pass a custom fetch via the fetch option.
npm install @streamlogia/javascript-sdkimport { LogIngestorClient } from '@streamlogia/javascript-sdk'
const client = new LogIngestorClient({
apiKey: process.env.STREAMLOGIA_API_KEY,
projectId: process.env.STREAMLOGIA_PROJECT_ID,
source: 'my-service',
})
await client.info('application started')
await client.info('user signed in', { meta: { userId: 'u_123' }, tags: ['auth'] })
// Flush remaining logs and stop the background timer before exit
await client.close()The client exposes a method for each log level:
client.debug('cache miss', { meta: { key: 'session:42' } })
client.info('order placed', { meta: { orderId: 'ord_99', total: 49.95 }, tags: ['orders'] })
client.warn('rate limit approaching', { meta: { remaining: 5 } })
client.error('payment failed', { meta: { error: err.message }, tags: ['payments'] })Each method accepts:
message— log message stringopts.meta— optional key/value metadata objectopts.tags— optional string array for filteringopts.source— override the default source for this entry
createWinstonTransport adapts the client to a Winston transport — analogous to NewSlogHandler in the Go SDK. Pass the Transport base class from winston-transport so the SDK stays free of hard dependencies.
import Transport from 'winston-transport'
import winston from 'winston'
import { LogIngestorClient } from '@streamlogia/javascript-sdk'
const client = new LogIngestorClient({ /* ... */ })
const LogTransport = client.createWinstonTransport(Transport)
const logger = winston.createLogger({
level: 'debug',
transports: [
new winston.transports.Console({ format: winston.format.simple() }),
new LogTransport(),
],
})
logger.info('order placed', { orderId: 'ord_123', amount: 49.99 })
logger.error('payment failed', { customerId: 'c_42', error: err.message })Adding Console alongside LogTransport fans logs out to both stdout and the ingestor simultaneously — the same pattern as MultiHandler in the Go SDK. Stdout is captured by systemd and visible via journalctl -u your-service -f.
Note: logs sent via
logger.*(Winston) go to both destinations. Logs generated byexpressMiddleware()are written directly to the client and do not pass through Winston — they reach the console via the client's built-in console mirroring (console: trueby default). Setconsole: falseto suppress them:const client = new LogIngestorClient({ ..., console: false })
Log source Console (stdout) Ingestor logger.info/warn/error/...✅ via Winston ✅ expressMiddleware()access logs✅ via client ✅
pinoDestination() returns a Writable stream that consumes Pino's NDJSON output and forwards each record to the ingestor:
import pino from 'pino'
import { LogIngestorClient } from '@streamlogia/javascript-sdk'
const client = new LogIngestorClient({ /* ... */ })
const logger = pino({ level: 'debug' }, pino.multistream([
{ stream: process.stdout }, // stdout
{ stream: client.pinoDestination() }, // ingestor
]))
logger.info({ orderId: 'ord_123' }, 'order placed')
logger.error({ customerId: 'c_42', err }, 'payment failed')Mount expressMiddleware() once at the top of your Express app. It automatically logs one entry per request — method, path, status code, duration, response size, IP, user agent, and X-Request-Id (if present). No per-route code needed.
import express from 'express'
import { LogIngestorClient } from '@streamlogia/javascript-sdk'
const client = new LogIngestorClient({ /* ... */ })
const app = express()
app.use(express.json())
app.use(client.expressMiddleware())The log level is determined by the response status code:
| Status range | Level |
|---|---|
| 2xx / 3xx | INFO |
| 4xx | WARN |
| 5xx | ERROR |
Options:
app.use(client.expressMiddleware({ logBody: true }))| Option | Default | Description |
|---|---|---|
logBody |
false |
Include the parsed request body in meta |
Place this at the root of your Next.js project as middleware.js. It runs on the Edge Runtime where response lifecycle hooks are not available, so the request is logged on the way in.
// middleware.js
import { NextResponse } from 'next/server'
const API_KEY = process.env.STREAMLOGIA_API_KEY
const PROJECT_ID = process.env.STREAMLOGIA_PROJECT_ID
export async function middleware(request) {
const entry = {
projectId: PROJECT_ID,
level: 'INFO',
message: `${request.method} ${request.nextUrl.pathname}`,
source: 'nextjs-edge',
timestamp: new Date().toISOString(),
tags: ['http', 'nextjs'],
meta: {
method: request.method,
path: request.nextUrl.pathname,
userAgent: request.headers.get('user-agent'),
},
}
// Fire-and-forget — never let logging block the response
fetch('https://api.streamlogia.com/v1/ingest', {
method: 'POST',
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify([entry]),
}).catch(() => {})
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}See examples/nextjs-middleware.js for the full version.
const client = new LogIngestorClient({
apiKey: process.env.STREAMLOGIA_API_KEY,
projectId: process.env.STREAMLOGIA_PROJECT_ID,
source: 'order-service',
batchSize: 50,
flushIntervalMs: 10_000,
fetch: customFetchFn, // optional: custom fetch implementation
onError: (err) => { ... }, // optional: called when an ingest request fails
console: true, // optional: mirror every log to the console
})| Option | Default | Description |
|---|---|---|
apiKey |
— | API key (required) |
projectId |
— | Project UUID (required) |
baseURL |
"https://api.streamlogia.com" |
Override the ingestor API base URL |
source |
"unknown" |
Default source field on every log entry |
batchSize |
1 |
Flush automatically after accumulating n entries |
flushIntervalMs |
5000 |
Background flush interval in milliseconds |
fetch |
globalThis.fetch |
Custom fetch implementation |
onError |
console.error |
Called when an ingest HTTP request fails |
console |
true |
Mirror every log entry to the console as well |
Call client.close() before your process exits to flush any buffered entries and stop the background timer.
process.on('SIGTERM', async () => {
await client.close()
process.exit(0)
})Creates the client and starts the background flush timer.
| Method | Description |
|---|---|
client.debug(message, opts?) |
Log at DEBUG level |
client.info(message, opts?) |
Log at INFO level |
client.warn(message, opts?) |
Log at WARN level |
client.error(message, opts?) |
Log at ERROR level |
| Method | Description |
|---|---|
client.ingest(entries) |
Send entries immediately, bypassing the queue |
client.flush() |
Drain the internal queue |
client.close() |
Flush and stop the background timer |
client.expressMiddleware(opts?) |
Express middleware for automatic request logging |
client.createWinstonTransport(Transport) |
Returns a Winston transport class bound to this client |
client.pinoDestination() |
Returns a Pino-compatible Writable destination stream |
{
projectId: string,
level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR',
message: string,
source: string,
timestamp: string, // ISO 8601
tags: string[],
meta: object,
}| File | Description |
|---|---|
examples/express-app.js |
Express app with Winston integration and request middleware |
examples/nextjs-middleware.js |
Next.js Edge Runtime middleware |
Run the Express example:
STREAMLOGIA_API_KEY=<key> STREAMLOGIA_PROJECT_ID=<id> node examples/express-app.js