Marketplace

workers-observability

Cloudflare Workers observability with logging, Analytics Engine, Tail Workers, metrics, and alerting. Use for monitoring, debugging, tracing, or encountering log parsing, metric aggregation, alert configuration errors.

$ Install

git clone https://github.com/secondsky/claude-skills /tmp/claude-skills && cp -r /tmp/claude-skills/plugins/cloudflare-workers/skills/cloudflare-workers-observability ~/.claude/skills/claude-skills

// tip: Run this command in your terminal to install the skill


name: workers-observability description: Cloudflare Workers observability with logging, Analytics Engine, Tail Workers, metrics, and alerting. Use for monitoring, debugging, tracing, or encountering log parsing, metric aggregation, alert configuration errors.

Cloudflare Workers Observability

Production-grade observability for Cloudflare Workers: logging, metrics, tracing, and alerting.

Quick Start

// Structured logging with context
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const requestId = crypto.randomUUID();
    const logger = createLogger(requestId, env);

    try {
      logger.info('Request received', { method: request.method, url: request.url });

      const result = await handleRequest(request, env);

      logger.info('Request completed', { status: result.status });
      return result;
    } catch (error) {
      logger.error('Request failed', { error: error.message, stack: error.stack });
      throw error;
    }
  }
};

// Simple logger factory
function createLogger(requestId: string, env: Env) {
  return {
    info: (msg: string, data?: object) => console.log(JSON.stringify({ level: 'info', requestId, msg, ...data, timestamp: Date.now() })),
    error: (msg: string, data?: object) => console.error(JSON.stringify({ level: 'error', requestId, msg, ...data, timestamp: Date.now() })),
    warn: (msg: string, data?: object) => console.warn(JSON.stringify({ level: 'warn', requestId, msg, ...data, timestamp: Date.now() })),
  };
}

Critical Rules

  1. Always use structured JSON logging - Plain text logs are hard to parse and aggregate
  2. Include request context - Request ID, method, path in every log entry
  3. Never log sensitive data - Redact tokens, passwords, PII from logs
  4. Use appropriate log levels - ERROR for failures, WARN for recoverable issues, INFO for operations
  5. Sample high-volume logs - Use 1-10% sampling for request logs in production

Observability Components

ComponentPurposeWhen to Use
console.logBasic loggingDevelopment, debugging
Tail WorkersReal-time log streamingProduction log aggregation
Analytics EngineCustom metrics/analyticsBusiness metrics, performance tracking
LogpushLog export to external servicesLong-term storage, compliance
Workers Trace EventsDistributed tracingRequest flow debugging

Top 8 Errors Prevented

ErrorSymptomPrevention
Logs not appearingNo output in dashboardEnable "Standard" logging in wrangler.jsonc
Log truncationMessages cut off at 128KBChunk large payloads, use sampling
Tail Worker not receivingNo events processedCheck binding name matches wrangler.jsonc
Analytics Engine write failsData not recordedVerify AE binding, check blobs format
PII in logsSecurity/compliance violationImplement redaction middleware
Missing request contextCan't correlate logsAdd requestId to all log entries
Log volume explosionHigh costs, noiseImplement sampling for high-frequency events
Alerting gapsIncidents not detectedConfigure monitors for error rate thresholds

Logging Configuration

wrangler.jsonc:

{
  "name": "my-worker",
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1 // 0-1, 1 = 100% of requests
  },
  "tail_consumers": [
    {
      "service": "log-aggregator", // Tail Worker name
      "environment": "production"
    }
  ],
  "analytics_engine_datasets": [
    {
      "binding": "ANALYTICS",
      "dataset": "my_worker_metrics"
    }
  ]
}

Structured Logging Pattern

interface LogEntry {
  level: 'debug' | 'info' | 'warn' | 'error';
  message: string;
  requestId: string;
  timestamp: number;
  // Contextual data
  method?: string;
  path?: string;
  status?: number;
  duration?: number;
  // Error details
  error?: {
    name: string;
    message: string;
    stack?: string;
  };
  // Custom fields
  [key: string]: unknown;
}

class Logger {
  constructor(private requestId: string, private baseContext: object = {}) {}

