@lovettbarron/cnc
v0.2.0
Published
Fire-and-forget monitoring client for [CandC](https://github.com/lovettbarron/candc). Sends heartbeats, errors, and logs to your CandC hub without ever blocking your application.
Readme
@lovettbarron/cnc
Fire-and-forget monitoring client for CandC. Sends heartbeats, errors, and logs to your CandC hub without ever blocking your application.
Install
npm install @lovettbarron/cnc
# or
pnpm add @lovettbarron/cncQuick Start
import { createClient } from '@lovettbarron/cnc'
const candc = createClient({
hubUrl: 'https://monitor.andrewlb.com',
apiKey: 'your-api-key', // from POST /v1/apps registration
})
// Start heartbeat loop (default: every 30s)
candc.start()
// Report an error
candc.reportError(new Error('Payment failed'), {
subsystem: 'payments',
severity: 'high',
})
// Send a log entry
candc.log('error', 'Unexpected response from Stripe')
// On shutdown
candc.stop()API
createClient(config): CandcClient
Creates a new CandC client instance.
Config:
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| hubUrl | string | yes | CandC hub URL (e.g. https://monitor.andrewlb.com) |
| apiKey | string | yes | App API key from hub registration |
| onError | (err: unknown) => void | no | Called on non-timeout network errors. Timeouts are always silent. |
client.start(intervalMs?)
Starts the heartbeat loop. Sends one heartbeat immediately, then repeats on the given interval (default: 30,000ms). The timer is unref()'d so it won't keep your process alive.
client.stop()
Stops the heartbeat loop. Safe to call multiple times.
client.heartbeat()
Sends a single heartbeat. Use this if you prefer manual control over the interval.
client.reportError(err, opts?)
Reports an error to the hub. Accepts Error instances or any value (non-Error values are wrapped via new Error(String(val))).
Options:
| Option | Type | Description |
|--------|------|-------------|
| subsystem | string | Optional grouping label (e.g. "payments", "scraper") |
| severity | 'low' \| 'medium' \| 'high' | Optional severity level |
client.log(level, message, fields?)
Sends a log entry to the hub's Loki instance.
| Param | Type | Description |
|-------|------|-------------|
| level | string | Log level (e.g. "info", "error") |
| message | string | Log message |
| fields | Record<string, string> | Optional metadata (note: not forwarded to hub in current version) |
Pino Transport
For apps using Pino, the package includes a transport that automatically forwards warn-level logs and above. Errors (level 50+) are dual-pushed to both /v1/logs and /v1/errors.
import pino from 'pino'
const logger = pino({
transport: {
targets: [
{ target: 'pino-pretty' },
{
target: '@lovettbarron/cnc/transport',
options: {
hubUrl: 'https://monitor.andrewlb.com',
apiKey: 'your-api-key',
},
},
],
},
})
// These are forwarded automatically:
logger.warn('High memory usage detected') // → /v1/logs
logger.error(new Error('DB connection lost')) // → /v1/logs + /v1/errors
// These are NOT forwarded (below warn threshold):
logger.info('Request handled')
logger.debug('Cache hit')Transport behavior
- Levels below 40 (warn): Ignored
- Levels 40-49 (warn): Sent to
/v1/logsonly - Levels 50+ (error, fatal): Sent to both
/v1/logsand/v1/errors - Fatal (60+): Reported with
severity: 'high'; error (50) usesseverity: 'medium' - Timeout: 5 second per request, failures are silently swallowed
Design Principles
- Fire-and-forget: All network calls are non-blocking and non-throwing. The hub being down will never crash or slow your app.
- Zero dependencies: The client uses native
fetch(Node 18+). Only the Pino transport depends onpino-abstract-transport. - Unref'd timers: The heartbeat interval won't prevent your process from exiting cleanly.
Registering Your App
Before using the client, register your app with the hub:
curl -X POST https://monitor.andrewlb.com/v1/apps \
-H 'Content-Type: application/json' \
-d '{"name": "my-app"}'The response includes your apiKey.
Requirements
- Node.js 18+ (native
fetchrequired)
License
No license specified.
