@crvouga/mockingbird-service-otel

中文文档
3.11.9发布于 2026/11/03MITTypeScriptES Module构建来源已验证近 30 天下载 1.6万

一个OTLP/HTTP收藏器(JSON和protobuf的痕迹和日志)的状态仿真器,以及在同一商店上的OpenObserve搜索API.

中文使用文档
文档目录8 节
  1. 安装
  2. 使用量
  3. 接收者
  4. 氧气搜索
  5. 管理员(超过标准合同)
  6. 命名空间
  7. 故意不模拟
  8. API 密码

@crvouga/mockingbird-service-otel (英语)

本地模拟器。 真正的API合同. 部分 嘲笑鸟。 。 。 .

国家模拟器 OTLP/HTTP采集器 {\fn华文仿宋\fs16\1chd1d1d1}在前面 OpenObserve (O2) 搜索 API, (中文(简体) ), 在一个商店。 当地和E2E公司停止向生产遥测基础设施出口,a 测试可以在结构化事件上断言:通过真实的OpenTelemotry SDK发射,然后读取 以相同的 SQL 日志返回, 我们的操作种子和调查代理发送( “调和语句) 发射 initial_credit_reconcile_completed 与 granted=1" (中文(简体) ).

  • 行动范围: 支助
  • OTLP/HTTP 遵循开远- proto v1; O2 路径遵循 OpenObserve 的 API,被修剪为 我们的客户怎么称呼的.

安装

npm install -D @crvouga/mockingbird-service-otel

仅限无害环境管理。 节点 22或 Bun 1.2. 没有本地的依赖, 没有原始库( 最小) 有线格式解码器被建入). 给它服务 npx mockingbird-otel serve, (中文(简体) ), createServer 从 ./server (节点),或 createRuntime 与任何获取服务器.

使用量

点 OTEL_EXPORTER_OTLP_ENDPOINT 在模拟器和设定中 OTEL_TRACES_SAMPLER_ARG=1 (我们的SDK) 样本的10%的根部横跨。 点 O2_BASE_URL 在同一模拟器上; O2_BASIC_AUTH 实值 任何基础64 user:password 除非 --search-auth 已经设定.

npx mockingbird-otel serve --port 8809 --ingest-token "$OTEL_AUTH_TOKEN"
# a local collector that takes no credentials, with a browser exporter allowed in
npx mockingbird-otel serve --ingest-auth none --cors-origins http://localhost:5173
# a hosted OTLP gateway: Authorization: Basic base64(instanceId:token)
npx mockingbird-otel serve --ingest-basic-auth "$OTLP_INSTANCE_ID:$OTLP_TOKEN"
import { SeverityNumber } from "@opentelemetry/api-logs"
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"
import { resourceFromAttributes } from "@opentelemetry/resources"
import { LoggerProvider, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs"
import { createServer } from "@crvouga/mockingbird-service-otel/server"

const server = await createServer()

// The OpenTelemetry SDK your application already uses. Only the exporter's URL changes.
const exporter = new OTLPLogExporter({
  url: `${server.url}/v1/logs`,
  headers: { authorization: "Bearer ingest-token" },
})
const loggerProvider = new LoggerProvider({
  resource: resourceFromAttributes({ "service.name": "backend" }),
  processors: [new SimpleLogRecordProcessor(exporter)],
})
loggerProvider.getLogger("backend").emit({
  severityNumber: SeverityNumber.INFO,
  severityText: "info",
  body: "reconcile done", // dropped: bodies are never stored
  attributes: { event: "initial_credit_reconcile_completed", granted: 1 },
})
await loggerProvider.forceFlush()

// Test control: long-poll until the event lands.
await fetch(`${server.url}/__admin/wait`, {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ kind: "log", where: { event: "initial_credit_reconcile_completed" } }),
})