  private log(level: LogEntry['level'], message: string, data?: object) {
    const entry: LogEntry = {
      level,
      message,
      requestId: this.requestId,
      timestamp: Date.now(),
      ...this.baseContext,
      ...data,
    };

    // Redact sensitive fields
    const sanitized = this.redact(entry);

    const output = JSON.stringify(sanitized);
    level === 'error' ? console.error(output) : console.log(output);
  }

  private redact(entry: LogEntry): LogEntry {
    const sensitiveKeys = ['password', 'token', 'secret', 'authorization', 'cookie'];
    const redacted = { ...entry };

    for (const key of Object.keys(redacted)) {
      if (sensitiveKeys.some(s => key.toLowerCase().includes(s))) {
        redacted[key] = '[REDACTED]';
      }
    }
    return redacted;
  }

  info(message: string, data?: object) { this.log('info', message, data); }
  warn(message: string, data?: object) { this.log('warn', message, data); }
  error(message: string, data?: object) { this.log('error', message, data); }
  debug(message: string, data?: object) { this.log('debug', message, data); }
}

Analytics Engine Usage

interface Env {
  ANALYTICS: AnalyticsEngineDataset;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const start = Date.now();
    const url = new URL(request.url);

    try {
      const response = await handleRequest(request, env);

      // Write success metric
      env.ANALYTICS.writeDataPoint({
        blobs: [request.method, url.pathname, String(response.status)],
        doubles: [Date.now() - start], // Response time in ms
        indexes: [url.pathname.split('/')[1] || 'root'], // Index for fast queries
      });

      return response;
    } catch (error) {
      // Write error metric
      env.ANALYTICS.writeDataPoint({
        blobs: [request.method, url.pathname, 'error', error.message],
        doubles: [Date.now() - start],
        indexes: ['error'],
      });
      throw error;
    }
  }
};

Tail Worker Pattern

// tail-worker.ts - Receives logs from other workers
interface TailEvent {
  scriptName: string;
  event: {
    request?: { method: string; url: string };
    response?: { status: number };
  };
  logs: Array<{
    level: string;
    message: unknown[];
    timestamp: number;
  }>;
  exceptions: Array<{
    name: string;
    message: string;
    timestamp: number;
  }>;
  outcome: 'ok' | 'exception' | 'exceededCpu' | 'exceededMemory' | 'canceled';
  eventTimestamp: number;
}

export default {
  async tail(events: TailEvent[], env: Env): Promise<void> {
    for (const event of events) {
      // Filter and forward logs
      const errorLogs = event.logs.filter(l => l.level === 'error');
      const exceptions = event.exceptions;

      if (errorLogs.length > 0 || exceptions.length > 0) {
        // Send to external logging service
        await fetch(env.LOGGING_ENDPOINT, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            scriptName: event.scriptName,
            timestamp: event.eventTimestamp,
            errors: errorLogs,
            exceptions,
            outcome: event.outcome,
          }),
        });
      }
    }
  }
};

When to Load References

Load specific references based on the task:

  • Setting up logging? β†’ Load references/logging.md for structured logging patterns, log levels, redaction
  • Building custom metrics? β†’ Load references/analytics-engine.md for Analytics Engine SQL queries, data modeling
  • Implementing log aggregation? β†’ Load references/tail-workers.md for Tail Worker patterns, external service integration
  • Creating dashboards/tracking? β†’ Load references/custom-metrics.md for business metrics, performance tracking
  • Setting up alerts? β†’ Load references/alerting.md for error rate monitoring, PagerDuty/Slack integration

Templates

TemplatePurposeUse When
templates/logging-setup.tsProduction logging classSetting up new worker with logging
templates/analytics-worker.tsAnalytics Engine integrationAdding custom metrics
templates/tail-worker.tsComplete Tail WorkerBuilding log aggregation pipeline

Scripts

ScriptPurposeCommand
scripts/setup-logging.shConfigure logging settings./setup-logging.sh
scripts/analyze-logs.shQuery and analyze logs./analyze-logs.sh --errors --last 1h

Resources

Repository

secondsky
secondsky
Author
secondsky/claude-skills/plugins/cloudflare-workers/skills/cloudflare-workers-observability
9
Stars
0
Forks
Updated2d ago
Added5d ago