Claude-skill-registry gamma-observability
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/gamma-observability" ~/.claude/skills/majiayu000-claude-skill-registry-gamma-observability && rm -rf "$T"
manifest:
skills/data/gamma-observability/SKILL.mdsource content
Gamma Observability
Overview
Implement comprehensive monitoring, logging, and tracing for Gamma integrations.
Prerequisites
- Observability stack (Prometheus, Grafana, or cloud equivalent)
- Log aggregation (ELK, CloudWatch, or similar)
- APM tool (Datadog, New Relic, or OpenTelemetry)
Three Pillars of Observability
1. Metrics
// lib/gamma-metrics.ts import { Counter, Histogram, Gauge, Registry } from 'prom-client'; const registry = new Registry(); // Request metrics const requestCounter = new Counter({ name: 'gamma_requests_total', help: 'Total Gamma API requests', labelNames: ['method', 'endpoint', 'status'], registers: [registry], }); const requestDuration = new Histogram({ name: 'gamma_request_duration_seconds', help: 'Gamma API request duration', labelNames: ['method', 'endpoint'], buckets: [0.1, 0.5, 1, 2, 5, 10, 30], registers: [registry], }); // Business metrics const presentationsCreated = new Counter({ name: 'gamma_presentations_created_total', help: 'Total presentations created', labelNames: ['style', 'user_tier'], registers: [registry], }); const rateLimitRemaining = new Gauge({ name: 'gamma_rate_limit_remaining', help: 'Remaining API calls in rate limit window', registers: [registry], }); // Instrumented client export function createInstrumentedClient() { return new GammaClient({ apiKey: process.env.GAMMA_API_KEY, interceptors: { request: (config) => { config._startTime = Date.now(); return config; }, response: (response, config) => { const duration = (Date.now() - config._startTime) / 1000; const endpoint = config.path.split('/')[1]; requestCounter.inc({ method: config.method, endpoint, status: response.status, }); requestDuration.observe( { method: config.method, endpoint }, duration ); // Update rate limit gauge const remaining = response.headers['x-ratelimit-remaining']; if (remaining) { rateLimitRemaining.set(parseInt(remaining, 10)); } return response; }, }, }); }
2. Logging
// lib/gamma-logger.ts import winston from 'winston'; const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'gamma-integration' }, transports: [ new winston.transports.Console(), new winston.transports.File({ filename: 'gamma-error.log', level: 'error' }), new winston.transports.File({ filename: 'gamma-combined.log' }), ], }); // Structured logging for Gamma operations export function logGammaRequest(operation: string, params: object) { logger.info('Gamma API request', { operation, params: sanitizeParams(params), timestamp: new Date().toISOString(), }); } export function logGammaResponse(operation: string, response: object, duration: number) { logger.info('Gamma API response', { operation, duration, success: true, responseId: response.id, }); } export function logGammaError(operation: string, error: Error, context: object) { logger.error('Gamma API error', { operation, error: error.message, stack: error.stack, context, }); } function sanitizeParams(params: object): object { const sanitized = { ...params }; // Remove sensitive fields delete sanitized.apiKey; delete sanitized.secret; return sanitized; }
3. Distributed Tracing
// lib/gamma-tracing.ts import { trace, SpanKind, SpanStatusCode } from '@opentelemetry/api'; const tracer = trace.getTracer('gamma-integration'); export async function traceGammaCall<T>( operationName: string, fn: () => Promise<T> ): Promise<T> { return tracer.startActiveSpan( `gamma.${operationName}`, { kind: SpanKind.CLIENT }, async (span) => { try { const result = await fn(); span.setAttributes({ 'gamma.operation': operationName, 'gamma.success': true, }); span.setStatus({ code: SpanStatusCode.OK }); return result; } catch (error) { span.setAttributes({ 'gamma.operation': operationName, 'gamma.success': false, 'gamma.error': error.message, }); span.setStatus({ code: SpanStatusCode.ERROR, message: error.message, }); span.recordException(error); throw error; } finally { span.end(); } } ); } // Usage const presentation = await traceGammaCall('presentations.create', () => gamma.presentations.create({ title: 'My Deck', prompt: 'AI content' }) );
Dashboard Configuration
Grafana Dashboard
{ "title": "Gamma Integration", "panels": [ { "title": "Request Rate", "type": "graph", "targets": [ { "expr": "rate(gamma_requests_total[5m])" } ] }, { "title": "Latency P95", "type": "graph", "targets": [ { "expr": "histogram_quantile(0.95, rate(gamma_request_duration_seconds_bucket[5m]))" } ] }, { "title": "Error Rate", "type": "stat", "targets": [ { "expr": "rate(gamma_requests_total{status=~'5..'}[5m]) / rate(gamma_requests_total[5m])" } ] }, { "title": "Rate Limit Remaining", "type": "gauge", "targets": [ { "expr": "gamma_rate_limit_remaining" } ] } ] }
Alert Rules
# prometheus/alerts.yml groups: - name: gamma rules: - alert: GammaHighErrorRate expr: rate(gamma_requests_total{status=~"5.."}[5m]) / rate(gamma_requests_total[5m]) > 0.05 for: 5m labels: severity: warning annotations: summary: High Gamma API error rate - alert: GammaRateLimitLow expr: gamma_rate_limit_remaining < 10 for: 1m labels: severity: critical annotations: summary: Gamma rate limit nearly exhausted - alert: GammaHighLatency expr: histogram_quantile(0.95, rate(gamma_request_duration_seconds_bucket[5m])) > 5 for: 5m labels: severity: warning annotations: summary: Gamma API latency is high
Health Check Endpoint
// routes/health.ts app.get('/health/gamma', async (req, res) => { const health = { status: 'unknown', latency: 0, rateLimit: { remaining: 0, limit: 0 }, timestamp: new Date().toISOString(), }; try { const start = Date.now(); const response = await gamma.ping(); health.latency = Date.now() - start; health.status = response.ok ? 'healthy' : 'degraded'; health.rateLimit = { remaining: response.rateLimit.remaining, limit: response.rateLimit.limit, }; } catch (error) { health.status = 'unhealthy'; } const statusCode = health.status === 'healthy' ? 200 : 503; res.status(statusCode).json(health); });
Resources
Next Steps
Proceed to
gamma-incident-runbook for incident response.