spinal-obs-node
Version:
WithSpinal cost-aware OpenTelemetry SDK for Node.js
294 lines (231 loc) • 11.6 kB
Markdown
**SPINAL-OBS-NODE** is Spinal's cost tracking library built on top of OpenTelemetry. It works by automatically instrumenting HTTP libraries and attaching a processor to existing OpenTelemetry setups. This dual approach allows it to integrate seamlessly with existing observability frameworks while selectively forwarding AI/LLM operations and billing events to Spinal's platform.
**What this SDK does**
- **Seamlessly integrates** with existing OpenTelemetry setups (Logfire, vanilla OpenTelemetry, or any OTEL-compatible framework)
- **Auto-instruments** Node apps via OpenTelemetry (HTTP today; LLM providers via HTTP for now)
- **Tags contexts** with `spinal.*` baggage for aggregation and cost grouping
- **Scrubs sensitive data** before export (API keys, tokens, PII patterns)
- **Selective span processing** - only sends relevant AI/billing spans to Spinal
- **Exports spans** to a Spinal endpoint when configured (cloud mode) or stores locally (local mode)
This repo is the Node SDK piece of Spinal's system. It is designed to work standalone today and later connect to a cloud dashboard (FastAPI + ClickHouse) when you opt in.
### Key Features
- **OpenTelemetry Integration**: Works with existing OTEL setups without disruption
- **Automatic Instrumentation**: Captures HTTP requests and OpenAI API calls automatically
- **Contextual Tagging**: Adds user and workflow context to spans for better tracking
- **Privacy-First**: Built-in data scrubbing for sensitive information
- **Dual Modes**: Local mode (free) and cloud mode (with backend dashboard)
### Modes
- **Cloud mode (available now):** send spans to a Spinal endpoint when `SPINAL_API_KEY` is set.
- **Local mode (available now):** store spans locally and provide basic cost analysis and usage stats with a CLI (no backend, no Cloudflare). This gives a taste of Spinal without sending data.
### Install
```bash
npm install spinal-obs-node
```
CLI (optional):
```bash
npx spinal status
```
### Quickstart (local mode)
```ts
import { configure, instrumentHTTP, instrumentOpenAI, tag, shutdown } from 'spinal-obs-node'
// 1) Configure in local mode (no API key required)
configure({
mode: 'local',
localStorePath: './spans.jsonl' // Optional: custom path
})
// 2) Enable built-in instrumentations
instrumentHTTP()
instrumentOpenAI()
// 3) Add contextual tags at boundaries
const t = tag({ aggregationId: 'signup-flow', tenant: 'acme' })
// ... your code ...
t.dispose()
// 4) View collected data
// npx spinal local
// 5) On shutdown
await shutdown()
```
```ts
import { configure, instrumentHTTP, instrumentOpenAI, tag, shutdown } from 'spinal-obs-node'
// 1) Configure once at app init
configure({
// Defaults to process.env.SPINAL_TRACING_ENDPOINT or 'https://cloud.withspinal.com'
// endpoint: 'https://cloud.withspinal.com',
// apiKey defaults to process.env.SPINAL_API_KEY
})
// 2) Enable built-in instrumentations
instrumentHTTP()
instrumentOpenAI()
// 3) Add contextual tags at boundaries
const t = tag({ aggregationId: 'signup-flow', tenant: 'acme' })
// ... your code ...
t.dispose()
// 4) On shutdown
await shutdown()
```
- **`SPINAL_API_KEY`**: required in cloud mode
- **`SPINAL_TRACING_ENDPOINT`**: defaults to `https://cloud.withspinal.com`
- **`SPINAL_MODE`**: set to `'local'` for local mode (default if no API key)
- **`SPINAL_LOCAL_STORE_PATH`**: custom path for local data storage
- **`SPINAL_DISABLE_LOCAL_MODE`**: set to `'true'` to force cloud mode (requires `SPINAL_API_KEY`)
- **`SPINAL_EXCLUDE_OPENAI`**: set to `'true'` to disable OpenAI tracking (default: false)
- **`SPINAL_EXCLUDED_HOSTS`**: comma-separated list of hosts to exclude from HTTP tracking (default: `api.anthropic.com,api.azure.com`)
- Tuning (optional): `SPINAL_PROCESS_MAX_QUEUE_SIZE`, `SPINAL_PROCESS_MAX_EXPORT_BATCH_SIZE`, `SPINAL_PROCESS_SCHEDULE_DELAY`, `SPINAL_PROCESS_EXPORT_TIMEOUT`
#### Configuration API
```typescript
configure({
// Mode and API Configuration
mode?: 'cloud' | 'local' // Explicitly set mode
apiKey?: string // API key for cloud mode
endpoint?: string // Custom endpoint URL
// Local Mode Options
disableLocalMode?: boolean // Force cloud mode (requires API key)
localStorePath?: string // Custom path for local data storage
// Performance Tuning
maxQueueSize?: number // Max spans in export queue (default: 2048)
maxExportBatchSize?: number // Max spans per export batch (default: 512)
scheduleDelayMs?: number // Export schedule delay (default: 5000ms)
exportTimeoutMs?: number // Export timeout (default: 30000ms)
// Advanced Options
headers?: Record<string, string> // Custom headers for cloud mode
timeoutMs?: number // Request timeout (default: 5000ms)
scrubber?: Scrubber // Custom data scrubbing logic
opentelemetryLogLevel?: DiagLogLevel // OpenTelemetry log level
})
```
#### Configuration Examples
**Force Cloud Mode (Disable Local Fallback)**
```typescript
configure({
apiKey: 'your-api-key',
disableLocalMode: true // Will throw error if no API key provided
})
```
**Custom Local Storage Path**
```typescript
configure({
localStorePath: '/tmp/my-app-spans.jsonl' // Store in custom location
})
```
**Environment Variable Override**
```bash
export SPINAL_LOCAL_STORE_PATH="/var/log/spinal/spans.jsonl"
export SPINAL_DISABLE_LOCAL_MODE="true"
```
**Analytics Examples**
```bash
npx spinal cost --format json --since 7d
npx spinal cost --by-model --format table
npx spinal cost --trends --since 30d
npx spinal usage --tokens --since 24h
npx spinal usage --by-model --format summary
npx spinal performance --since 7d
npx spinal optimize --since 30d
npx spinal models --since 7d
npx spinal responses --since 24h
```
```bash
npx spinal local
npx spinal local --format summary
npx spinal local --format json
npx spinal report
npx spinal report --since 7d
npx spinal cost
npx spinal cost --by-model
npx spinal cost --by-aggregation
npx spinal cost --trends
npx spinal usage
npx spinal usage --tokens
npx spinal performance
npx spinal models
npx spinal aggregations
npx spinal trends
npx spinal optimize
npx spinal responses
npx spinal content
npx spinal status
npx spinal login
npx spinal init
```
The CLI provides comprehensive analytics for cost optimization and usage insights:
**Cost Analysis (`spinal cost`)**
- Track total costs and cost trends over time
- Breakdown costs by model, aggregation ID, or time period
- Export data in table, JSON, CSV, or summary formats
**Usage Analytics (`spinal usage`)**
- Monitor API call patterns and token usage
- Analyze usage by model, aggregation, or time period
- Track input/output token ratios and efficiency
**Performance Insights (`spinal performance`, `spinal optimize`)**
- Identify performance bottlenecks and optimization opportunities
- Get recommendations for cost reduction
- Monitor response times and error rates
**Model Analysis (`spinal models`, `spinal responses`)**
- Compare performance across different models
- Analyze response patterns and content
- Track model-specific costs and usage
### Data handling and privacy
- Attributes matching common secret/PII patterns are scrubbed to `[Scrubbed]` before export.
- Baggage keys under the `spinal.*` namespace are attached to spans for aggregation.
- Default exporter excludes certain vendor hosts from HTTP collection to avoid redundant chatter.
### Local data storage
- In local mode, data is stored in `.spinal/spans.jsonl` in your project directory.
- **Recommended**: Add `.spinal/` to your `.gitignore` to avoid committing local telemetry data.
### Why this SDK (business intent)
- Developer-first, like how Stripe/Sentry/PostHog seeded adoption: free local SDK → optional paid cloud.
- Local mode provides immediate value (usage + cost insights) without any backend.
- Cloud mode later unlocks real-time analytics, team dashboards, ClickHouse-backed queries, and enterprise controls.
### Roadmap
- ✅ Local storage of spans + pricing calculators (estimates only in local)
- ✅ Terminal CLI: `spinal login`, `spinal status`, `spinal report` (pretty usage/cost views)
- Optional cloud connect using dashboard-backend auth when `SPINAL_MODE=cloud`
- Richer LLM adapters beyond HTTP (tokens, model-aware costing)
### Development & CI/CD
This package uses automated CI/CD for seamless publishing:
- **Tests**: Unit tests and E2E tests run on PR merge
- **Versioning**: Manual version management with semantic versioning
- **Publishing**: Automatic npm publish on main branch pushes
- **Duplicate Prevention**: Won't publish same version twice
- **Cursor Rules**: `.cursorrules` file included for AI coding assistance
**Release Workflow**: Update version → Push to main → CI publishes → Create GitHub release
**Workflow**:
1. Create pull request to `main` branch
2. On merge, GitHub Action runs lint + unit tests + E2E tests
3. If all tests pass, version is bumped (patch)
4. Package is published to npm
5. Git tag is created and pushed
**Required Secrets**:
- `NPM_TOKEN`: npm authentication token for publishing
- `OPENAI_API_KEY`: OpenAI API key for E2E tests (see [GitHub Secrets Setup](./docs/GITHUB_SECRETS.md))
**📖 [Quickstart Guide](./docs/QUICKSTART.md)**
Get started in minutes with step-by-step instructions.
**📦 [Installation Guide](./docs/INSTALLATION.md)**
What you get when you install the SDK and dependency details.
**📊 [What Gets Tracked](./docs/TRACKING.md)**
Detailed explanation of tracking capabilities and data captured.
**🏠 [Local Mode Guide](./docs/LOCAL_MODE.md)**
Complete guide to local mode storage and data management.
**🖥️ [CLI Commands](./docs/CLI_COMMANDS.md)**
Complete reference for all CLI commands and usage examples.
**🚀 [Release Guide](./docs/RELEASES.md)**
Complete release workflow and version management.
### Repository boundaries
- This Node SDK should be a **public repo** and versioned/published to npm.
- Keep it separate from cloud/backend repos. Later, it can optionally connect to the Spinal cloud.
### Similar ecosystems
- Think `mermaidchart.com` distribution model: dev-first, easy install, great docs; the SDK is the product interface.
MIT