Loading...
Loading...
Compare original and translation side by side
tws_tracewaytws_traceway| Invocation carries | Path |
|---|---|
| A project ingest token + url | Fast Path for a single component, otherwise Steps 1-2, then integrate against the existing project |
A setup token ( | Steps 1-2, then Step 3 from "Write the plan file" (3b) |
| No token | Steps 1-2, then Step 3 from the top |
<instance>/api/otel/*| 调用携带的信息 | 路径 |
|---|---|
| 项目 ingest token + url | 单一组件使用快速路径;否则执行步骤 1-2,然后针对现有项目进行集成 |
setup token( | 执行步骤 1-2,然后从步骤 3 的“编写计划文件”(3b)开始 |
| 没有 token | 执行步骤 1-2,然后从步骤 3 开始 |
<instance>/api/otel/*tws_<name>tws_<name>package.jsongo.modcomposer.jsonrequirements.txtpyproject.tomlpubspec.yamlbuild.gradle.ktsPackage.swift*.xcodeproj*.xcworkspacePodfileDockerfileoutput: 'export'next.config.*app/api/**/route.*pages/api/*'use server'rewritesNEXT_PUBLIC_API_URLout/'use server'next startCONSUMERopenai@anthropic-ai/sdkanthropiclangchain@langchain/*ailitellmgoogle-generativeaicohereopenrouterlanggraphcrewaiautogenag2pydantic-ai@openai/agents@mastra/*llamaindexllama-indexsemantic-kernel/chat/completionsmessagesconversationsai-agent.md| Component | Repository evidence | Production role | Current tracking | Proposed Traceway project | Integration | Credentials needed |
|---|---|---|---|---|---|---|
| Go module + Gin routes | Backend API | OTel / none / vendor | | OTel | Runtime token |
| Svelte + Vite | Browser app | Existing SDK / none | | Traceway Svelte SDK | Runtime + upload token |
package.jsongo.modcomposer.jsonrequirements.txtpyproject.tomlpubspec.yamlbuild.gradle.ktsPackage.swift*.xcodeproj*.xcworkspacePodfileDockerfilenext.config.*output: 'export'app/api/**/route.*pages/api/*'use server'rewritesNEXT_PUBLIC_API_URLout/'use server'next startCONSUMERopenai@anthropic-ai/sdkanthropiclangchain@langchain/*ailitellmgoogle-generativeaicohereopenrouterlanggraphcrewaiautogenag2pydantic-ai@openai/agents@mastra/*llamaindexllama-indexsemantic-kernel/chat/completionsmessagesconversationsai-agent.md| 组件 | 仓库证据 | 生产角色 | 当前跟踪状态 | 建议的 Traceway 项目 | 集成方式 | 所需凭据 |
|---|---|---|---|---|---|---|
| Go module + Gin routes | Backend API | OTel / none / vendor | | OTel | 运行时 token |
| Svelte + Vite | Browser app | Existing SDK / none | | Traceway Svelte SDK | 运行时 + upload token |
| Application boundary | Traceway project | What belongs in it |
|---|---|---|
| Backend system | One project with framework OpenTelemetry | API endpoints, child spans, issues, background tasks, AI traces, logs, application/runtime metrics, and host metrics. APIs, workers, schedulers, and the host agent share the project token; distinguish them with stable |
| Browser frontend | One separate project per deployed browser application, using React, Svelte, Vue.js, or jQuery | Browser errors, web vitals, session replay, distributed-trace linkage, source maps, and browser release metadata. |
| Mobile app | One separate project per independently released application, using Flutter, React Native, Android, or iOS | Mobile errors/crashes, replay where supported, and build symbols or mappings. A single Flutter product targeting Android and iOS normally uses one project; separate native apps use separate projects. |
| Full-stack JS app | Two projects when its server runs in production | Server-side work goes to the OpenTelemetry backend project; browser-side work goes to the browser project. |
opentelemetryreactsveltevuejsjqueryflutterreact-nativeandroidiosenvFileenvVarVITE_PUBLIC_NEXT_PUBLIC_envFormattokenconnectionString<token>@<instance>/api/reportdeployment<token>deploymentenvFile| 应用边界 | Traceway 项目 | 应包含的内容 |
|---|---|---|
| Backend system | 一个使用 OpenTelemetry 框架的项目 | API endpoints、child span、issues、background task、AI trace、logs、application/runtime metrics 和 host metrics。API、worker、scheduler 和 host agent 共享项目 token;使用稳定的 |
| Browser frontend | 每个已部署 browser application 使用一个独立项目,框架为 React、Svelte、Vue.js 或 jQuery | Browser errors、web vitals、session replay、distributed-trace linkage、source map 和 browser release metadata。 |
| Mobile app | 每个独立发布的应用使用一个独立项目,框架为 Flutter、React Native、Android 或 iOS | Mobile errors/crashes、受支持的 replay,以及 build symbol 或 mapping。单个同时面向 Android 和 iOS 的 Flutter 产品通常使用一个项目;独立原生应用使用独立项目。 |
| Full-stack JS app | 当 server 在生产环境运行时使用两个项目 | Server 端工作进入 OpenTelemetry backend 项目;browser 端工作进入 browser 项目。 |
opentelemetryreactsveltevuejsjqueryflutterreact-nativeandroidiosenvFileenvVarVITE_PUBLIC_NEXT_PUBLIC_envFormattoken<token>@<instance>/api/reportenvFormatconnectionStringdeployment<token>deploymentenvFilecurljqcurljq<instance>/setuptws_<instance>/registertws_<instance>/setuptws_<instance>/registertws_traceway-setup-plan.json{
"projects": [
{"name": "Product Backend", "framework": "opentelemetry",
"envFile": "backend/.env", "envVar": "TRACEWAY_BACKEND_TOKEN"},
{"name": "Product Web", "framework": "svelte",
"envFile": "frontend/.env", "envVar": "PUBLIC_TRACEWAY_WEB_CONNECTION_STRING",
"envFormat": "connectionString",
"deployment": {"platform": "Vercel",
"instructions": "vercel env add PUBLIC_TRACEWAY_WEB_CONNECTION_STRING production\n# paste: <token>"}}
]
}frameworkenvFileenvVardeployment.instructions<token>traceway-setup-plan.json{
"projects": [
{"name": "Product Backend", "framework": "opentelemetry",
"envFile": "backend/.env", "envVar": "TRACEWAY_BACKEND_TOKEN"},
{"name": "Product Web", "framework": "svelte",
"envFile": "frontend/.env", "envVar": "PUBLIC_TRACEWAY_WEB_CONNECTION_STRING",
"envFormat": "connectionString",
"deployment": {"platform": "Vercel",
"instructions": "vercel env add PUBLIC_TRACEWAY_WEB_CONNECTION_STRING production\n# paste: <token>"}}
]
}frameworkenvFileenvVardeployment.instructions<token>curljqtracewaysetup applyprintf '%s' "$TRACEWAY_SETUP_TOKEN" | traceway setup apply --url "$TRACEWAY_URL" --plan traceway-setup-plan.json --token-stdinjqexport TRACEWAY_URL="<instance>" # no trailing slash
export TRACEWAY_SETUP_TOKEN="tws_..."curl -s -w '\nHTTP %{http_code}\n' -H "Authorization: Bearer $TRACEWAY_SETUP_TOKEN" \
"$TRACEWAY_URL/api/setup/session"curl -s -w '\nHTTP %{http_code}\n' -X PUT \
-H "Authorization: Bearer $TRACEWAY_SETUP_TOKEN" -H 'Content-Type: application/json' \
--data-binary @traceway-setup-plan.json "$TRACEWAY_URL/api/setup/plan"200 {"status":"pending"}422401<instance>/setuptraceway-setup-wait.shsh traceway-setup-wait.shGET /api/setup/plan#!/bin/shcurljqtracewaysetup applyprintf '%s' "$TRACEWAY_SETUP_TOKEN" | traceway setup apply --url "$TRACEWAY_URL" --plan traceway-setup-plan.json --token-stdinjqexport TRACEWAY_URL="<instance>" # no trailing slash
export TRACEWAY_SETUP_TOKEN="tws_..."curl -s -w '\\nHTTP %{http_code}\\n' -H "Authorization: Bearer $TRACEWAY_SETUP_TOKEN" \\
"$TRACEWAY_URL/api/setup/session"curl -s -w '\\nHTTP %{http_code}\\n' -X PUT \\
-H "Authorization: Bearer $TRACEWAY_SETUP_TOKEN" -H 'Content-Type: application/json' \\
--data-binary @traceway-setup-plan.json "$TRACEWAY_URL/api/setup/plan"200 {"status":"pending"}422401<instance>/setuptraceway-setup-wait.shsh traceway-setup-wait.shGET /api/setup/plan#!/bin/sh
Env files come out mode 0600 via mktemp. Failure handling:
- **Rejected (exit 2)**: the script prints the user's rejection reason. Revise the map with the user, update the plan file, submit again (a new PUT replaces the pending draft), and rerun the wait script. Already-approved projects are matched by name, never duplicated.
- **Expired or invalid token (401 / exit 1)**: ask the user for a fresh token from `<instance>/setup`, re-export it, resubmit, rerun.
- **Timeout (exit 3)**: resubmit and rerun when the user is ready; it is safe.
Delete the plan file and `traceway-setup-wait.sh` after a successful apply; they hold no secrets but they are setup litter.
通过 `mktemp` 生成的 env 文件权限为 0600。失败处理:
- **Rejected(exit 2)**:脚本会打印用户的拒绝原因。与用户一起修改映射,更新计划文件,重新提交(新的 PUT 会替换 pending draft),然后重新运行等待脚本。已批准的项目按名称匹配,绝不会重复创建。
- **Token 过期或无效(401 / exit 1)**:要求用户从 `<instance>/setup` 获取新 token,重新导出,重新提交并重新运行。
- **超时(exit 3)**:用户准备好后重新提交并重新运行即可,操作是安全的。
成功 apply 后,删除计划文件和 `traceway-setup-wait.sh`;它们不包含 secret,但属于临时设置文件。deploymentdeploymentcurljqdashboard-project-setup.mdtraceway-sourcemapsTRACEWAY_SOURCEMAP_TOKENdart run traceway:upload_symbolsTRACEWAY_UPLOAD_TOKENTRACEWAY_URLtraceway-sourcemaps --url "$TRACEWAY_URL" --token "$TRACEWAY_WEB_UPLOAD_TOKEN" --directory ./distTRACEWAY_WEB_UPLOAD_TOKEN--tokencurljqdashboard-project-setup.mdtraceway-sourcemapsTRACEWAY_SOURCEMAP_TOKENdart run traceway:upload_symbolsTRACEWAY_UPLOAD_TOKENTRACEWAY_URLtraceway-sourcemaps --url "$TRACEWAY_URL" --token "$TRACEWAY_WEB_UPLOAD_TOKEN" --directory ./distTRACEWAY_WEB_UPLOAD_TOKEN--token| Project type | Path |
|---|---|
| Backend (any language) | OpenTelemetry, exporting OTLP/HTTP to |
| Frontend (browser SPA, or a JS meta-framework running frontend-only in production) | Traceway |
| Full-stack JS (Next.js, SvelteKit, Remix, actually serving its API/SSR in production per "Analyze the Architecture") | BOTH sides, each under its own Traceway project: server side via OpenTelemetry AND browser side via the frontend SDK. |
| Mobile (Flutter, React Native, Android, native Swift iOS) | The Traceway platform SDK. Never OTel. Sole exception: a non-Swift iOS/Apple app has no native SDK, so it uses an OTel library (e.g. Honeycomb) exporting to Traceway like a backend (see "Frontend and Mobile"). |
| 项目类型 | 路径 |
|---|---|
| Backend(任意语言) | OpenTelemetry,通过 OTLP/HTTP 导出到 |
| Frontend(browser SPA,或生产环境仅运行 frontend 的 JS meta-framework) | Traceway |
| Full-stack JS(Next.js、SvelteKit、Remix,且根据“分析架构”确认其 framework server 确实在生产环境提供 API/SSR) | 同时集成两端,并分别使用独立的 Traceway 项目:server 端通过 OpenTelemetry,browser 端通过 frontend SDK。 |
| Mobile(Flutter、React Native、Android、原生 Swift iOS) | Traceway platform SDK,绝不使用 OTel。唯一例外是非 Swift iOS/Apple app 没有原生 SDK,因此使用 OTel library(如 Honeycomb),像 backend 一样导出到 Traceway(参见“前端与移动端”)。 |
service.nameservice.versionservice.nameservice.versionhttp.route/api/users/:id/app_user_showurl.pathSpanKind.CONSUMERINTERNALhttp.route/api/users/:id/app_user_showurl.pathSpanKind.CONSUMERINTERNAL| # | Span | Condition | Becomes |
|---|---|---|---|
| 1 | Any span | | Issue. Never promoted, even with HTTP or |
| 2 | Root span, or a span whose parent is not in the same export batch | | Endpoint |
| 3 | Any span | | Task |
| 4 | Root span | | Task (CLI command) |
| 5 | Any span | Has any | AI Trace |
| 6 | Non-root span | Has a parent span id | Span (child) |
| 7 | Root span | Nothing above matched | Dropped (exceptions recorded on it still become Issues, unlinked) |
CONSUMERgen_ai.*SERVERgen_ai.*exceptionexception.*http.route//*UNMATCHEDdata-model.md| # | Span | 条件 | 结果 |
|---|---|---|---|
| 1 | 任意 span | | Issue。 即使同时存在 HTTP 或 |
| 2 | Root span,或 parent 不在同一 export batch 中的 span | | Endpoint |
| 3 | 任意 span | | Task |
| 4 | Root span | 带 | Task(CLI command) |
| 5 | 任意 span | 包含任意 | AI Trace |
| 6 | Non-root span | 包含 parent span id | Span(child) |
| 7 | Root span | 未匹配上述任何规则 | 丢弃(记录在其上的 exception 仍会生成无关联的 Issue) |
gen_ai.*CONSUMERgen_ai.*SERVERexceptionexception.*http.route//*UNMATCHEDdata-model.mdOTEL_SERVICE_NAME=my-service
OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3 # there is no OTEL_SERVICE_VERSION variable
OTEL_EXPORTER_OTLP_ENDPOINT=https://<instance>/api/otel
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf # required, several SDKs default to gRPC
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <project-token>"
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlpOTEL_EXPORTER_OTLP_PROTOCOLotlphttp/jsonOTEL_SERVICE_VERSIONservice.versionOTEL_RESOURCE_ATTRIBUTES/v1/traces/v1/metrics/v1/logsOTEL_EXPORTER_OTLP_ENDPOINT/v1/traces/v1/tracesOTEL_EXPORTER_OTLP_TRACES_ENDPOINThttps://<instance>/api/otel/v1/traces/v1/metrics/v1/logsAuthorization: Bearer <project-token>Authorization=Bearer ${TRACEWAY_BACKEND_TOKEN}TRACEWAY_URLContent-Encoding: gzip413{"error":"request body exceeds the 10MB limit"}401404OTEL_SERVICE_NAME=my-service
OTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3 # there is no OTEL_SERVICE_VERSION variable
OTEL_EXPORTER_OTLP_ENDPOINT=https://<instance>/api/otel
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf # required, several SDKs default to gRPC
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <project-token>"
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlpOTEL_EXPORTER_OTLP_PROTOCOLotlphttp/jsonOTEL_SERVICE_VERSIONservice.versionOTEL_RESOURCE_ATTRIBUTES/v1/traces/v1/metrics/v1/logsOTEL_EXPORTER_OTLP_ENDPOINT/v1/traces/v1/tracesOTEL_EXPORTER_OTLP_TRACES_ENDPOINThttps://<instance>/api/otel/v1/traces/v1/metrics/v1/logsAuthorization: Bearer <project-token>Authorization=Bearer ${TRACEWAY_BACKEND_TOKEN}TRACEWAY_URLContent-Encoding: gzip413{"error":"request body exceeds the 10MB limit"}401404npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node \
@opentelemetry/instrumentation @opentelemetry/api @opentelemetry/sdk-metrics \
@opentelemetry/exporter-trace-otlp-http @opentelemetry/exporter-metrics-otlp-http@opentelemetry/apinode_modulesinstrumentation.mjsnode --import ./instrumentation.mjs server.js.mjs.jsimportimport { register } from "node:module";
register("@opentelemetry/instrumentation/hook.mjs", import.meta.url);
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
const url = process.env.TRACEWAY_URL;
const headers = { Authorization: `Bearer ${process.env.TRACEWAY_BACKEND_TOKEN}` };
const sdk = new NodeSDK({
serviceName: process.env.OTEL_SERVICE_NAME ?? "my-service",
traceExporter: new OTLPTraceExporter({ url: `${url}/api/otel/v1/traces`, headers }),
metricReaders: [
new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${url}/api/otel/v1/metrics`, headers }),
exportIntervalMillis: 30_000,
}),
],
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();register"type": "module"import express from "express"http.routerequire("express")serviceNameunknown_service:nodeOTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3metricReadersmetricReaderhttp.routepgmysql2mongodbioredistracer.startActiveSpan()npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node \\
@opentelemetry/instrumentation @opentelemetry/api @opentelemetry/sdk-metrics \\
@opentelemetry/exporter-trace-otlp-http @opentelemetry/exporter-metrics-otlp-http@opentelemetry/apinode_modulesinstrumentation.mjsnode --import ./instrumentation.mjs server.js.mjsimport.jsimport { register } from "node:module";
register("@opentelemetry/instrumentation/hook.mjs", import.meta.url);
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
const url = process.env.TRACEWAY_URL;
const headers = { Authorization: `Bearer ${process.env.TRACEWAY_BACKEND_TOKEN}` };
const sdk = new NodeSDK({
serviceName: process.env.OTEL_SERVICE_NAME ?? "my-service",
traceExporter: new OTLPTraceExporter({ url: `${url}/api/otel/v1/traces`, headers }),
metricReaders: [
new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${url}/api/otel/v1/metrics`, headers }),
exportIntervalMillis: 30_000,
}),
],
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();register"type": "module"import express from "express"http.routerequire("express"serviceNameunknown_service:nodeOTEL_RESOURCE_ATTRIBUTES=service.version=1.2.3metricReadersmetricReaderhttp.routepgmysql2mongodbioredistracer.startActiveSpan()instrumentation.mjsnode --importNODE_OPTIONShttp.route/api/users/1/api/users/2instrumentation.tsinstrumentation.node.tsimport()export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./instrumentation.node");
}
}import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";
(Error as unknown as { prepareStackTrace?: unknown }).prepareStackTrace = undefined;
const instance = process.env.TRACEWAY_URL!.replace(/\/+$/, "");
const base = `${instance}/api/otel`;
const headers = { Authorization: `Bearer ${process.env.TRACEWAY_BACKEND_TOKEN}` };
new NodeSDK({
resource: resourceFromAttributes({
"service.name": process.env.OTEL_SERVICE_NAME ?? "nextjs-server",
"service.version": process.env.APP_VERSION ?? "development",
}),
traceExporter: new OTLPTraceExporter({ url: `${base}/v1/traces`, headers }),
metricReaders: [
new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${base}/v1/metrics`, headers }),
exportIntervalMillis: 30_000,
}),
],
logRecordProcessors: [
new BatchLogRecordProcessor({
exporter: new OTLPLogExporter({ url: `${base}/v1/logs`, headers }),
}),
],
instrumentations: [getNodeAutoInstrumentations()],
}).start();@opentelemetry/exporter-logs-otlp-http@opentelemetry/sdk-logs@opentelemetry/api-logs@opentelemetry/resourcesexperimental.instrumentationHook: trueError.prepareStackTraceinstrumentation.node.tsNODE_OPTIONS=--enable-source-maps.next/server/**/*.js.mapexperimental: { serverSourceMaps: true }next.config@opentelemetry/auto-instrumentations-nodeserverExternalPackagesinstrumentation.mjsnode --importNODE_OPTIONShttp.route/api/users/1/api/users/2instrumentation.tsinstrumentation.node.tsimport()export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
await import("./instrumentation.node");
}
}import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";
(Error as unknown as { prepareStackTrace?: unknown }).prepareStackTrace = undefined;
const instance = process.env.TRACEWAY_URL!.replace(/\\/+$/, "");
const base = `${instance}/api/otel`;
const headers = { Authorization: `Bearer ${process.env.TRACEWAY_BACKEND_TOKEN}` };
new NodeSDK({
resource: resourceFromAttributes({
"service.name": process.env.OTEL_SERVICE_NAME ?? "nextjs-server",
"service.version": process.env.APP_VERSION ?? "development",
}),
traceExporter: new OTLPTraceExporter({ url: `${base}/v1/traces`, headers }),
metricReaders: [
new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${base}/v1/metrics`, headers }),
exportIntervalMillis: 30_000,
}),
],
logRecordProcessors: [
new BatchLogRecordProcessor({
exporter: new OTLPLogExporter({ url: `${base}/v1/logs`, headers }),
}),
],
instrumentations: [getNodeAutoInstrumentations()],
}).start();@opentelemetry/exporter-logs-otlp-http@opentelemetry/sdk-logs@opentelemetry/api-logs@opentelemetry/resourcesexperimental.instrumentationHook: trueinstrumentation.node.tsError.prepareStackTraceNODE_OPTIONS=--enable-source-maps.next/server/**/*.js.mapnext.configexperimental: { serverSourceMaps: true }@opentelemetry/auto-instrumentations-nodeserverExternalPackagesgo.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgingithub.com/riandyrn/otelchiotelchi.WithChiRoutes(r)github.com/gofiber/contrib/otelfiber/v2http.routeotlptracehttp.WithEndpointURL(...)WithHeaderstraceparentotel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, propagation.Baggage{},
))net/httpotelhttpr.PatternServeMuxotelhttp.NewHandler(mux, "server")http.routemux.Handle("GET /api/users/{id}", otelhttp.NewHandler(usersHandler, "users"))// imports: strings, go.opentelemetry.io/otel/trace,
// semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
func withRoute(mux *http.ServeMux) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, pattern := mux.Handler(r); pattern != "" {
if i := strings.IndexByte(pattern, '/'); i >= 0 {
route := pattern[i:] // drop the "GET " prefix, http.route must start with "/"
trace.SpanFromContext(r.Context()).SetAttributes(semconv.HTTPRoute(route))
}
}
mux.ServeHTTP(w, r)
})
}
handler := otelhttp.NewHandler(withRoute(mux), "server")pip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a installopentelemetry-instrumentopentelemetry-instrument uvicorn app:appopentelemetry-instrument gunicorn wsgi:appopentelemetry-instrument python worker.pyhttp.routeGET /users/{user_id}GET /orders/<order_id>500OTEL_LOGS_EXPORTER=otlpWARNINGlogger.info(...)OTEL_PYTHON_LOG_CORRELATION=true # restores console output, stamps otelTraceID/otelSpanID
OTEL_PYTHON_LOG_LEVEL=info # only read alongside the line aboveOTEL_PYTHON_LOG_LEVELOTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=trueOTEL_METRIC_EXPORT_INTERVALOTEL_METRIC_EXPORT_INTERVAL=10000DJANGO_SETTINGS_MODULEopentelemetry-instrumentopentelemetry-instrumentation-djangohttp.routeapi/users/<int:user_id>//composer require keepsuit/laravel-opentelemetry open-telemetry/exporter-otlp php-http/guzzle7-adaptercomposer require traceway/opentelemetry-symfony open-telemetry/exporter-otlp php-http/guzzle7-adapterhttp.routedata-model.mdOTEL_PHP_AUTOLOAD_ENABLEDopen_telemetry.sdk.autoload_enabled: trueconfig/packages/open_telemetry.yamlOTEL_PHP_AUTOLOAD_ENABLED.envOTEL_EXPORTER_OTLP_PROTOCOL=http/jsonhttp/protobufext-protobufmessenger:consumetraces.console.excluded_commands['messenger:consume', 'messenger:consume-messages']config/packages/open_telemetry.yamlbin/console debug:config open_telemetry traces.consolego.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgingithub.com/riandyrn/otelchiotelchi.WithChiRoutes(r)github.com/gofiber/contrib/otelfiber/v2http.routeotlptracehttp.WithEndpointURL(...)WithHeaderstraceparentotel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, propagation.Baggage{},
))net/httpotelhttpr.PatternServeMuxotelhttp.NewHandler(mux, "server")http.routemux.Handle("GET /api/users/{id}", otelhttp.NewHandler(usersHandler, "users"))// imports: strings, go.opentelemetry.io/otel/trace,
// semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
func withRoute(mux *http.ServeMux) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if _, pattern := mux.Handler(r); pattern != "" {
if i := strings.IndexByte(pattern, '/'); i >= 0 {
route := pattern[i:] // drop the "GET " prefix, http.route must start with "/"
trace.SpanFromContext(r.Context()).SetAttributes(semconv.HTTPRoute(route))
}
}
mux.ServeHTTP(w, r)
})
}
handler := otelhttp.NewHandler(withRoute(mux), "server")pip install opentelemetry-distro opentelemetry-exporter-otlpopentelemetry-bootstrap -a installopentelemetry-instrumentopentelemetry-instrument uvicorn app:appopentelemetry-instrument gunicorn wsgi:appopentelemetry-instrument python worker.pyhttp.routeGET /users/{user_id}GET /orders/<order_id>500OTEL_LOGS_EXPORTER=otlpWARNINGlogger.info(...)OTEL_PYTHON_LOG_CORRELATION=true # restores console output, stamps otelTraceID/otelSpanID
OTEL_PYTHON_LOG_LEVEL=info # only read alongside the line aboveOTEL_PYTHON_LOG_LEVELOTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=trueOTEL_METRIC_EXPORT_INTERVALOTEL_METRIC_EXPORT_INTERVAL=10000DJANGO_SETTINGS_MODULEopentelemetry-instrumentopentelemetry-instrumentation-djangohttp.routeapi/users/<int:user_id>//composer require keepsuit/laravel-opentelemetry open-telemetry/exporter-otlp php-http/guzzle7-adaptercomposer require traceway/opentelemetry-symfony open-telemetry/exporter-otlp php-http/guzzle7-adapterhttp.routedata-model.mdOTEL_PHP_AUTOLOAD_ENABLEDconfig/packages/open_telemetry.yamlopen_telemetry.sdk.autoload_enabled: trueOTEL_PHP_AUTOLOAD_ENABLED.envOTEL_EXPORTER_OTLP_PROTOCOL=http/jsonhttp/protobufext-protobufmessenger:consumetraces.console.excluded_commands['messenger:consume', 'messenger:consume-messages']config/packages/open_telemetry.yamlbin/console debug:config open_telemetry traces.consoleGET /api/users/:idhttp.route/import { trace } from "@opentelemetry/api";
trace.getActiveSpan()?.setAttribute("http.route", matchedRoutePattern);GET /api/users/:idhttp.route/import { trace } from "@opentelemetry/api";
trace.getActiveSpan()?.setAttribute("http.route", matchedRoutePattern);import { trace, SpanStatusCode } from "@opentelemetry/api";
const span = trace.getActiveSpan();
span?.recordException(error);
span?.setStatus({ code: SpanStatusCode.ERROR, message: error.message });span.RecordError(err, trace.WithStackTrace(true))exception.stacktrace500ERROR2002xx200500ERROR422401404otelginc.Next()otelgin.Middleware// imports: fmt, net/http, go.opentelemetry.io/otel/codes, go.opentelemetry.io/otel/trace
func otelRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
span := trace.SpanFromContext(c.Request.Context())
span.RecordError(err, trace.WithStackTrace(true))
span.SetStatus(codes.Error, err.Error())
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}
router.Use(otelgin.Middleware(serviceName))
router.Use(otelRecovery())import { trace, SpanStatusCode } from "@opentelemetry/api";
const span = trace.getActiveSpan();
span?.recordException(error);
span?.setStatus({ code: SpanStatusCode.ERROR, message: error.message });span.RecordError(err, trace.WithStackTrace(true))exception.stacktrace500ERROR2002xx200500ERROR422401404otelginc.Next()c.Next()otelgin.Middleware// imports: fmt, net/http, go.opentelemetry.io/otel/codes, go.opentelemetry.io/otel/trace
func otelRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
span := trace.SpanFromContext(c.Request.Context())
span.RecordError(err, trace.WithStackTrace(true))
span.SetStatus(codes.Error, err.Error())
c.AbortWithStatus(http.StatusInternalServerError)
}
}()
c.Next()
}
}
router.Use(otelgin.Middleware(serviceName))
router.Use(otelRecovery())OTEL_LOGS_EXPORTER=otlp@opentelemetry/winston-transporttrace_idspan_idopentelemetry-instrumentsdklog.LoggerProviderotlploghttp.WithEndpointURL(...)global.SetLoggerProvider(lp)defer lp.Shutdown(ctx)c.Request.Context()r.Context()severity_textseverity_numberservice.nameOTEL_LOGS_EXPORTER=otlp@opentelemetry/winston-transporttrace_idspan_idopentelemetry-instrumentotlploghttp.WithEndpointURL(...)sdklog.LoggerProviderglobal.SetLoggerProvider(lp)defer lp.Shutdown(ctx)c.Request.Context()r.Context()severity_textseverity_numberservice.nameopentelemetry-gogo get go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttpopentelemetry-gogo get go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttpCONSUMERCONSUMERcleanup-expired-sessionsprocess-email-queueCONSUMERSpanKind.INTERNALINTERNALconsole.commandimport { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
const tracer = trace.getTracer("my-app");
async function runScheduledJob() {
await tracer.startActiveSpan(
"cleanup-expired-sessions",
{ kind: SpanKind.CONSUMER },
async (span) => {
try {
await doWork();
span.setStatus({ code: SpanStatusCode.OK });
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
throw error;
} finally {
span.end();
}
}
);
}tracer.Start(ctx, "cleanup-expired-sessions", trace.WithSpanKind(trace.SpanKindConsumer))CONSUMERCONSUMERcleanup-expired-sessionsprocess-email-queueCONSUMERSpanKind.INTERNALconsole.commandINTERNALimport { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
const tracer = trace.getTracer("my-app");
async function runScheduledJob() {
await tracer.startActiveSpan(
"cleanup-expired-sessions",
{ kind: SpanKind.CONSUMER },
async (span) => {
try {
await doWork();
span.setStatus({ code: SpanStatusCode.OK });
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
throw error;
} finally {
span.end();
}
}
);
}tracer.Start(ctx, "cleanup-expired-sessions", trace.WithSpanKind(trace.SpanKindConsumer))gen_ai.*gen_ai.*CONSUMERSpanKind.CLIENTINTERNALexception.*trace.nameai-agent.mdgen_ai.conversation.iduser.id| Attribute | Meaning |
|---|---|
| Requested / serving model |
| Provider ( |
| Operation ( |
| Token counts |
| Cached / reasoning tokens |
| Cost, when you compute pricing |
| Agent/workflow grouping name |
| Multi-turn conversation grouping (falls back to |
| End-user attribution: a stable customer id (account id, tenant id, or email), the same value across all of that user's conversations |
| Why generation stopped |
| Conversation content, shown on the trace detail page (skip if content must not leave the app) |
trace.inputtrace.outputspan.inputspan.outputgen_ai.promptgen_ai.completiontotal_tokenstotal_costimport { SpanKind } from "@opentelemetry/api";
return tracer.startActiveSpan("chat-completion", { kind: SpanKind.CLIENT }, async (span) => {
const response = await openai.chat.completions.create({ model: "gpt-4o", messages });
span.setAttributes({
"gen_ai.system": "openai",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": response.usage?.prompt_tokens ?? 0,
"gen_ai.usage.output_tokens": response.usage?.completion_tokens ?? 0,
"trace.name": "support-agent",
});
span.end();
return response;
});https://<instance>/api/otel/v1/traces{"Authorization": "Bearer <project-token>"}gen_ai.*gen_ai.*CONSUMERSpanKind.CLIENTexception.*trace.nameai-agent.mdgen_ai.conversation.iduser.id| Attribute | 含义 |
|---|---|
| 请求的模型 / 实际提供服务的模型 |
| Provider( |
| Operation( |
| Token 数量 |
| Cached token / reasoning token |
| 计算价格时的费用 |
| Agent/workflow 分组名称 |
| 多轮 conversation 分组(回退到 |
| 最终用户归因:稳定的客户 ID(account ID、tenant ID 或 email),该用户的所有 conversation 中都应使用相同值 |
| 生成停止的原因 |
| Conversation 内容,在 trace detail 页面中显示(如果内容不能离开 app,则跳过) |
gen_ai.promptgen_ai.completiontrace.inputtrace.outputspan.inputspan.outputtotal_tokenstotal_costimport { SpanKind } from "@opentelemetry/api";
return tracer.startActiveSpan("chat-completion", { kind: SpanKind.CLIENT }, async (span) => {
const response = await openai.chat.completions.create({ model: "gpt-4o", messages });
span.setAttributes({
"gen_ai.system": "openai",
"gen_ai.request.model": "gpt-4o",
"gen_ai.usage.input_tokens": response.usage?.prompt_tokens ?? 0,
"gen_ai.usage.output_tokens": response.usage?.completion_tokens ?? 0,
"trace.name": "support-agent",
});
span.end();
return response;
});https://<instance>/api/otel/v1/traces{"Authorization": "Bearer <project-token>"}/api/report<project-token>@https://<instance>/api/reportnpm install @tracewayapp/reactvuesveltejqueryfrontend<TracewayProvider connectionString="...">npm install -D @tracewayapp/bundler-plugintracewayDebugIds()@tracewayapp/bundler-plugin/vite/rollupTracewayDebugIdsWebpackPlugin/webpackbuild.sourcemap: truedevtool: "source-map"npm install -D @tracewayapp/sourcemap-uploadtraceway-sourcemaps --url <instance> --token <source-map-upload-token> --directory ./distTRACEWAY_URLTRACEWAY_SOURCEMAP_TOKENfrontend-js.mdvuesveltejqueryjs-sdkinstrumentation.ts@tracewayapp/bundler-pluginproductionBrowserSourceMaps: trueTracewayDebugIdsWebpackPlugin!isServer && !devnext build --webpack.js//# debugId=.js.mapdebugId.next/static/chunksflutter pub add tracewayTraceway.run(connectionString: '<token>@https://<instance>/api/report', child: MyApp())--obfuscate --split-debug-info.symbolsflutter.mdnpm install @tracewayapp/react-nativeTracewayProviderimplementation("com.tracewayapp:traceway:1.0.1")Traceway.init(application = this, connectionString = "<token>@https://<instance>/api/report", options = TracewayOptions(version = "1.0.0"))Application.onCreate()ApplicationINTERNETACCESS_NETWORK_STATETraceway.captureException(...)mapping.txtcom.tracewayapp.symbolsversion "1.0.1"mavenCentral()BuildConfig.TRACEWAY_PROGUARD_UUIDTracewayOptions(proguardUuid = ...)android.mdhttps://github.com/tracewayapp/traceway-ios.gitTraceway.start(connectionString: "<token>@https://<instance>/api/report", options: TracewayOptions(version: "1.0.0"))NSExceptionTraceway.capture(...)ios.md<instance>/api/otelAuthorization: Bearer <project-token>ios.md/api/report<project-token>@https://<instance>/api/reportnpm install @tracewayapp/reactvuesveltejqueryfrontend<TracewayProvider connectionString="...">npm install -D @tracewayapp/bundler-plugin@tracewayapp/bundler-plugin/vitetracewayDebugIds()/rollup/webpackTracewayDebugIdsWebpackPluginbuild.sourcemap: truedevtool: "source-map"npm install -D @tracewayapp/sourcemap-uploadtraceway-sourcemaps --url <instance> --token <source-map-upload-token> --directory ./distTRACEWAY_URLTRACEWAY_SOURCEMAP_TOKENfrontend-js.mdvuesveltejqueryjs-sdkinstrumentation.ts@tracewayapp/bundler-pluginproductionBrowserSourceMaps: true!isServer && !devTracewayDebugIdsWebpackPluginnext build --webpack.js//# debugId=.js.mapdebugId.next/static/chunksflutter pub add tracewayTraceway.run(connectionString: '<token>@https://<instance>/api/report', child: MyApp())--obfuscate --split-debug-info.symbolsflutter.mdnpm install @tracewayapp/react-nativeTracewayProviderimplementation("com.tracewayapp:traceway:1.0.1")Application.onCreate()Traceway.init(application = this, connectionString = "<token>@https://<instance>/api/report", options = TracewayOptions(version = "1.0.0"))ApplicationINTERNETACCESS_NETWORK_STATETraceway.captureException(...)mapping.txtcom.tracewayapp.symbolsversion "1.0.1"mavenCentral()BuildConfig.TRACEWAY_PROGUARD_UUIDTracewayOptions(proguardUuid = ...)android.mdhttps://github.com/tracewayapp/traceway-ios.gitTraceway.start(connectionString: "<token>@https://<instance>/api/report", options: TracewayOptions(version: "1.0.0"))NSExceptionTraceway.capture(...)ios.md<instance>/api/otelAuthorization: Bearer <project-token>ios.md| Deployment | Wants host metrics | What to do |
|---|---|---|
| Docker on a VM, or directly on a VM/host | Yes | Install the Traceway OTel Agent on the host (below). For Docker deploys this is the default; the agent goes on the host, not in a container. |
| Kubernetes | Any | Not the agent, which is a host service. Deploy the two collector workloads in |
| Serverless / PaaS | Any | No host to install on; skip. |
| Anything | No | Skip. |
kubernetes.mdservice.name*.utilizationk8s.cluster.nameroot_path: /hostfscurl -fsSL https://install.tracewayapp.com/install.sh | \
TRACEWAY_TOKEN=<project-token> \
TRACEWAY_ENDPOINT=https://<instance>/api/otel \
TRACEWAY_SERVICE_NAME=<host-label, e.g. api-prod-eu-1> \
bashTRACEWAY_ENDPOINT/api/otelTRACEWAY_LOG_PATHSTRACEWAY_PROCESS_NAMESuser_datadeploy.shsystem.cpu.utilizationsystem.memory.usageserver_nameTRACEWAY_SERVICE_NAMEhost.namehost.idhost.archos.typeTRACEWAY_SERVICE_NAMEsystem.cpu.utilizationsystem.memory.*system.filesystem.*system.disk.*system.network.*| 部署方式 | 需要 host metrics | 操作 |
|---|---|---|
| VM 上的 Docker,或直接运行在 VM/host 上 | 是 | 在 host 上安装 Traceway OTel Agent(见下文)。对于 Docker 部署,这是默认方案;Agent 安装在 host 上,而不是 container 中。 |
| Kubernetes | 任意 | 不使用作为 host service 的 Agent。按照此 skill 目录中的 |
| Serverless / PaaS | 任意 | 没有可安装 Agent 的 host;跳过。 |
| 任意 | 否 | 跳过。 |
kubernetes.mdservice.name*.utilizationk8s.cluster.nameroot_path: /hostfscurl -fsSL https://install.tracewayapp.com/install.sh | \\
TRACEWAY_TOKEN=<project-token> \\
TRACEWAY_ENDPOINT=https://<instance>/api/otel \\
TRACEWAY_SERVICE_NAME=<host-label, e.g. api-prod-eu-1> \\
bashTRACEWAY_ENDPOINT/api/otelTRACEWAY_LOG_PATHSTRACEWAY_PROCESS_NAMESuser_datadeploy.shsystem.cpu.utilizationsystem.memory.usageserver_nameTRACEWAY_SERVICE_NAMEhost.namehost.idhost.archos.typeTRACEWAY_SERVICE_NAMEsystem.cpu.utilizationsystem.memory.*system.filesystem.*system.disk.*system.network.*<instance>/<page>/endpoints/issues/tasks/ai-traces/logs/dashboardscurl <app>/api/users/1/2/3/endpointsGET /api/users/:idhttp.routeUNMATCHED/health/issues/tasks/logs/ai-traces/dashboardsserver_name/organizationservice.name*.utilizationtracewaytraceway logintraceway projects use <project-id>--project <project-id>traceway endpoints list --since 15m
traceway exceptions list --since 15m
traceway logs query --since 15m
traceway metrics query --name system.cpu.utilization --since 15mlistshow <id>/tasks/ai-tracescurl <their-app>/api/users/1/2/3/endpoints/issues/tasks<instance>/<page>/endpoints/issues/tasks/ai-traces/logs/dashboardscurl <app>/api/users/1/2/3/endpointsGET /api/users/:idhttp.routeUNMATCHED/health/issues/tasks/logs/ai-traces/dashboardsserver_name/organizationservice.name*.utilizationtracewaytraceway logintraceway projects use <project-id>--project <project-id>traceway endpoints list --since 15m
traceway exceptions list --since 15m
traceway logs query --since 15m
traceway metrics query --name system.cpu.utilization --since 15mlistshow <id>/tasks/ai-tracescurl <their-app>/api/users/1/2/3/endpoints/issues/tasks