Exporters

处理和导出您的遥测数据

发送遥测数据到 OpenTelemetry Collector,以确保其正确导出。在生产环境中使用 Collector 是一种最佳实践。要可视化您的遥测数据,请将其导出到后端,例如 JaegerZipkinPrometheus特定厂商 的后端。

可用导出器

该注册表包含一份 JavaScript 导出器列表

在导出器中,OpenTelemetry Protocol (OTLP) 导出器是按照 OpenTelemetry 数据模型设计的,可以无损地发出 OTel 数据。此外,许多处理遥测数据的工具都支持 OTLP(例如 PrometheusJaeger 和大多数 厂商),为您提供高度的灵活性。要了解更多关于 OTLP 的信息,请参阅 OTLP 规范

此页面介绍了主要的 OpenTelemetry JavaScript 导出器以及如何设置它们。

OTLP

Collector 设置

要尝试并验证您的 OTLP 导出器,您可以在一个 Docker 容器中运行 collector,该容器将遥测数据直接写入控制台。

在一个空目录下,创建一个名为 collector-config.yaml 的文件,内容如下

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
exporters:
  debug:
    verbosity: detailed
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug]
    metrics:
      receivers: [otlp]
      exporters: [debug]
    logs:
      receivers: [otlp]
      exporters: [debug]

现在,在 Docker 容器中运行 collector

docker run -p 4317:4317 -p 4318:4318 --rm -v $(pwd)/collector-config.yaml:/etc/otelcol/config.yaml otel/opentelemetry-collector

此 collector 现在可以通过 OTLP 接受遥测数据。稍后,您可能需要 配置 collector,以便将其遥测数据发送到您的可观察性后端。

依赖项

如果您想将遥测数据发送到 OTLP 端点(例如 OpenTelemetry Collector Jaeger Prometheus),您可以在三种不同的协议之间进行选择来传输您的数据

首先,将相应的导出器包安装为项目的依赖项

npm install --save @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/exporter-metrics-otlp-proto
npm install --save @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/exporter-metrics-otlp-http
npm install --save @opentelemetry/exporter-trace-otlp-grpc \
  @opentelemetry/exporter-metrics-otlp-grpc

Node.js 使用方式

接下来,配置导出器以指向 OTLP 端点。例如,您可以更新 入门指南中的 instrumentation.ts 文件(如果您使用 JavaScript,则为 instrumentation.js),如下所示通过 OTLP(http/protobuf)导出 traces 和 metrics:

/*instrumentation.ts*/
import * as opentelemetry from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new OTLPTraceExporter({
    // optional - default url is https://:4318/v1/traces
    url: '<your-otlp-endpoint>/v1/traces',
    // optional - collection of custom headers to be sent with each request, empty by default
    headers: {},
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: '<your-otlp-endpoint>/v1/metrics', // url is optional and can be omitted - default is https://:4318/v1/metrics
      headers: {}, // an optional object containing custom headers to be sent with each request
    }),
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
/*instrumentation.js*/
const opentelemetry = require('@opentelemetry/sdk-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const {
  OTLPTraceExporter,
} = require('@opentelemetry/exporter-trace-otlp-proto');
const {
  OTLPMetricExporter,
} = require('@opentelemetry/exporter-metrics-otlp-proto');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new OTLPTraceExporter({
    // optional - default url is https://:4318/v1/traces
    url: '<your-otlp-endpoint>/v1/traces',
    // optional - collection of custom headers to be sent with each request, empty by default
    headers: {},
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: '<your-otlp-endpoint>/v1/metrics', // url is optional and can be omitted - default is https://:4318/v1/metrics
      headers: {}, // an optional object containing custom headers to be sent with each request
      concurrencyLimit: 1, // an optional limit on pending requests
    }),
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

浏览器中使用方式

当您在基于浏览器的应用程序中使用 OTLP 导出器时,您需要注意:

  1. 不支持使用 gRPC 进行导出
  2. 您网站的内容安全策略 (CSPs) 可能会阻止您的导出
  3. 跨域资源共享 (CORS) 头部可能不允许您的导出被发送
  4. 您可能需要将您的 Collector 公开到互联网

下面您将找到使用正确导出器、配置 CSPs 和 CORS 头部以及暴露 Collector 时需要注意的事项的说明。

使用 OTLP 导出器配合 HTTP/JSON 或 HTTP/protobuf

OpenTelemetry Collector Exporter with gRPC 仅适用于 Node.js,因此您只能使用OpenTelemetry Collector Exporter with HTTP/JSONOpenTelemetry Collector Exporter with HTTP/protobuf

如果您使用的是 OpenTelemetry Collector Exporter with HTTP/JSON,请确保您的导出器的接收端(Collector 或可观测性后端)接受 http/json,并且您正在将数据导出到正确的端点,端口设置为 4318。

配置 CSPs

如果您的网站使用了内容安全策略 (CSPs),请确保您的 OTLP 端点的域包含在内。如果您的 Collector 端点是 https://collector.example.com:4318/v1/traces,请添加以下指令

connect-src collector.example.com:4318/v1/traces

如果您的 CSP 未包含 OTLP 端点,您将看到一条错误消息,表明对您端点的请求违反了 CSP 指令。

配置 CORS 头部

如果您的网站和 Collector 托管在不同的域上,您的浏览器可能会阻止发送到 Collector 的请求。您需要为跨域资源共享 (CORS) 配置特殊的头部。

OpenTelemetry Collector 为基于 http 的接收器提供了一个功能,用于添加必要的头部以允许接收器接受来自 Web 浏览器的 traces。

receivers:
  otlp:
    protocols:
      http:
        include_metadata: true
        cors:
          allowed_origins:
            - https://foo.bar.com
            - https://*.test.com
          allowed_headers:
            - Example-Header
          max_age: 7200

