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명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? 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.