@streamlogia/javascript-sdk
v1.0.0
Published
JavaScript client for sending structured logs to the Streamlogia Log Ingestor API
Maintainers
Readme
Streamlogia JavaScript SDK
A JavaScript client library for sending structured logs to the Streamlogia Log Ingestor API.
Features
- 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
Requirements
Node.js 18 or later (uses native fetch). For older Node.js, pass a custom fetch via the fetch option.
Installation
npm install @streamlogia/javascript-sdkQuick Start
import { LogIngestorClient } from '@streamlogia/javascript-sdk'
const client = new LogIngestorClient({
apiKey: process.env.LOGINGESTOR_API_KEY,
projectId: process.env.LOGINGESTOR_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()Usage
Direct Client Logging
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
Winston Integration
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 | ✅ |
Pino Integration
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')Express Middleware
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 |
Next.js Edge Middleware
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.LOGINGESTOR_API_KEY
const PROJECT_ID = process.env.LOGINGESTOR_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.
Configuration
const client = new LogIngestorClient({
apiKey: process.env.LOGINGESTOR_API_KEY,
projectId: process.env.LOGINGESTOR_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 |
Graceful Shutdown
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)
})API Reference
new LogIngestorClient(opts)
Creates the client and starts the background flush timer.
Log methods
| 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 |
Other methods
| 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 |
Log entry shape
{
projectId: string,
level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR',
message: string,
source: string,
timestamp: string, // ISO 8601
tags: string[],
meta: object,
}Examples
| 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:
LOGINGESTOR_API_KEY=<key> LOGINGESTOR_PROJECT_ID=<id> node examples/express-app.js