安全地暴露您的 Collector

要从 Web 应用程序接收遥测数据,您需要允许您的最终用户浏览器将数据发送到您的 Collector。如果您的 Web 应用程序可以从公共互联网访问,您也必须让您的 Collector 对所有人可见。

建议您不要直接暴露您的 Collector,而是将其置于反向代理(NGINX、Apache HTTP Server 等)之后。反向代理可以负责 SSL 卸载、设置正确的 CORS 头部以及许多其他 Web 应用程序特有的功能。

下面您将找到一个适用于流行的 NGINX Web 服务器的配置,供您参考

server {
    listen 80 default_server;
    server_name _;
    location / {
        # Take care of preflight requests
        if ($request_method = 'OPTIONS') {
             add_header 'Access-Control-Max-Age' 1728000;
             add_header 'Access-Control-Allow-Origin' 'name.of.your.website.example.com' always;
             add_header 'Access-Control-Allow-Headers' 'Accept,Accept-Language,Content-Language,Content-Type' always;
             add_header 'Access-Control-Allow-Credentials' 'true' always;
             add_header 'Content-Type' 'text/plain charset=UTF-8';
             add_header 'Content-Length' 0;
             return 204;
        }

        add_header 'Access-Control-Allow-Origin' 'name.of.your.website.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Accept-Language,Content-Language,Content-Type' always;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://collector:4318;
    }
}

控制台

为了调试您的仪器或在开发环境中本地查看值,您可以使用将遥测数据写入控制台(stdout)的导出器。

如果您遵循了入门指南手动插桩指南,您已经安装了控制台导出器。

ConsoleSpanExporter 包含在@opentelemetry/sdk-trace-node 包中,而 ConsoleMetricExporter 包含在@opentelemetry/sdk-metrics 包中。

Jaeger

后端设置

Jaeger 原生支持 OTLP 来接收跟踪数据。您可以在 Docker 容器中运行 Jaeger,UI 可通过端口 16686 访问,OTLP 可通过端口 4317 和 4318 访问。

docker run --rm \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 9411:9411 \
  jaegertracing/all-in-one:latest

用法

然后按照说明设置 OTLP 导出器

Prometheus

要将您的指标数据发送到 Prometheus,您可以 启用 Prometheus 的 OTLP Receiver 并使用 OTLP 导出器,或者您可以使用 Prometheus 导出器,这是一个 MetricReader,它会启动一个 HTTP 服务器,在请求时收集指标并将其序列化为 Prometheus 文本格式。

后端设置

您可以按照以下说明,在 Docker 容器中运行 Prometheus,端口 9090 可访问。

创建一个名为 prometheus.yml 的文件,内容如下

scrape_configs:
  - job_name: dice-service
    scrape_interval: 5s
    static_configs:
      - targets: [host.docker.internal:9464]

在 Docker 容器中运行 Prometheus,UI 可通过端口 9090 访问。

docker run --rm -v ${PWD}/prometheus.yml:/prometheus/prometheus.yml -p 9090:9090 prom/prometheus --enable-feature=otlp-write-receive

依赖项

导出器包安装为应用程序的依赖项

npm install --save @opentelemetry/exporter-prometheus

更新您的 OpenTelemetry 配置以使用该导出器并将数据发送到您的 Prometheus 后端

import * as opentelemetry from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';

const sdk = new opentelemetry.NodeSDK({
  metricReader: new PrometheusExporter({
    port: 9464, // optional - default is 9464
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
const opentelemetry = require('@opentelemetry/sdk-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const sdk = new opentelemetry.NodeSDK({
  metricReader: new PrometheusExporter({
    port: 9464, // optional - default is 9464
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

通过上述配置,您可以访问您的指标,地址为 https://:9464/metrics。Prometheus 或带有 Prometheus receiver 的 OpenTelemetry Collector 可以从此端点抓取指标。

Zipkin

后端设置

您可以通过执行以下命令,在 Docker 容器中运行 Zipkin

docker run --rm -d -p 9411:9411 --name zipkin openzipkin/zipkin

依赖项

要将您的 trace 数据发送到 Zipkin,您可以使用 ZipkinExporter

导出器包安装为应用程序的依赖项

npm install --save @opentelemetry/exporter-zipkin

更新您的 OpenTelemetry 配置以使用该导出器并将数据发送到您的 Zipkin 后端

import * as opentelemetry from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { ZipkinExporter } from '@opentelemetry/exporter-zipkin';

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new ZipkinExporter({}),
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
const opentelemetry = require('@opentelemetry/sdk-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');

const sdk = new opentelemetry.NodeSDK({
  traceExporter: new ZipkinExporter({}),
  instrumentations: [getNodeAutoInstrumentations()],
});

自定义导出器

最后,您也可以编写自己的导出器。有关更多信息,请参阅 API 文档中的 SpanExporter 接口

批量发送 Span 和 Log 记录

OpenTelemetry SDK 提供了一组默认的 Span 和 Log 记录处理器,允许您逐个发送 Span(“简单”)或批量发送。建议使用批量发送,但如果您不想批量发送 Span 或 Log 记录,可以改用简单的处理器,如下所示:

/*instrumentation.ts*/
import * as opentelemetry from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  spanProcessors: [new SimpleSpanProcessor(exporter)],
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
/*instrumentation.js*/
const opentelemetry = require('@opentelemetry/sdk-node');
const {
  getNodeAutoInstrumentations,
} = require('@opentelemetry/auto-instrumentations-node');

const sdk = new opentelemetry.NodeSDK({
  spanProcessors: [new SimpleSpanProcessor(exporter)],
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();