@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/traces | ExportTraceServiceRequest, (中文(简体) ), application/json 或者说 application/x-protobuf (可选择) content-encoding: gzip页:1 200 {partialSuccess: {}} (对原生取出请求的空原生取回. |
POST /v1/logs | ExportLogsServiceRequest同样的编码和答案. |
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:只有那些标志. |
basic | Authorization: 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口. |
部分 嘲笑鸟。 。 。 .