
Monitoring Logging
- 78 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
monitoring-logging is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- monitoring-logging
- AI & Agent Building
- AI-coding skill
Monitoring Logging by the numbers
- 78 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,313 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill monitoring-loggingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 78 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Monitoring & Logging
Overview
Application observability through logging, metrics collection, monitoring dashboards, and alerting systems.
---
Structured Logging
Pino Logger (Node.js)
import pino from 'pino';
// Base logger configuration
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
bindings: () => ({}), // Remove pid and hostname
},
timestamp: pino.stdTimeFunctions.isoTime,
redact: {
paths: ['password', 'token', 'authorization', '*.password', '*.token'],
censor: '[REDACTED]',
},
});
// Child logger with context
function createRequestLogger(req: Request) {
return logger.child({
requestId: req.headers['x-request-id'] || crypto.randomUUID(),
method: req.method,
path: req.path,
userAgent: req.headers['user-agent'],
userId: req.user?.id,
});
}
// Express middleware
app.use((req, res, next) => {
req.log = createRequestLogger(req);
const startTime = Date.now();
res.on('finish', () => {
const duration = Date.now() - startTime;
req.log.info({
statusCode: res.statusCode,
duration,
contentLength: res.get('content-length'),
}, 'request completed');
});
next();
});
// Usage in handlers
app.get('/api/users/:id', async (req, res) => {
req.log.info({ userId: req.params.id }, 'fetching user');
try {
const user = await getUser(req.params.id);
req.log.debug({ user: user.id }, 'user found');
res.json(user);
} catch (error) {
req.log.error({ error }, 'failed to fetch user');
res.status(500).json({ error: 'Internal error' });
}
});Log Levels
// Log level guidelines
logger.trace('Detailed debugging info'); // 10 - Very verbose
logger.debug('Debugging information'); // 20 - Debug mode only
logger.info('Normal operation events'); // 30 - Default level
logger.warn('Warning conditions'); // 40 - Potential issues
logger.error('Error conditions'); // 50 - Errors that need attention
logger.fatal('System-critical errors'); // 60 - System failure
// Contextual logging
logger.info({ orderId, userId, amount }, 'order placed');
logger.error({ error: err.message, stack: err.stack }, 'payment failed');
logger.warn({ retryCount, maxRetries }, 'retry attempt');Log Aggregation Format
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"message": "request completed",
"service": "api",
"version": "1.2.3",
"environment": "production",
"requestId": "abc-123",
"traceId": "xyz-789",
"method": "GET",
"path": "/api/users/123",
"statusCode": 200,
"duration": 45,
"userId": "user-456"
}---
Metrics Collection
Prometheus Metrics
import { Counter, Histogram, Gauge, Registry, collectDefaultMetrics } from 'prom-client';
const register = new Registry();
// Collect default Node.js metrics
collectDefaultMetrics({ register });
// HTTP request metrics
const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'path', 'status'],
registers: [register],
});
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'path'],
buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
registers: [register],
});
// Business metrics
const ordersTotal = new Counter({
name: 'orders_total',
help: 'Total number of orders',
labelNames: ['status', 'payment_method'],
registers: [register],
});
const activeUsers = new Gauge({
name: 'active_users',
help: 'Number of currently active users',
registers: [register],
});
const orderAmount = new Histogram({
name: 'order_amount_dollars',
help: 'Distribution of order amounts',
buckets: [10, 50, 100, 250, 500, 1000, 5000],
registers: [register],
});
// Middleware to collect metrics
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer({
method: req.method,
path: req.route?.path || req.path,
});
res.on('finish', () => {
end();
httpRequestsTotal
.labels(req.method, req.route?.path || req.path, res.statusCode.toString())
.inc();
});
next();
});
// Metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.send(await register.metrics());
});
// Business metric usage
async function createOrder(order: Order) {
// ... create order
ordersTotal.labels(order.status, order.paymentMethod).inc();
orderAmount.observe(order.total);
}Custom Metrics Patterns
// Rate limiting metrics
const rateLimitHits = new Counter({
name: 'rate_limit_hits_total',
help: 'Number of rate limit hits',
labelNames: ['endpoint', 'user_tier'],
});
// Cache metrics
const cacheHits = new Counter({
name: 'cache_hits_total',
help: 'Number of cache hits',
labelNames: ['cache_name'],
});
const cacheMisses = new Counter({
name: 'cache_misses_total',
help: 'Number of cache misses',
labelNames: ['cache_name'],
});
// Database metrics
const dbQueryDuration = new Histogram({
name: 'db_query_duration_seconds',
help: 'Database query duration',
labelNames: ['operation', 'table'],
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1],
});
const dbConnectionPool = new Gauge({
name: 'db_connection_pool_size',
help: 'Database connection pool size',
labelNames: ['state'], // active, idle, waiting
});
// Queue metrics
const queueSize = new Gauge({
name: 'queue_size',
help: 'Number of items in queue',
labelNames: ['queue_name'],
});
const jobDuration = new Histogram({
name: 'job_duration_seconds',
help: 'Job processing duration',
labelNames: ['job_type', 'status'],
});---
Alerting
Alert Rules (Prometheus)
# prometheus/alerts.yml
groups:
- name: application
rules:
# High error rate
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"
# High latency
- alert: HighLatency
expr: |
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
> 1
for: 5m
labels:
severity: warning
annotations:
summary: "High latency detected"
description: "95th percentile latency is {{ $value }}s"
# Service down
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.instance }} is down"
# High memory usage
- alert: HighMemoryUsage
expr: |
process_resident_memory_bytes / 1024 / 1024 / 1024 > 4
for: 10m
labels:
severity: warning
annotations:
summary: "High memory usage"
description: "Memory usage is {{ $value | humanize }}GB"
- name: business
rules:
# Low order rate
- alert: LowOrderRate
expr: |
sum(rate(orders_total[1h])) < 10
for: 30m
labels:
severity: warning
annotations:
summary: "Order rate is below normal"
# Payment failures
- alert: HighPaymentFailures
expr: |
sum(rate(orders_total{status="failed"}[15m]))
/ sum(rate(orders_total[15m])) > 0.1
for: 10m
labels:
severity: critical
annotations:
summary: "High payment failure rate"PagerDuty Integration
import axios from 'axios';
interface Alert {
severity: 'critical' | 'error' | 'warning' | 'info';
summary: string;
source: string;
details?: Record<string, any>;
}
async function sendPagerDutyAlert(alert: Alert) {
const event = {
routing_key: process.env.PAGERDUTY_ROUTING_KEY,
event_action: 'trigger',
dedup_key: `${alert.source}-${alert.summary}`,
payload: {
summary: alert.summary,
severity: alert.severity,
source: alert.source,
custom_details: alert.details,
timestamp: new Date().toISOString(),
},
};
await axios.post(
'https://events.pagerduty.com/v2/enqueue',
event
);
}
// Resolve alert
async function resolvePagerDutyAlert(dedupKey: string) {
await axios.post('https://events.pagerduty.com/v2/enqueue', {
routing_key: process.env.PAGERDUTY_ROUTING_KEY,
event_action: 'resolve',
dedup_key: dedupKey,
});
}---
Dashboards
Grafana Dashboard JSON
{
"title": "Application Overview",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (status)",
"legendFormat": "{{status}}"
}
]
},
{
"title": "Latency (p95)",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, path))",
"legendFormat": "{{path}}"
}
]
},
{
"title": "Error Rate",
"type": "stat",
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) * 100"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 1 },
{ "color": "red", "value": 5 }
]
}
}
}
},
{
"title": "Active Users",
"type": "stat",
"targets": [
{ "expr": "active_users" }
]
}
]
}---
Health Checks
import { Router } from 'express';
const healthRouter = Router();
// Liveness probe - is the app running?
healthRouter.get('/health/live', (req, res) => {
res.json({ status: 'ok' });
});
// Readiness probe - is the app ready to serve traffic?
healthRouter.get('/health/ready', async (req, res) => {
const checks = await Promise.allSettled([
checkDatabase(),
checkRedis(),
checkExternalApi(),
]);
const results = {
database: checks[0].status === 'fulfilled' ? 'ok' : 'error',
redis: checks[1].status === 'fulfilled' ? 'ok' : 'error',
externalApi: checks[2].status === 'fulfilled' ? 'ok' : 'error',
};
const allHealthy = Object.values(results).every(s => s === 'ok');
res.status(allHealthy ? 200 : 503).json({
status: allHealthy ? 'ok' : 'degraded',
checks: results,
timestamp: new Date().toISOString(),
});
});
async function checkDatabase() {
const start = Date.now();
await db.query('SELECT 1');
return { latency: Date.now() - start };
}
async function checkRedis() {
const start = Date.now();
await redis.ping();
return { latency: Date.now() - start };
}
async function checkExternalApi() {
const start = Date.now();
await fetch('https://api.example.com/health', { timeout: 5000 });
return { latency: Date.now() - start };
}---
Related Skills
- [[reliability-engineering]] - SRE practices
- [[devops-cicd]] - CI/CD monitoring
- [[cloud-platforms]] - Cloud monitoring
# Docker Compose Monitoring Stack Template
# Usage: docker-compose -f docker-compose.monitoring.yml up -d
# Includes: Prometheus, Grafana, Node Exporter, cAdvisor
version: '3.8'
services:
# ===========================================
# Prometheus - Metrics Collection
# ===========================================
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./alerts:/etc/prometheus/alerts:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
- '--web.enable-lifecycle'
# ===========================================
# Grafana - Visualization
# ===========================================
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning:ro
depends_on:
- prometheus
# ===========================================
# Node Exporter - Host Metrics
# ===========================================
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
# ===========================================
# cAdvisor - Container Metrics
# ===========================================
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
devices:
- /dev/kmsg
# ===========================================
# Alertmanager - Alert Management
# ===========================================
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
restart: unless-stopped
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
- alertmanager_data:/alertmanager
volumes:
prometheus_data:
grafana_data:
alertmanager_data:
# ===========================================
# Access URLs
# ===========================================
# Prometheus: http://localhost:9090
# Grafana: http://localhost:3001 (admin/admin)
# Node Exporter: http://localhost:9100/metrics
# cAdvisor: http://localhost:8080
# Alertmanager: http://localhost:9093
{
"__comment": "Grafana Dashboard Template - Node.js Application Metrics",
"__usage": "Import via Grafana UI: Dashboards → Import → Upload JSON",
"annotations": {
"list": []
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"title": "Request Rate",
"type": "stat",
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 },
"targets": [
{
"expr": "sum(rate(http_requests_total[5m]))",
"legendFormat": "req/s"
}
],
"fieldConfig": {
"defaults": {
"unit": "reqps"
}
}
},
{
"title": "Error Rate",
"type": "stat",
"gridPos": { "h": 4, "w": 6, "x": 6, "y": 0 },
"targets": [
{
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) * 100",
"legendFormat": "Error %"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent",
"thresholds": {
"steps": [
{ "color": "green", "value": null },
{ "color": "yellow", "value": 1 },
{ "color": "red", "value": 5 }
]
}
}
}
},
{
"title": "Response Time (p95)",
"type": "stat",
"gridPos": { "h": 4, "w": 6, "x": 12, "y": 0 },
"targets": [
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95"
}
],
"fieldConfig": {
"defaults": {
"unit": "s"
}
}
},
{
"title": "Active Connections",
"type": "stat",
"gridPos": { "h": 4, "w": 6, "x": 18, "y": 0 },
"targets": [
{
"expr": "sum(nodejs_active_handles_total)",
"legendFormat": "Connections"
}
]
},
{
"title": "Request Rate by Endpoint",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 },
"targets": [
{
"expr": "sum(rate(http_requests_total[5m])) by (route)",
"legendFormat": "{{route}}"
}
]
},
{
"title": "Response Time Distribution",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 },
"targets": [
{
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p50"
},
{
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p95"
},
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))",
"legendFormat": "p99"
}
],
"fieldConfig": {
"defaults": {
"unit": "s"
}
}
},
{
"title": "Memory Usage",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 },
"targets": [
{
"expr": "nodejs_heap_size_used_bytes",
"legendFormat": "Heap Used"
},
{
"expr": "nodejs_heap_size_total_bytes",
"legendFormat": "Heap Total"
},
{
"expr": "process_resident_memory_bytes",
"legendFormat": "RSS"
}
],
"fieldConfig": {
"defaults": {
"unit": "bytes"
}
}
},
{
"title": "CPU Usage",
"type": "timeseries",
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 12 },
"targets": [
{
"expr": "rate(process_cpu_seconds_total[5m]) * 100",
"legendFormat": "CPU %"
}
],
"fieldConfig": {
"defaults": {
"unit": "percent"
}
}
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": ["nodejs", "application"],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Application Dashboard",
"uid": "app-dashboard",
"version": 1
}
# Prometheus Configuration Template
# Usage: Copy to prometheus.yml
# Run: docker run -p 9090:9090 -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
global:
scrape_interval: 15s # How often to scrape targets
evaluation_interval: 15s # How often to evaluate rules
scrape_timeout: 10s # Timeout for scrape requests
# Attach labels to any time series or alerts
external_labels:
monitor: 'my-project'
environment: 'production'
# ===========================================
# Alerting Configuration
# ===========================================
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# ===========================================
# Rule Files
# ===========================================
rule_files:
- "alerts/*.yml"
# - "recording_rules.yml"
# ===========================================
# Scrape Configurations
# ===========================================
scrape_configs:
# Prometheus self-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter (host metrics)
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
# Application metrics
- job_name: 'app'
metrics_path: '/metrics'
static_configs:
- targets: ['app:3000']
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '(.+):\d+'
replacement: '${1}'
# Docker containers (cAdvisor)
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
# Nginx
- job_name: 'nginx'
static_configs:
- targets: ['nginx-exporter:9113']
# PostgreSQL
- job_name: 'postgres'
static_configs:
- targets: ['postgres-exporter:9187']
# Redis
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
# ===========================================
# Kubernetes Service Discovery (if applicable)
# ===========================================
# - job_name: 'kubernetes-pods'
# kubernetes_sd_configs:
# - role: pod
# relabel_configs:
# - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
# action: keep
# regex: true
# - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
# action: replace
# target_label: __metrics_path__
# regex: (.+)
Monitoring & Logging Templates
Production-ready monitoring stack configurations.
Files
| Template | Purpose |
|---|---|
prometheus.yml | Prometheus metrics collection config |
grafana-dashboard.json | Node.js application dashboard |
docker-compose.monitoring.yml | Complete monitoring stack |
Quick Start
# Copy all templates
cp templates/prometheus.yml ./prometheus.yml
cp templates/docker-compose.monitoring.yml ./docker-compose.monitoring.yml
mkdir -p grafana/dashboards
cp templates/grafana-dashboard.json ./grafana/dashboards/
# Start monitoring stack
docker-compose -f docker-compose.monitoring.yml up -d
# Access
# Prometheus: http://localhost:9090
# Grafana: http://localhost:3001 (admin/admin)Components
Prometheus
Metrics collection and alerting:
# Check config
promtool check config prometheus.yml
# Reload config (if enabled)
curl -X POST http://localhost:9090/-/reloadGrafana
Visualization and dashboards:
1. Login at http://localhost:3001 2. Go to Dashboards → Import 3. Upload grafana-dashboard.json
Node Exporter
Host-level metrics (CPU, memory, disk):
curl http://localhost:9100/metricsApplication Integration
Node.js (prom-client)
import { Counter, Histogram, collectDefaultMetrics } from 'prom-client';
// Enable default metrics
collectDefaultMetrics();
// Custom metrics
const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status'],
});
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
labelNames: ['method', 'route'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5],
});
// Expose /metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});Python (prometheus-client)
from prometheus_client import Counter, Histogram, generate_latest
http_requests = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status']
)
@app.route('/metrics')
def metrics():
return generate_latest()Key Metrics
| Metric | Type | Description |
|---|---|---|
http_requests_total | Counter | Total HTTP requests |
http_request_duration_seconds | Histogram | Request latency |
nodejs_heap_size_used_bytes | Gauge | Node.js heap usage |
process_cpu_seconds_total | Counter | CPU time |