@wai-industries/polylogger
v1.0.4
Published
SDK for sending logs, audits, and API access events to PolyLogs service
Maintainers
Readme
@wai-industries/polylogger
SDK for sending logs, audits, and API access events to the PolyLogs service.
Installation
npm install @wai-industries/polyloggerEnvironment Variables
POLY_LOGS_SERVICE_URL- URL of the poly-logs service (required if not passed via options)JWT_SECRET- Secret key for signing JWT tokens (required)SERVICE_NAME- Name of your service (default:poly-service)POLY_LOGS_TIMEOUT_MS- Request timeout in milliseconds (default:2000)LOG_LEVEL- Winston log level (default:info)LOGGER_FORMAT- Logger format:jsonor default colorized (default: colorized)
Quick Start
1. Trace ID Middleware (Add First)
Add trace ID middleware early in your Express app to ensure trace IDs are available:
import express from 'express';
import { traceMiddleware } from '@wai-industries/polylogger';
const app = express();
// Add trace middleware first
app.use(traceMiddleware);
// ... rest of your middleware2. API Access Middleware
Automatically log all API requests:
import { createApiAccessMiddleware } from '@wai-industries/polylogger';
// Add after trace middleware
app.use(createApiAccessMiddleware({
serviceName: 'my-service',
// Either set POLY_LOGS_SERVICE_URL in env or pass polyLogsUrl here
// polyLogsUrl: 'http://poly-logs.my-domain:5200',
// Optional: Only needed if requests don't include organization-id or organization-code in headers
// If your API sends 'organization-id' or 'organization-code' in request headers, you don't need this
// organizationCode: 'ORG123',
}));3. Log Middleware (Optional)
Automatically log request/response events:
import { createLogMiddleware } from '@wai-industries/polylogger';
app.use(createLogMiddleware({
serviceName: 'my-service',
// Either set POLY_LOGS_SERVICE_URL in env or pass polyLogsUrl here
// polyLogsUrl: 'http://poly-logs.my-domain:5200',
// Optional: Only needed if requests don't include organization-id or organization-code in headers
// If your API sends 'organization-id' or 'organization-code' in request headers, you don't need this
// organizationCode: 'ORG123',
}));4. Winston Logger (Manual Logging)
Note: For automatic request logging with route/method/status, use createLogMiddleware (see step 3) instead of manual logger functions. Manual loggers are useful for custom logging outside of HTTP requests.
Use winston logger that forwards to poly-logs:
import { logInfo, logError, logWarn, logDebug } from '@wai-industries/polylogger';
// Basic usage - logs to console AND forwards to poly-logs (fire-and-forget, non-blocking)
void logInfo('User logged in', { userId: '123', organizationCode: 'ORG-456' });
void logError('Failed to process request', { error: 'Connection timeout', organizationCode: 'ORG-456' });
void logWarn('Rate limit approaching', { current: 90, limit: 100, organizationCode: 'ORG-456' });
void logDebug('Debug information', { data: { /* ... */ }, organizationCode: 'ORG-456' });
// With route/method/status (useful for custom logging or background jobs)
void logInfo('Custom operation completed', {
organizationCode: 'ORG-456',
route: '/api/custom-endpoint',
method: 'POST',
status: 200,
userId: 'user-123',
customField: 'value'
});
void logError('Background job failed', {
organizationCode: 'ORG-456',
route: '/jobs/process-payment',
method: 'POST',
status: 500,
error: 'Payment gateway timeout'
});5. Audit Events (Manual Placement)
Emit audit events where needed in your controllers:
import { emitAudit } from '@wai-industries/polylogger';
// In your controller
export async function createUser(req: Request, res: Response) {
try {
const user = await User.create(req.body);
// Emit audit event (fire-and-forget, non-blocking)
void emitAudit(req, {
action: 'USER_CREATE',
entityType: 'user',
entityId: user._id.toString(),
result: 'SUCCESS',
metadata: { email: user.email },
});
res.json({ data: user });
} catch (error) {
// Emit failure audit (fire-and-forget, non-blocking)
void emitAudit(req, {
action: 'USER_CREATE',
entityType: 'user',
entityId: req.body.id || 'unknown',
result: 'FAILURE',
metadata: { error: error.message },
});
res.status(500).json({ error: 'Failed to create user' });
}
}API Reference
Middleware
traceMiddleware
Express middleware that ensures trace ID is present in request.
app.use(traceMiddleware);createApiAccessMiddleware(options?)
Creates Express middleware to automatically log API access events.
Options:
serviceName?: string- Service name (default:SERVICE_NAMEenv var)organizationCode?: string- Default organization code when request doesn't provideorganization-idororganization-codein headersskip?: string[]- Routes to skip (default:/metrics,/favicon.ico,/health,/status)polyLogsUrl?: string- Override poly-logs URLtimeoutMs?: number- Request timeout
Note: The middleware automatically extracts organization from request headers (organization-id or organization-code). Only provide organizationCode in options if your requests don't include these headers.
createLogMiddleware(options?)
Creates Express middleware to automatically log request/response events.
Options: Same as createApiAccessMiddleware
Logger Functions
logInfo(message, context?)
logError(message, context?)
logWarn(message, context?)
logDebug(message, context?)
Log messages that are printed to console and forwarded to poly-logs.
⚠️ Important: For automatic request logging with route/method/status, use createLogMiddleware instead. These manual logger functions are useful for custom logging outside of HTTP requests (e.g., background jobs, scheduled tasks, or custom business logic).
Note: These functions are async but designed for fire-and-forget usage. Use void instead of await for non-blocking behavior (recommended):
// ✅ Recommended: Fire-and-forget (non-blocking)
void logInfo('User created', { organizationCode: 'ORG-123' });
// ⚠️ Not recommended: Blocks until HTTP request completes
await logInfo('User created', { organizationCode: 'ORG-123' });Context:
organizationCode: string(required) - Organization code. If neitherorganizationCodenororgIdis present, the event will NOT be sent to poly-logs.userId?: string- User IDtraceId?: string- Trace IDservice?: string- Service namejwtToken?: string- JWT token (if available)route?: string- API route (e.g., '/api/users') - useful for filtering in Elasticsearchmethod?: string- HTTP method (e.g., 'GET', 'POST') - useful for filtering in Elasticsearchstatus?: number- HTTP status code (e.g., 200, 404, 500) - useful for filtering in Elasticsearch- Any other fields will be included in context
Important: Always send
organizationCode(or ensureorgIdis present via headers/middleware).
If neither is provided, the SDK will log to console only and will drop the event for poly-logs.
Example with route/method/status:
// Custom logging with route/method/status for Elasticsearch filtering
void logInfo('Payment processed', {
organizationCode: 'ORG-123',
route: '/api/payments',
method: 'POST',
status: 200,
userId: 'user-456',
amount: 100.50
});
void logError('Payment failed', {
organizationCode: 'ORG-123',
route: '/api/payments',
method: 'POST',
status: 500,
error: 'Insufficient funds'
});Audit Emitter
emitAudit(req, event, options?)
Emit audit event to poly-logs.
Note: This function is async but designed for fire-and-forget usage. Use void instead of await for non-blocking behavior (recommended):
// ✅ Recommended: Fire-and-forget (non-blocking)
// If request has organization-id or organization-code in headers, it's used automatically
void emitAudit(req, {
action: 'USER_CREATE',
entityType: 'user',
entityId: user._id,
result: 'SUCCESS',
});
// If request doesn't have organization headers, pass organizationCode in options
void emitAudit(req, {
action: 'USER_CREATE',
entityType: 'user',
entityId: user._id,
result: 'SUCCESS',
}, {
organizationCode: 'ORG-123' // Only needed if headers don't include organization-id or organization-code
});
// ⚠️ Not recommended: Blocks until HTTP request completes
await emitAudit(req, { ... });Event:
action: string- Action name (e.g.,USER_CREATE)entityType: string- Entity type (e.g.,user)entityId: string- Entity IDresult: 'SUCCESS' | 'FAILURE'- Resultmetadata?: Record<string, unknown>- Additional metadata
Options:
organizationCode?: string- Organization code to identify the organization (only needed if request headers don't includeorganization-idororganization-code)jwtToken?: string- Override JWT token
Note: The function automatically extracts organization from request headers (organization-id or organization-code). Only provide organizationCode in options if your requests don't include these headers.
Helper Functions
getTraceId(req)
Extract trace ID from request.
import { getTraceId } from '@wai-industries/polylogger';
const traceId = getTraceId(req);Token Handling
The SDK automatically handles JWT tokens:
- If token in request header: Uses
Authorization: Bearer <token>from request - If no token: Generates service token using
JWT_SECRET
Organization Identification
Organizations are identified primarily by organizationCode to avoid exposing internal Mongo _id values, but the SDK supports both for flexibility.
⚠️ Critical: If a log/audit/API access event does not have either organizationCode or orgId, the SDK will skip sending it to poly-logs.
Always ensure organizationCode is available (preferred) for every event.
How It Works
Automatic Detection (Preferred):
- If your request includes
organization-idororganization-codein headers, the SDK uses it automatically - No need to pass
organizationCodeto middleware functions or logger calls - This is the recommended approach for inter-service communication
- If your request includes
Manual Provision:
- If requests don't include organization headers, you can pass
organizationCodeto:- Middleware options:
createApiAccessMiddleware({ organizationCode: 'ORG-123' }) - Logger context:
logInfo('message', { organizationCode: 'ORG-123' }) - Audit options:
emitAudit(req, event, { organizationCode: 'ORG-123' })
- Middleware options:
- If requests don't include organization headers, you can pass
Priority Order:
- Request header
organization-id(MongoDB_id) - works fine if available - Request header
organization-code(public code) - preferred to avoid exposing MongoDB IDs - Function parameter
organizationCode- fallback when headers are missing
- Request header
Best Practices
- Use headers when possible: If your API gateway or middleware can attach
organization-codeto all requests, you don't need to pass it manually - Avoid exposing MongoDB IDs: Prefer
organization-codein headers overorganization-idwhen possible - Fallback to function parameters: Only pass
organizationCodeto functions when headers aren't available
TypeScript Support
Full TypeScript support with exported types:
import type {
LogEvent,
AuditEvent,
ApiAccessEvent,
SDKConfig,
MiddlewareOptions,
} from '@wai-industries/polylogger';License
ISC
