一键导入
otel-onboarding-style
General OpenTelemetry onboarding style for Superlog managed agents: native APIs, signal quality, env vars, LLM metrics, and smoke checks.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
General OpenTelemetry onboarding style for Superlog managed agents: native APIs, signal quality, env vars, LLM metrics, and smoke checks.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Pull production observability context from the Superlog MCP to ground debugging, incident response, and 'how is this behaving in prod right now?' questions. Triggers whenever the user is investigating a bug, regression, or incident; asking about real traffic, error rates, latency, or throughput; validating a deploy; or wants the recent history of a service.
Expo / React Native OpenTelemetry style: bootstrap guards, init ordering, inline public ingest token, mobile-compatible exporters, and product action spans.
Generic OpenTelemetry style fallback for languages without a dedicated otel-*-style skill (Go, Java/Kotlin, Ruby, Rust, .NET/C#, PHP, Elixir, plain Node, etc.). Native SDK APIs, module-scope tracers/meters, bounded spans, error recording, OTLP logs, and resource attributes.
Next.js/Vercel OpenTelemetry style: instrumentation.ts, @vercel/otel bootstrap, native @opentelemetry/api call sites, inline public ingest token, and no raw NodeSDK replacement.
Python OpenTelemetry style: module-scope tracers/meters, decorators for bounded work, error spans, logs, and no wrappers.
Supabase Edge Function observability style: tiny provider-neutral OTel-shaped shim, OTLP export config, traces/logs/metrics, and LLM cost metrics.
| name | otel-onboarding-style |
| description | General OpenTelemetry onboarding style for Superlog managed agents: native APIs, signal quality, env vars, LLM metrics, and smoke checks. |
Use native OpenTelemetry APIs. Do not invent helper APIs.
In TypeScript/JavaScript, use the published @superlog/otel-helpers withSpan
helper for bounded business spans and add @superlog/otel-helpers to
package.json when it is not already present. This is required when the package
can be installed. withSpan is the intended replacement for expanding a whole
function into tracer.startActiveSpan(...) plus try / catch / finally. Do
not use helpers to wrap provider SDK calls that OpenInference/provider
instrumentation can observe directly.
Do:
const tracer = trace.getTracer("mugline.api");
const meter = metrics.getMeter("mugline.api");
const ordersSubmitted = meter.createCounter("orders.submitted");
await withSpan("order.submit", async (span) => {
span.setAttributes({
"tenant.id": tenantId,
"order.id": orderId,
outcome: "success",
});
ordersSubmitted.add(1, { "tenant.id": tenantId, outcome: "success" });
}, { tracer });
Do not:
await sendSuperlogSpan(...);
recordCounter(...);
withTelemetry(...);
telemetry.ts, observability.ts,
initTelemetry(), initObservability().checkout.process,
voice.session, llm.generate_copy.llm.generate_copy or llm.voice_response is usually more useful than
llm.anthropic.messages.create.Inline the endpoint and the Superlog public ingest token directly in the
bootstrap source. The token starts with sl_public_, is project-scoped and
write-only, and is intended to be safe in browser/mobile/server source code
like a PostHog project token or Sentry DSN.
SUPERLOG_ENDPOINT = "https://intake.superlog.sh"
SUPERLOG_PUBLIC_TOKEN = "sl_public_…"
Do not invent legacy names such as SUPERLOG_API_KEY or SUPERLOG_INTAKE_URL,
even as placeholder text in docs or comments. While pairing is in flight, use
the generated sl_public_ token in source; ingest may reject it until the
signup intent flow claims it for the user's project.
Pass the inline values to the SDK explicitly via the exporter constructor's
endpoint / headers options. Do not configure Superlog through implicit
OTEL env-var reads; missing env vars are the main onboarding failure mode this
skill avoids.
The token goes in the x-api-key header. Ingest accepts only
x-api-key: <token> or Authorization: Bearer <token> (literal Bearer
prefix required) — prefer x-api-key. Any other header name returns 401 on
every request even though the token is valid, which is the second most common
onboarding failure after missing env vars. Define a one-line helper so the
header name lives in one place:
function superlogHeaders(token: string): Record<string, string> {
return { "x-api-key": token };
}
def superlog_headers(token: str) -> dict[str, str]:
return {"x-api-key": token}
If the repo can call telemetry init from multiple paths, guard provider/exporter setup so repeated imports, tests, reloads, or framework callbacks do not install duplicate processors or log handlers. For a single-entrypoint app that starts cleanly, keep this simple.
Include standard resource attributes when values are available:
service.name, service.version, deployment.environment.name, and the VCS
attributes below.
Tag every signal with the runtime environment when possible. Prefer setting
deployment.environment.name once on the OTel resource so it propagates to
traces, logs, and metrics. Infer it from existing platform env vars such as
NODE_ENV, VERCEL_ENV, RAILWAY_ENVIRONMENT_NAME, FLY_APP_NAME/release
metadata, ENVIRONMENT, APP_ENV, or PYTHON_ENV; normalize obvious values
to low-cardinality names like local, development, preview, staging, and
production.
If a runtime/exporter path cannot attach resource attributes to all observables,
add the same low-cardinality environment value to custom span attributes, log
records, and metric data points manually. Do not create separate attribute names
such as env, environment, or app.environment unless the repo already has a
standard; use deployment.environment.name wherever the SDK allows it.
Set vcs.repository.url.full on the OTel resource for every instrumented
service. The value is the canonical https URL of the repo (e.g.
https://github.com/acme/api) — the same URL the user would paste in a
browser, not an SSH URL, not a local working-tree path. This is the important
one: it lets Superlog link telemetry back to the source of truth. It is fine
to hardcode this string alongside service.name in the SDK init; if a build
env already exposes the slug (e.g. VERCEL_GIT_REPO_OWNER +
VERCEL_GIT_REPO_SLUG, RAILWAY_GIT_REPO_OWNER + RAILWAY_GIT_REPO_NAME),
prefer reading from env so a fork or rename doesn't drift.
Also set vcs.ref.head.revision (the commit SHA) on a best-effort basis. Read
it from whatever env var the runtime/build platform already injects:
VERCEL_GIT_COMMIT_SHA, RAILWAY_GIT_COMMIT_SHA, GITHUB_SHA,
SOURCE_COMMIT, GIT_COMMIT, HEROKU_SLUG_COMMIT, etc. Do not shell out to
git from the running process — many production images do not have git or a
working tree. If no env source is available, omit the attribute; skipping the
SHA is fine, skipping the URL is not.
Use vcs.repository.url.full and vcs.ref.head.revision exactly as named —
these are the OTel semantic-convention keys. Do not invent parallel attributes
like git.repo or app.repo_url.
The baseline goal for every instrumented service is RED per software component: operation count, error count, and operation duration. Two things must hold:
service.name is set on every signal, so RED can be rolled up per service.If a service ships without something that produces those three numbers per operation for its main work (inbound HTTP requests, queue handlers, scheduled jobs, background loops, etc.), the onboarding is incomplete.
withSpan (or
language equivalent) around the request/handler entry point.For each service, install the standard OpenTelemetry HTTP/server
instrumentation for its language and framework (e.g.
@opentelemetry/instrumentation-http plus the framework package in Node,
opentelemetry-instrumentation-fastapi / -django / -flask in Python, etc.)
and add it to package.json / pyproject.toml if missing. This is what
populates http.server.request.duration, http.request.method, and
http.response.status_code and makes RED work without per-route code.
If the service's framework or runtime has no auto-instrumentation available
(custom server, exotic runtime, RPC over a non-HTTP transport, background
worker loop), wire spans manually at the entry point so every unit of work
produces one span with http.* or operation-appropriate attributes and a
status. Do not skip this step on the grounds that the service "only has
business spans" — those don't give you per-service RED.
Every span that represents a unit of work must signal failure consistently so error counts are derivable:
span.recordException(err) and
span.setStatus({ code: SpanStatusCode.ERROR, message }) (or the language
equivalent). Auto-instrumentation usually does this for HTTP 5xx; you still
need it for handled errors and for manually wired spans.outcome attribute of "success" or "error" on business spans
and matching metric data points, so a single low-cardinality dimension
splits RED into success vs. error without joining on status code.error.type to a short, low-cardinality
string (e.g. "timeout", "validation", "upstream_5xx"). Do not use the
exception message — it's unbounded.withSpan callback; rethrow
after recording so the surrounding span and caller see the failure.If the app uses LLMs, first look for provider instrumentation that already
captures model/provider/token/error spans. In JavaScript/TypeScript, prefer
OpenInference packages such as
@arizeai/openinference-instrumentation-anthropic for supported SDKs. Keep the
real provider call native and readable.
const response = await client.messages.create({
model,
max_tokens: 100,
messages,
});
Every provider/call site still needs enough telemetry to answer usage questions. Let provider instrumentation own model/provider/token spans where it supports them. Do not duplicate those attributes at every application call site, and do not put provider pricing tables or cost math in product handlers. Superlog computes estimated LLM cost centrally in the UI/query layer from captured provider/model/token data.
llmInputTokens.add(inputTokens, {
"tenant.id": tenantId,
"gen_ai.provider.name": "anthropic",
"gen_ai.request.model": model,
"app.gen_ai.use_case": "voice.initial_greeting",
"app.gen_ai.call_site": "_callMugCopyLlm",
outcome: "success",
});
Use counters for additive totals only when provider instrumentation cannot capture token usage:
llm.tokens.inputllm.tokens.outputToken counters use unit="tokens" or the SDK equivalent. If
OpenInference/provider instrumentation already captures token usage, do not
duplicate token counters just to mirror it. Do not add llm.cost_usd or
equivalent app-side cost metrics for normal LLM calls; cost belongs in Superlog's
central pricing layer.
Use histograms for latency/duration distributions.
Prefer current gen_ai.* semantic-convention-style attribute names for LLM
provider/model/token attributes, plus app.gen_ai.* for bounded application
dimensions such as use case and call site. Avoid inventing parallel llm.*
attributes unless the repo already standardizes on them.
If the app has OpenAI, Anthropic, and Google callers, instrument all three.
Add a durable smoke path when the repo has a natural place for it: README, TESTING guide, script, npm command, pytest, or checked-in command note.
The smoke should explicitly prove startup/import with OTel env vars present so provider setup, exporter construction, log bridging, and framework instrumentation initialize without errors. Then, where practical, exercise an actual instrumented span/log/metric or OTLP export attempt. A generic health route only proves the server responds; prefer an operation that crosses the instrumentation you added.