// Then query it the way our O2 clients do (OpenObserve's search API is plain HTTP).
const search = await fetch(`${server.url}/api/30rBqcDevOrg7Hn2KmQ4xW9sLtY/_search`, {
  method: "POST",
  headers: { "content-type": "application/json", authorization: `Basic ${btoa("agent:pw")}` },
  body: JSON.stringify({
    query: { sql: `SELECT * FROM "default" WHERE event = 'initial_credit_reconcile_completed'` },
  }),
})
console.log(await search.json())
// { hits: [{ service_name: "backend", event: "…", granted: 1, severity_text: "info", _timestamp }], … }

await loggerProvider.shutdown()
await server.close()

接收者

路线行为
POST /v1/tracesExportTraceServiceRequest, (中文(简体) ), application/json 或者说 application/x-protobuf (可选择) content-encoding: gzip页:1 200 {partialSuccess: {}} (对原生取出请求的空原生取回.
POST /v1/logsExportLogsServiceRequest同样的编码和答案.
POST /v1/metrics接受和计算GET /__admin/otlp-metrics),从未被存储.
OPTIONS /v1/traces, (中文(简体) ), /v1/logs, (中文(简体) ), /v1/metrics浏览器导出者的 CORS 预飞, 当 cors.allowedOrigins 设置(参见 浏览器 CORS页:1 405。 。 。 .

不可解码的有效载荷为400个,其他内容类型为415. 每个接收器错误都是 google.rpc.Status (单位:千美元){code, message})在请求的编码中: 二进制原生于 application/x-protobuf 请求, JSON 否则。 可选 stream-name 标题选择 流( 默认) default) (中文(简体) ).

收集者认证. 接收者认证输出它所代表的收藏家的方式 因此,出口商在接受测试时保留其真正的信头。 模式是 ingestAuth 设置 (单位:千美元)createRuntime({settings}), (中文(简体) ), PUT /__admin/settings 每个命名空间,或 --ingest-auth:

ingestAuth导出需要被缩小
bearer (默认)Authorization: Bearer <token>- 任何标志,401没有.ingestTokens 编号 : --ingest-token:只有那些标志.
basicAuthorization: Basic base64(user:password),一个主机OTLP网关所取头条(Grafana Cloud: instanceId:token:任何证书,401没有.ingestUsers: [{username, password}] 编号 : --ingest-basic-auth <user:password>:只有这些.
none没什么 本地收藏家 一个 Authorization 仍然发送的出口商信头被忽略 .—— 说

拒绝的导出是 401 {"code": 16, "message": "Unauthenticated"}模式只需要它自己的 计划(无记名代号为401) basic 模式和反向),在前检查证书 有效载荷被解析,在每一种模式下,一个可接受的有效载荷被解析并以同样的方式储存. 只有接收器会改变:O2搜索总是想要Basic Certain.

