可观测性
编辑可观测性
编辑为了观察和衡量 Elasticsearch 客户端的使用情况,提供了几个客户端功能。
首先,从 8.15.0 版本开始,客户端原生支持 OpenTelemetry,这允许您将客户端使用数据发送到任何支持 OpenTelemetry 的端点,而无需对您的 JavaScript 代码库进行任何更改。
此外,客户端没有提供默认的日志记录器,而是提供了一个事件发射器接口来挂钩内部事件,例如 request
和 response
,允许您记录您关心的事件,或者以您需要的任何方式对客户端使用情况做出反应。
关联事件可能很困难,特别是当您的应用程序具有庞大的代码库并且同时发生许多事件时。为了帮助您解决这个问题,客户端提供了一个关联 ID 系统和其他功能。
所有这些可观测性功能将在下面记录。
OpenTelemetry
编辑客户端支持 OpenTelemetry 的 零代码检测,以启用将每个客户端请求跟踪为 OpenTelemetry span。这些 span 遵循所有 Elasticsearch 的语义 OpenTelemetry 约定,除了 db.query.text
。
要开始将 Elasticsearch 跟踪数据发送到您的 OpenTelemetry 端点,请按照 OpenTelemetry 的零代码检测指南,或以下步骤进行操作
- 安装
@opentelemetry/api
和@opentelemetry/auto-instrumentations-node
作为 Node.js 依赖项 -
导出具有适当值的以下环境变量
-
OTEL_EXPORTER_OTLP_ENDPOINT
-
OTEL_EXPORTER_OTLP_HEADERS
-
OTEL_RESOURCE_ATTRIBUTES
-
OTEL_SERVICE_NAME
-
-
在启动时
require
Node.js 自动检测库
node --require '@opentelemetry/auto-instrumentations-node/register' index.js
事件
编辑客户端是一个事件发射器。这意味着您可以监听它的事件,以便向您的代码添加额外的逻辑,而无需更改客户端的内部结构或您如何使用客户端。您可以通过访问客户端的 events
键来查找事件的名称
const { events } = require('@elastic/elasticsearch') console.log(events)
如果您想记录客户端创建的每个请求、响应或错误,事件发射器功能可能会很有用
const logger = require('my-logger')() const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' } }) client.diagnostic.on('response', (err, result) => { if (err) { logger.error(err) } else { logger.info(result) } })
客户端会发出以下事件
|
在开始序列化和压缩之前发出。如果您想测量此阶段的持续时间,则应测量此事件和 client.diagnostic.on('serialization', (err, result) => { console.log(err, result) }) |
|
在将实际请求发送到 Elasticsearch 之前发出(如果发生重试,则会多次发出)。 client.diagnostic.on('request', (err, result) => { console.log(err, result) }) |
|
在开始反序列化和解压缩之前发出。如果您想测量此阶段的持续时间,则应测量此事件和 client.diagnostic.on('deserialization', (err, result) => { console.log(err, result) }) |
|
在收到并解析 Elasticsearch 响应后发出。 client.diagnostic.on('response', (err, result) => { console.log(err, result) }) |
|
当客户端结束嗅探请求时发出。 client.diagnostic.on('sniff', (err, result) => { console.log(err, result) }) |
|
如果客户端能够复活死节点,则发出。 client.diagnostic.on('resurrect', (err, result) => { console.log(err, result) }) |
serialization
、request
、deserialization
、response
和 sniff
中的 result
的值是
body: any; statusCode: number | null; headers: anyObject | null; warnings: string[] | null; meta: { context: any; name: string; request: { params: TransportRequestParams; options: TransportRequestOptions; id: any; }; connection: Connection; attempts: number; aborted: boolean; sniff?: { hosts: any[]; reason: string; }; };
而 resurrect
中的 result
值是
strategy: string; isAlive: boolean; connection: Connection; name: string; request: { id: any; };
事件顺序
编辑事件顺序在以下图中描述,在某些极端情况下,不保证顺序。您可以在 test/acceptance/events-order.test.js
中找到该顺序如何根据情况变化。
serialization │ │ (serialization and compression happens between those two events) │ └─▶ request │ │ (actual time spent over the wire) │ └─▶ deserialization │ │ (deserialization and decompression happens between those two events) │ └─▶ response
关联 ID
编辑关联事件可能很困难,特别是当同时发生许多事件时。客户端为您提供了一个自动(且可配置)的系统来帮助您处理此问题。
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' } }) client.diagnostic.on('request', (err, result) => { const { id } = result.meta.request if (err) { console.log({ error: err, reqId: id }) } }) client.diagnostic.on('response', (err, result) => { const { id } = result.meta.request if (err) { console.log({ error: err, reqId: id }) } }) client.search({ index: 'my-index', query: { match_all: {} } }).then(console.log, console.log)
默认情况下,ID 是一个递增的整数,但您可以使用 generateRequestId
选项进行配置
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' }, // it takes two parameters, the request parameters and options generateRequestId: function (params, options) { // your id generation logic // must be syncronous return 'id' } })
您还可以为每个请求指定自定义 ID
client.search({ index: 'my-index', query: { match_all: {} } }, { id: 'custom-id' }).then(console.log, console.log)
上下文对象
编辑有时,您可能需要在您的事件中提供一些自定义数据,您可以通过请求的 context
选项来实现
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' } }) client.diagnostic.on('request', (err, result) => { const { id } = result.meta.request const { context } = result.meta if (err) { console.log({ error: err, reqId: id, context }) } }) client.diagnostic.on('response', (err, result) => { const { id } = result.meta.request const { winter } = result.meta.context if (err) { console.log({ error: err, reqId: id, winter }) } }) client.search({ index: 'my-index', query: { match_all: {} } }, { context: { winter: 'is coming' } }).then(console.log, console.log)
上下文对象也可以在客户端配置中配置为全局选项。如果您同时提供了两者,则两个上下文对象将进行浅合并,并且 API 级别的对象将具有优先权。
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' }, context: { winter: 'is coming' } }) client.diagnostic.on('request', (err, result) => { const { id } = result.meta.request const { context } = result.meta if (err) { console.log({ error: err, reqId: id, context }) } }) client.diagnostic.on('response', (err, result) => { const { id } = result.meta.request const { winter } = result.meta.context if (err) { console.log({ error: err, reqId: id, winter }) } }) client.search({ index: 'my-index', query: { match_all: {} } }, { context: { winter: 'has come' } }).then(console.log, console.log)
客户端名称
编辑如果您正在使用客户端的多个实例,或者您正在使用多个子客户端(这是拥有多个客户端实例的推荐方式),您可能需要识别您正在使用的客户端。name
选项在这方面可以帮助您。
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' }, name: 'parent-client' // default to 'elasticsearch-js' }) const child = client.child({ name: 'child-client' }) console.log(client.name, child.name) client.diagnostic.on('request', (err, result) => { const { id } = result.meta.request const { name } = result.meta if (err) { console.log({ error: err, reqId: id, name }) } }) client.diagnostic.on('response', (err, result) => { const { id } = result.meta.request const { name } = result.meta if (err) { console.log({ error: err, reqId: id, name }) } }) client.search({ index: 'my-index', query: { match_all: {} } }).then(console.log, console.log) child.search({ index: 'my-index', query: { match_all: {} } }).then(console.log, console.log)
X-Opaque-Id 支持
编辑为了提高可观测性,客户端提供了一种简单的方法来配置 X-Opaque-Id
标头。如果在特定请求中设置 X-Opaque-Id
,这允许您在弃用日志中发现此标识符,可以帮助您识别搜索慢日志的来源以及识别正在运行的任务。
X-Opaque-Id
应在每个请求中配置,为此您可以使用 opaqueId
选项,如以下示例所示。生成的标头将为 { 'X-Opaque-Id': 'my-search' }
。
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' } }) client.search({ index: 'my-index', body: { foo: 'bar' } }, { opaqueId: 'my-search' }).then(console.log, console.log)
有时,在需要识别特定客户端或服务器的情况下,可能需要为所有 X-Opaque-Id
标头添加特定字符串作为前缀。为此,客户端提供了一个顶级配置选项:opaqueIdPrefix
。在以下示例中,生成的标头将为 { 'X-Opaque-Id': 'proxy-client::my-search' }
。
const { Client } = require('@elastic/elasticsearch') const client = new Client({ cloud: { id: '<cloud-id>' }, auth: { apiKey: 'base64EncodedKey' }, opaqueIdPrefix: 'proxy-client::' }) client.search({ index: 'my-index', body: { foo: 'bar' } }, { opaqueId: 'my-search' }).then(console.log, console.log)