customerio-observability

Set up Customer.io monitoring and observability. Use when implementing metrics, logging, alerting, or dashboards for Customer.io integrations. Trigger with phrases like "customer.io monitoring", "customer.io metrics", "customer.io dashboard", "customer.io alerts". allowed-tools: Read, Write, Edit, Bash(kubectl:*), Bash(curl:*) version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>

Allowed Tools

No tools specified

Provided by Plugin

customerio-pack

Claude Code skill pack for Customer.io (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the customerio-pack plugin:

/plugin install customerio-pack@claude-code-plugins-plus

Click to copy

Instructions

# Customer.io Observability ## Overview Implement comprehensive observability for Customer.io integrations including metrics, logging, tracing, and alerting. ## Prerequisites - Customer.io integration deployed - Monitoring infrastructure (Prometheus, Grafana, etc.) - Log aggregation system ## Key Metrics | Metric | Type | Description | |--------|------|-------------| | `customerio_api_latency_ms` | Histogram | API call latency | | `customerio_api_requests_total` | Counter | Total API requests | | `customerio_api_errors_total` | Counter | API error count | | `customerio_email_sent_total` | Counter | Emails sent | | `customerio_email_delivered_total` | Counter | Emails delivered | | `customerio_email_bounced_total` | Counter | Email bounces | | `customerio_webhook_received_total` | Counter | Webhooks received | ## Instructions ### Step 1: Metrics Collection ```typescript // lib/metrics.ts import { Counter, Histogram, Registry } from 'prom-client'; const register = new Registry(); // API metrics export const apiLatency = new Histogram({ name: 'customerio_api_latency_ms', help: 'Customer.io API call latency in milliseconds', labelNames: ['operation', 'status'], buckets: [10, 25, 50, 100, 250, 500, 1000, 2500, 5000], registers: [register] }); export const apiRequests = new Counter({ name: 'customerio_api_requests_total', help: 'Total Customer.io API requests', labelNames: ['operation', 'status'], registers: [register] }); export const apiErrors = new Counter({ name: 'customerio_api_errors_total', help: 'Total Customer.io API errors', labelNames: ['operation', 'error_type'], registers: [register] }); // Email metrics export const emailsSent = new Counter({ name: 'customerio_email_sent_total', help: 'Total emails sent via Customer.io', labelNames: ['campaign_type'], registers: [register] }); export const emailsDelivered = new Counter({ name: 'customerio_email_delivered_total', help: 'Total emails delivered', labelNames: ['campaign_type'], registers: [register] }); export const emailsBounced = new Counter({ name: 'customerio_email_bounced_total', help: 'Total email bounces', labelNames: ['bounce_type'], registers: [register] }); // Webhook metrics export const webhooksReceived = new Counter({ name: 'customerio_webhook_received_total', help: 'Total webhooks received from Customer.io', labelNames: ['event_type'], registers: [register] }); export { register }; ``` ### Step 2: Instrumented Client ```typescript // lib/customerio-instrumented.ts import { TrackClient, RegionUS } from '@customerio/track'; import * as metrics from './metrics'; export class InstrumentedCustomerIO { private client: TrackClient; constructor(siteId: string, apiKey: string) { this.client = new TrackClient(siteId, apiKey, { region: RegionUS }); } async identify(userId: string, attributes: Record): Promise { const timer = metrics.apiLatency.startTimer({ operation: 'identify' }); try { await this.client.identify(userId, attributes); timer({ status: 'success' }); metrics.apiRequests.inc({ operation: 'identify', status: 'success' }); } catch (error: any) { timer({ status: 'error' }); metrics.apiRequests.inc({ operation: 'identify', status: 'error' }); metrics.apiErrors.inc({ operation: 'identify', error_type: error.statusCode || 'unknown' }); throw error; } } async track(userId: string, event: string, data?: Record): Promise { const timer = metrics.apiLatency.startTimer({ operation: 'track' }); try { await this.client.track(userId, { name: event, data }); timer({ status: 'success' }); metrics.apiRequests.inc({ operation: 'track', status: 'success' }); } catch (error: any) { timer({ status: 'error' }); metrics.apiRequests.inc({ operation: 'track', status: 'error' }); metrics.apiErrors.inc({ operation: 'track', error_type: error.statusCode || 'unknown' }); throw error; } } } ``` ### Step 3: Structured Logging ```typescript // lib/logger.ts import pino from 'pino'; export const logger = pino({ name: 'customerio', level: process.env.LOG_LEVEL || 'info', formatters: { level: (label) => ({ level: label }) }, base: { service: 'customerio-integration', environment: process.env.NODE_ENV } }); // Logging wrapper for Customer.io operations export function logOperation( operation: string, userId: string, data: any, result: 'success' | 'error', error?: Error ) { const logData = { operation, userId, result, data: sanitizeForLogging(data), ...(error && { error: { message: error.message, stack: error.stack } }) }; if (result === 'error') { logger.error(logData, `Customer.io ${operation} failed`); } else { logger.info(logData, `Customer.io ${operation} succeeded`); } } // Remove PII from logs function sanitizeForLogging(data: any): any { if (!data) return data; const sanitized = { ...data }; const piiFields = ['email', 'phone', 'address', 'ssn']; for (const field of piiFields) { if (sanitized[field]) { sanitized[field] = '[REDACTED]'; } } return sanitized; } ``` ### Step 4: Distributed Tracing ```typescript // lib/tracing.ts import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api'; const tracer = trace.getTracer('customerio-integration'); export async function withTracing( operationName: string, attributes: Record, operation: () => Promise ): Promise { return tracer.startActiveSpan( `customerio.${operationName}`, { kind: SpanKind.CLIENT, attributes: { 'customerio.operation': operationName, ...attributes } }, async (span) => { try { const result = await operation(); span.setStatus({ code: SpanStatusCode.OK }); return result; } catch (error: any) { span.setStatus({ code: SpanStatusCode.ERROR, message: error.message }); span.recordException(error); throw error; } finally { span.end(); } } ); } // Usage await withTracing('identify', { userId }, () => client.identify(userId, attributes) ); ``` ### Step 5: Grafana Dashboard ```json { "dashboard": { "title": "Customer.io Integration", "panels": [ { "title": "API Latency (p50, p95, p99)", "type": "timeseries", "targets": [ { "expr": "histogram_quantile(0.50, rate(customerio_api_latency_ms_bucket[5m]))", "legendFormat": "p50" }, { "expr": "histogram_quantile(0.95, rate(customerio_api_latency_ms_bucket[5m]))", "legendFormat": "p95" }, { "expr": "histogram_quantile(0.99, rate(customerio_api_latency_ms_bucket[5m]))", "legendFormat": "p99" } ] }, { "title": "API Request Rate", "type": "timeseries", "targets": [ { "expr": "rate(customerio_api_requests_total[5m])", "legendFormat": "{{operation}} - {{status}}" } ] }, { "title": "Error Rate", "type": "stat", "targets": [ { "expr": "sum(rate(customerio_api_errors_total[5m])) / sum(rate(customerio_api_requests_total[5m])) * 100", "legendFormat": "Error Rate %" } ], "fieldConfig": { "defaults": { "thresholds": { "steps": [ { "value": 0, "color": "green" }, { "value": 1, "color": "yellow" }, { "value": 5, "color": "red" } ] } } } }, { "title": "Email Delivery Funnel", "type": "bargauge", "targets": [ { "expr": "sum(customerio_email_sent_total)", "legendFormat": "Sent" }, { "expr": "sum(customerio_email_delivered_total)", "legendFormat": "Delivered" }, { "expr": "sum(customerio_email_bounced_total)", "legendFormat": "Bounced" } ] } ] } } ``` ### Step 6: Alerting Rules ```yaml # prometheus/alerts/customerio.yml groups: - name: customerio rules: - alert: CustomerIOHighErrorRate expr: | sum(rate(customerio_api_errors_total[5m])) / sum(rate(customerio_api_requests_total[5m])) > 0.05 for: 5m labels: severity: critical annotations: summary: Customer.io API error rate > 5% description: Error rate is {{ $value | printf "%.2f" }}% - alert: CustomerIOHighLatency expr: | histogram_quantile(0.99, rate(customerio_api_latency_ms_bucket[5m])) > 5000 for: 10m labels: severity: warning annotations: summary: Customer.io p99 latency > 5s description: p99 latency is {{ $value | printf "%.0f" }}ms - alert: CustomerIOHighBounceRate expr: | sum(rate(customerio_email_bounced_total[1h])) / sum(rate(customerio_email_sent_total[1h])) > 0.05 for: 30m labels: severity: warning annotations: summary: Email bounce rate > 5% description: Bounce rate is {{ $value | printf "%.2f" }}% - alert: CustomerIOWebhookProcessingFailed expr: | sum(rate(customerio_webhook_errors_total[5m])) > 0 for: 5m labels: severity: critical annotations: summary: Customer.io webhook processing failures description: {{ $value }} webhooks failed in last 5 minutes ``` ## Observability Checklist - [ ] API latency metrics collected - [ ] Error rate tracking enabled - [ ] Structured logging implemented - [ ] Distributed tracing configured - [ ] Grafana dashboard created - [ ] Alert rules defined - [ ] PII redacted from logs - [ ] Log retention policy set ## Error Handling | Issue | Solution | |-------|----------| | Missing metrics | Check metric registration | | High cardinality | Reduce label values | | Log volume too high | Adjust log level | ## Resources - [Prometheus Best Practices](https://prometheus.io/docs/practices/) - [OpenTelemetry Node.js](https://opentelemetry.io/docs/instrumentation/js/) ## Next Steps After observability setup, proceed to `customerio-advanced-troubleshooting` for debugging.

Skill file: plugins/saas-packs/customerio-pack/skills/customerio-observability/SKILL.md