Skip to main content ホーム クリエイター jeremylongshore claude-code-plugins-plus-skills webflow-observability
webflow-observability Set up observability for Webflow integrations — Prometheus metrics for API calls,
OpenTelemetry tracing, structured logging with pino, Grafana dashboards,
and alerting for rate limits, errors, and latency.
Trigger with phrases like "webflow monitoring", "webflow metrics",
"webflow observability", "monitor webflow", "webflow alerts", "webflow tracing".
インストールへ移動 Skills Marketplace コミュニティが作成したAIスキルを発見・探索
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill webflow-observabilityコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
Zipをダウンロード ダウンロード中... このリポジトリの他の Skills Implement user sign-up and sign-in flows with Clerk.
Use when building authentication UI, customizing sign-in experience,
or implementing OAuth social login.
Trigger with phrases like "clerk sign-in", "clerk sign-up",
"clerk login flow", "clerk OAuth", "clerk social login".
Implement session management and middleware with Clerk.
Use when managing user sessions, configuring route protection,
or implementing token refresh and custom JWT templates.
Trigger with phrases like "clerk session", "clerk middleware",
"clerk route protection", "clerk token", "clerk JWT".
Configure enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls.
Trigger with phrases like "clerk SSO", "clerk RBAC",
"clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
GitHub リポジトリを開く name webflow-observability description Set up observability for Webflow integrations — Prometheus metrics for API calls,
OpenTelemetry tracing, structured logging with pino, Grafana dashboards,
and alerting for rate limits, errors, and latency.
Trigger with phrases like "webflow monitoring", "webflow metrics",
"webflow observability", "monitor webflow", "webflow alerts", "webflow tracing".
allowed-tools Read, Write, Edit version 1.5.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","design","no-code","webflow"] compatibility Designed for Claude Code
Webflow Observability
Overview
Full observability stack for Webflow Data API v2 integrations: Prometheus metrics
for API call counting and latency, OpenTelemetry distributed tracing, structured
JSON logging, and alerting rules for error rate and rate limit exhaustion.
Prerequisites
prom-client for Prometheus metrics
@opentelemetry/api for tracing (optional)
pino for structured logging
Prometheus + Grafana (or compatible backend)
Instructions
Step 1: Prometheus Metrics
import { Registry , Counter , Histogram , Gauge } from "prom-client" ;
export const registry = new Registry ();
export const apiRequests = new Counter ({
name : "webflow_api_requests_total" ,
help : "Total Webflow API requests" ,
labelNames : ["operation" , "status_code" , "method" ] as const ,
registers : [registry],
});
export const apiDuration = new Histogram ({
name : "webflow_api_request_duration_seconds" ,
help : "Webflow API request duration in seconds" ,
: [ ] ,
: [ , , , , , , , ],
: [registry],
});
apiErrors = ({
: ,
: ,
: [ , , ] ,
: [registry],
});
rateLimitRemaining = ({
: ,
: ,
: [registry],
});
cmsItemCount = ({
: ,
: ,
: [ , ] ,
: [registry],
});
webhookEvents = ({
: ,
: ,
: [ , ] ,
: [registry],
});
labelNames
"operation"
as
const
buckets
0.05
0.1
0.25
0.5
1
2
5
10
registers
export
const
new
Counter
name
"webflow_api_errors_total"
help
"Webflow API errors by status code"
labelNames
"operation"
"status_code"
"error_type"
as
const
registers
export
const
new
Gauge
name
"webflow_rate_limit_remaining"
help
"Remaining API calls before rate limit"
registers
export
const
new
Gauge
name
"webflow_cms_items_total"
help
"Total CMS items by collection"
labelNames
"collection"
"site"
as
const
registers
export
const
new
Counter
name
"webflow_webhook_events_total"
help
"Received webhook events by trigger type"
labelNames
"trigger_type"
"status"
as
const
registers
Step 2: Instrumented Client Wrapper
import { WebflowClient } from "webflow-api" ;
import { apiRequests, apiDuration, apiErrors, rateLimitRemaining } from "./metrics.js" ;
export async function instrumentedCall<T>(
operation : string ,
method : string ,
fn : () => Promise <T>
): Promise <T> {
const timer = apiDuration.startTimer ({ operation });
try {
const result = await fn ();
apiRequests.inc ({ operation, status_code : "200" , method });
timer ();
return result;
} catch (error : any ) {
const statusCode = String (error.statusCode || error.status || "unknown" );
apiRequests.inc ({ operation, status_code : statusCode, method });
apiErrors.inc ({
operation,
status_code : statusCode,
error_type : statusCode === "429" ? "rate_limit" : statusCode >= "500" ? "server" : "client" ,
});
timer ();
throw error;
}
}
const { sites } = await instrumentedCall ("sites.list" , "GET" , () =>
webflow.sites .list ()
);
const { items } = await instrumentedCall ("items.listLive" , "GET" , () =>
webflow.collections .items .listItemsLive (collectionId)
);
const item = await instrumentedCall ("items.create" , "POST" , () =>
webflow.collections .items .createItem (collectionId, {
fieldData : { name : "Test" , slug : "test" },
})
);
Step 3: Metrics Endpoint
import express from "express" ;
import { registry } from "../observability/metrics.js" ;
const app = express ();
app.get ("/metrics" , async (req, res) => {
res.set ("Content-Type" , registry.contentType );
res.send (await registry.metrics ());
});
Step 4: OpenTelemetry Distributed Tracing
import { trace, SpanStatusCode , context } from "@opentelemetry/api" ;
const tracer = trace.getTracer ("webflow-integration" , "1.0.0" );
export async function tracedCall<T>(
operationName : string ,
attributes : Record <string , string >,
fn : () => Promise <T>
): Promise <T> {
return tracer.startActiveSpan (`webflow.${operationName} ` , async (span) => {
span.setAttributes ({
"webflow.operation" : operationName,
...attributes,
});
try {
const result = await fn ();
span.setStatus ({ code : SpanStatusCode .OK });
return result;
} catch (error : any ) {
span.setStatus ({
code : SpanStatusCode .ERROR ,
message : error.message ,
});
span.recordException (error);
span.setAttributes ({
"webflow.error.status_code" : String (error.statusCode || "unknown" ),
});
throw error;
} finally {
span.end ();
}
});
}
const { collections } = await tracedCall (
"collections.list" ,
{ "webflow.site_id" : siteId },
() => webflow.collections .list (siteId)
);
Step 5: Structured Logging
import pino from "pino" ;
export const logger = pino ({
name : "webflow-integration" ,
level : process.env .LOG_LEVEL || "info" ,
serializers : {
err : pino.stdSerializers .err ,
},
redact : {
paths : ["accessToken" , "apiToken" , "*.authorization" , "req.headers.authorization" ],
censor : "[REDACTED]" ,
},
});
export function logApiCall (
operation : string ,
durationMs : number ,
status : "success" | "error" ,
metadata ?: Record <string , any >
) {
const logFn = status === "error" ? logger.error .bind (logger) : logger.info .bind (logger);
logFn ({
service : "webflow" ,
operation,
durationMs,
status,
...metadata,
}, `webflow.${operation} ${status} (${durationMs} ms)` );
}
export function logWebhook (triggerType : string , status : "processed" | "failed" | "skipped" ) {
logger.info ({
service : "webflow" ,
event : "webhook" ,
triggerType,
status,
}, `webhook.${triggerType} ${status} ` );
}
Step 6: AlertManager Rules
groups:
- name: webflow
rules:
- alert: WebflowHighErrorRate
expr: |
(
rate(webflow_api_errors_total[5m]) /
rate(webflow_api_requests_total[5m])
) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Webflow API error rate > 5%"
description: "{{ $value | humanizePercentage }} errors in last 5m"
- alert: WebflowRateLimited
expr: |
rate(webflow_api_errors_total{status_code="429"}[5m]) > 0
for: 2m
labels:
severity: warning
annotations:
summary: "Webflow API rate limited"
- alert: WebflowHighLatency
expr: |
histogram_quantile(0.95,
rate(webflow_api_request_duration_seconds_bucket[5m])
) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "Webflow P95 latency > 3s"
- alert: WebflowDown
expr: |
sum(rate(webflow_api_requests_total{status_code=~"5.."}[5m])) /
sum(rate(webflow_api_requests_total[5m])) > 0.5
for: 2m
labels:
severity: critical
annotations:
summary: "Webflow API > 50% server errors"
- alert: WebflowRateLimitLow
expr: webflow_rate_limit_remaining < 10
for: 1m
labels:
severity: warning
annotations:
summary: "Webflow rate limit nearly exhausted"
Step 7: Grafana Dashboard Queries {
"panels" : [
{
"title" : "Request Rate by Operation" ,
"targets" : [ { "expr" : "sum by (operation) (rate(webflow_api_requests_total[5m]))" } ]
} ,
{
"title" : "Error Rate" ,
"targets" : [ { "expr" : "sum(rate(webflow_api_errors_total[5m])) / sum(rate(webflow_api_requests_total[5m]))" } ]
} ,
{
"title" : "Latency P50 / P95 / P99" ,
"targets" : [
{ "expr" : "histogram_quantile(0.5, rate(webflow_api_request_duration_seconds_bucket[5m]))" , "legendFormat" : "p50" } ,
{ "expr" : "histogram_quantile(0.95, rate(webflow_api_request_duration_seconds_bucket[5m]))" , "legendFormat" : "p95" } ,
{ "expr" : "histogram_quantile(0.99, rate(webflow_api_request_duration_seconds_bucket[5m]))" , "legendFormat" : "p99" }
]
} ,
{
"title" : "Rate Limit Remaining" ,
"targets" : [ { "expr" : "webflow_rate_limit_remaining" } ]
} ,
{
"title" : "Webhook Events by Type" ,
"targets" : [ { "expr" : "sum by (trigger_type) (rate(webflow_webhook_events_total[5m]))" } ]
}
]
}
Output
Prometheus metrics: request count, latency histogram, error rate, rate limit gauge
OpenTelemetry tracing for end-to-end request visibility
Structured JSON logging with PII redaction
AlertManager rules for error rate, latency, and rate limits
Grafana dashboard panels
Error Handling Issue Cause Solution Missing metrics Calls not instrumented Wrap with instrumentedCall() High cardinality Too many label values Limit operation to known set Trace gaps Missing context propagation Pass OTel context in async calls Alert storms Thresholds too sensitive Increase for duration
Resources
Next Steps For incident response, see webflow-incident-runbook.