浏览器 CORS. 默认关闭 : OPTIONS /v1/* 实值 405 并且没有带CORS信头. 设定 cors (同为三地; --cors-origins, (中文(简体) ), --cors-headers)将浏览器导出者回答为 打开数位收集器 cors: 块执行 :

import { createRuntime } from "@crvouga/mockingbird-service-otel"

createRuntime({
  settings: {
    ingestAuth: "none",
    cors: {
      allowedOrigins: ["https://app.example.test", "https://*.preview.example.test"], // or "*"
      allowedHeaders: ["authorization", "content-type", "traceparent", "tracestate"], // or "*"
      allowedMethods: ["POST"], // default GET, POST, HEAD
      exposedHeaders: [], // Access-Control-Expose-Headers on the export
      maxAge: 600, // Access-Control-Max-Age, seconds
    },
  },
})

飞行前OPTIONS 与 Origin 财务报告和审定财务报表 Access-Control-Request-Method) 之前已回答 认证, 总是 204。当来源、要求的方法和每个名称在 Access-Control-Request-Headers 允许携带 Access-Control-Allow-Origin (单位:千美元) 来源,或 *, (中文(简体) ), -Allow-Methods, (中文(简体) ), -Allow-Headers (两者都响应请求), -Allow-Credentials: true 财务报告和审定财务报表 -Max-Age不然,它不携带任何物 在浏览器中失败 。 包含错误的导出反应 Access-Control-Allow-Origin, (中文(简体) ), -Allow-Credentials 财务报告和审定财务报表 Vary: Origin 被允许的来源. 与 allowedHeaders 未设置 accept, (中文(简体) ), content-type 财务报告和审定财务报表 x-requested-with 通过;一旦 列表是准确的,所以名称 content-type (OTLP内容类型从来不是CORS-安全列表) 和你们的出口者所加的推广信头。 飞行前没有证书 名称空间: 它读取默认的名称空间设置, 除非端点使用 /__admin/ns/<name> 前缀( 设置传递给 createRuntime 适用于每个命名空间.

储存,因为氧气储存它. 每个字段名称都小写并平分(clientUserId → clientuserid, (中文(简体) ), http.status_code → http_status_code,与 _; 资源 属性获得一个 service_ 前缀( e)service_service_version, (中文(简体) ), service_deployment_environment_name) 除外 service.name → service_name; 无字段为 掉下来了 日志携带 _timestamp (单位), severity_text, (中文(简体) ), severity_number, (中文(简体) ), trace_id, (中文(简体) ), span_id 及其属性event...)斯潘斯携带 operation_name, (中文(简体) ), trace_id, (中文(简体) ), span_id, (中文(简体) ), reference_parent_span_id, (中文(简体) ), span_kind, (中文(简体) ), span_status (单位:千美元)UNSET编号 :OK编号 :ERROR, (中文(简体) ), start_time 编号 : end_time (数量), duration (单位), events (JSON:每个事件的名称、时间和属性)和 其属性,其标识与导出完全相同. 记录机构被丢弃 除非 --keep-bodies (一躯体可以持有即时或PHI;截取不是保护) body 栏仍然存在于图中,因此选择它继续工作的食谱.

鄂尔克通路. deployment.environment.name (或 deployment.environment(中文(简体) ) production 土地 输入 production org; 任何其它内容 development就像收藏家的dev -org默认. 默认值 : default, (中文(简体) ), development (单位:千美元)30rBqcDevOrg7Hn2KmQ4xW9sLtY, (中文(简体) ), production (单位:千美元)3HSzeProdOrg5Jd8VpN1cR6gTfB) (中文(简体) ).

路线行为
GET /api/organizations{data: [{id, identifier, name, type}]}: 显示名称-标识符.
GET /api/{org}/streams?type=logs|traces{list: [{name, stream_type, stats: {doc_num}}]}。 。 。 .
GET /api/{org}/streams/{stream}/schema?type=logs|traces{name, stream_type, schema: [{name, type}]}:每个领域都摄入了非核. 摄入前404个.
POST /api/{org}/_search?type=logs|traces{query: {sql, start_time, end_time (µs), from, size}} → {took, hits, total, from, size, scan_records, is_partial}。 。 。 .

需要基本的认证( E)401 Unauthorized Access页:1. {org} 必须是 org 标识符: 显示名称 (/api/production/_search如释放导体所发送的) 同样的赤出401个真正的O2答案,这样bug就保持可见.

SQL 子集 : SELECT * | expr [AS alias], … FROM "<stream>" [WHERE …] [GROUP BY …] [HAVING …] [ORDER BY expr [ASC|DESC], …] [LIMIT n [OFFSET m]] 与 AND编号 :OR编号 :NOT, (中文(简体) ), =, (中文(简体) ), !=编号 :<>, (中文(简体) ), <, (中文(简体) ), <=, (中文(简体) ), >, (中文(简体) ), >=, (中文(简体) ), IS [NOT] NULL, (中文(简体) ), [NOT] IN, (中文(简体) ), [NOT] LIKE编号 :ILIKE, (中文(简体) ), [NOT] BETWEEN (ISO 字符串比较 _timestamp),以及 str_match, (中文(简体) ), str_match_ignore_case, (中文(简体) ), match_all, (中文(简体) ), re_match, (中文(简体) ), lower, (中文(简体) ), upper, (中文(简体) ), tostring, (中文(简体) ), length, (中文(简体) ), coalesce, (中文(简体) ), count(*), (中文(简体) ), count([DISTINCT] x), (中文(简体) ), min, (中文(简体) ), max, (中文(简体) ), sum, (中文(简体) ), avg外侧行 start_time– 说吧 –end_time 除外,默认命令是 _timestamp DESC, (中文(简体) ), LIMIT 应用 在此之前 from编号 :size 呼唤. 列名称对大小写敏感,必须存在于流中 计划数字 : clientUserId 或者从未被摄入的一栏是 400 … No field named …作为我们的客户 等着吧 在无数据流上的查询会解答空打.

管理员(超过标准合同)

路线效果
GET /__admin/logs?service=&event=&severity=&trace_id=&org=已存储日志行( 有 _org, (中文(简体) ), _stream) (中文(简体) ).
GET /__admin/spans?service=&name=&trace_id=&org=存储横行.
POST /__admin/wait{kind: "log"|"span", where: {event: "…", …}, count?: 1, timeoutMs?: 5000, org?}: 长波到 count 行匹配( 200) {matched, count})或出局次数(408次与匹配次数). where 键可能被拼写为已放出( e)clientUserId, (中文(简体) ), service.name)或为所储存.
GET /__admin/otlp-metrics{requests, bytes, metrics} 页:1 /v1/metrics。 。 。 .
GET /__admin/settings调用命名空间的设置 .
PUT /__admin/settings{ingestAuth?: "bearer"|"basic"|"none", ingestTokens?, ingestUsers?: [{username, password}], cors?: {allowedOrigins, allowedHeaders?, allowedMethods?, exposedHeaders?, maxAge?}, searchUsers?: [{username, password}], organizations?: [{identifier, name}], routing?: {byEnvironment, default}, keepBodies?} 用于调用命名空间;只有密钥发出更改.

/__admin/spans, (中文(简体) ), /__admin/logs 财务报告和审定财务报表 /__admin/wait 是导出所摄入的: 每个跨度或日志记录的正则行数,每个属性保存(参见 存储 (见上文)。 原画 OTLP请求批次没有保留 GET /__admin/requests 记录每个导出,而不包含其 躯体.

错误预设( E)POST /__admin/faults {"preset": "<name>", "count"?: n}: rate_limited (429个+) retry-after: 1, (中文(简体) ), bad_gateway (502个), unavailable (503个), gateway_timeout (504个)——全部 被SDK重审.. server_error (500个)和 unauthorized (401),即SDK下降, partial_success (200个拒绝每个项目,只有JSON)和 search_unavailable (O2搜索503). 它们在每个认证模式下工作 并且使导出有问题 POST 只有,没有CORS飞行前 尸体是JSON 无论要求的编码.

命名空间

x-mockingbird-namespace页:1 /__admin/ns/<name> 前缀在端点和基址上,或者通过证书: PUT /__admin/credentials {"credentials": {"<OTEL_AUTH_TOKEN>": "w1", "<O2 username>": "w1"}} (图 使一个工人的出口 和搜索满足)。 单位 basic 模式 用户名( 实例编号); in none 模式没有,所以使用标题或路径前缀.

故意不模拟

  • gRPC OTLP(4317号口);只有OTLP/HTTP.
  • 计量存储和O2度量/PromQL API:仅计算计量出口.
  • O2的全SQL方言(DataFusion):相接,子克,窗口功能,算术, histogram(), (中文(简体) ), approx_*; 只有上面的子集.
  • 氧气通过 /api/{org}/{stream}/_json 或者说 /api/{org}/v1/logs、仪表板、警报和 (b) 收集器本身的分批、尾部取样和变压处理器.
  • partialSuccess 用于 protobuf 请求(发送空成功响应).
  • 原OTLP批次,无行所持的跨字段:链接,痕量状态,旗帜 状态信息、范围版本和已丢弃的项目计数. start_time 编号 : end_time 为: JSON 数字,所以 纳米秒超过 2^53 个回合(_timestamp 财务报告和审定财务报表 duration),用μs表示,准确.
  • 主机网关的地表: /otlp 路径前缀( 在模拟器上指定导出者) root,或设置每个符号终点),其符号范围和其401的措辞. 401号是 oTLP号 Status 见上文.
  • 收集器的 CORS 库也拒绝飞行前 Access-Control-Request-Headers 不是小写, 已排序的列表浏览器发送; 这里的名称按顺序和大小写匹配 .
  • 默认日志机构(参见 --keep-bodies) (中文(简体) ).

API 密码

导出仁爱说明
OtelAPI类进程模拟器 : fetch(request), (中文(简体) ), reset(), (中文(简体) ), logs(), (中文(简体) ), spans(), (中文(简体) ), state选项: sqlite, (中文(简体) ), now, (中文(简体) ), namespace, (中文(简体) ), settings。 。 。 .
createRuntime函数全面服务合同的仿真器(保健、管理员、等待、命名空间、证书、预设)。 选项 : settings (单位:千美元)ingestAuth, (中文(简体) ), ingestTokens, (中文(简体) ), ingestUsers, (中文(简体) ), cors, (中文(简体) ), searchUsers, (中文(简体) ), organizations, (中文(简体) ), routing, (中文(简体) ), keepBodies, (中文(简体) ), clock, (中文(简体) ), seed, (中文(简体) ), adminKey, (中文(简体) ), onLog, (中文(简体) ), sqlite。 。 。 .
OTEL_PRESETS对象每个命名的断层预设.
OTEL_NAMESPACE字符串服务名称, "otel"。 。 。 .
otelCredential函数OTLP无记名符号或 Basic 用户名( 如何将证书映射到命名空间) .
adminRow函数管理员路线显示的存储行( A)_org, (中文(简体) ), _stream 页:1.
DEFAULT_ORGANIZATIONS, (中文(简体) ), DEFAULT_SETTINGS, (中文(简体) ), INGEST_AUTH_MODES数值被播种的野兽和设置( E)ingestAuth: "bearer"和认证模式.
logRows, (中文(简体) ), spanRows, (中文(简体) ), formatKey函数OTLP/JSON导出 → O2行,而 O2 的字段名正态化.
decodeLogsRequest, (中文(简体) ), decodeTraceRequest, (中文(简体) ), ProtobufError函数,类原生出出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自.
parseSql, (中文(简体) ), execute, (中文(简体) ), referencedColumns, (中文(简体) ), SqlError函数,类SQL 子集: 解析, 运行在行上, 列出所读查询的列 .
document, (中文(简体) ), operationIds, (中文(简体) ), supportedOperationIds数值OpenAPI合同及其操作代号.
createServer, (中文(简体) ), serveTarget, (中文(简体) ), DEFAULT_PORT (单位:千美元)./server(中文(简体) )节点服务过来 node:http; 数据 serve CLI目标; 第8809口.

部分 嘲笑鸟。 。 。 .

版本与发布记录

@crvouga/mockingbird-service-otel 共发布 42 个版本,首次发布于 2026/09/23;过去 12 个月发布了 42 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
3.11.9latest2026/11/038 天前
3.11.82026/11/038 天前
3.11.72026/11/038 天前
3.11.62026/11/038 天前
3.11.52026/11/038 天前
3.11.42026/11/038 天前

关于 @crvouga/mockingbird-service-otel 的常见问题

@crvouga/mockingbird-service-otel 是什么?

一个OTLP/HTTP收藏器(JSON和protobuf的痕迹和日志)的状态仿真器,以及在同一商店上的OpenObserve搜索API.

如何安装 @crvouga/mockingbird-service-otel?

在项目目录运行 npm install @crvouga/mockingbird-service-otel,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 mockingbird-otel,可以用 npx mockingbird-otel 直接运行。

@crvouga/mockingbird-service-otel 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@crvouga/mockingbird-service-otel 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@crvouga/mockingbird-service-otel 可以商用吗?

@crvouga/mockingbird-service-otel 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@crvouga/mockingbird-service-otel 的下载量有多少?

根据 npm 官方下载统计,@crvouga/mockingbird-service-otel 在 2026/10/11 – 2026/11/09 这 30 天内被下载 16,397 次,日均约 547 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@crvouga/mockingbird-service-otel 还在维护吗?

最新版本 3.11.9 发布于 2026/11/03(8 天前);至今共发布 42 个版本,其中过去 12 个月发布了 42 个;首次发布于 2026/09/23。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。