可观测性编辑

客户端不提供默认日志记录器,而是提供一个事件发射器接口来挂钩内部事件,例如 requestresponse

关联这些事件可能很困难,特别是如果您的应用程序具有庞大的代码库,并且同时发生许多事件。

为了帮助您解决这个问题,客户端为您提供了一个关联 ID 系统和其他功能。让我们看看它们的作用。

事件编辑

客户端是一个事件发射器,这意味着您可以监听其事件并向代码添加额外的逻辑,而无需更改客户端内部结构或正常使用方式。您可以通过访问客户端的 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)
  }
})

客户端发出以下事件

序列化

在开始序列化和压缩之前发出。如果要测量此阶段的持续时间,则应测量此事件与 request 之间经过的时间。

client.diagnostic.on('serialization', (err, result) => {
  console.log(err, result)
})

请求

在向 Elasticsearch 发送实际请求之前发出*(如果重试,则会多次发出)*。

client.diagnostic.on('request', (err, result) => {
  console.log(err, result)
})

反序列化

在开始反序列化和解压缩之前发出。如果要测量此阶段的持续时间,则应测量此事件与 response 之间经过的时间。*(在某些情况下,可能不会发出此事件)*。

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)
})

serializationrequestdeserializationresponsesniff 中的 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)