Improve observability (#834)
* API generation * Added correlation id support * Updated docs * Updated test * Updated code generation * API generation * Updated code generation * Added support for client name and custom context object * Updated docs * Updated test * Fix docs * Updated docs * Added id support also for sniffing * Updated test * Update docs/observability.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Update docs/observability.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Apply suggestions * Update docs/configuration.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Update docs/configuration.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Update docs/observability.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Update docs/observability.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Update docs/observability.asciidoc Co-Authored-By: delvedor <delvedor@users.noreply.github.com> * Apply suggestions * Updated README.md * Fixed test * Addressed suggestions
This commit is contained in:
committed by
delvedor
parent
c47725d401
commit
ed9db61d1f
@ -133,6 +133,23 @@ function nodeSelector (connections) {
|
||||
return connections[index]
|
||||
}
|
||||
----
|
||||
|
||||
|`generateRequestId`
|
||||
a|`function` - function to generate the request id for every request, it takes two parameters, the request parameters and options. +
|
||||
By default it generates an incremental integer for every request. +
|
||||
_Custom function example:_
|
||||
[source,js]
|
||||
----
|
||||
function generateRequestId (params, options) {
|
||||
// your id generation logic
|
||||
// must be syncronous
|
||||
return 'id'
|
||||
}
|
||||
----
|
||||
|
||||
|`name`
|
||||
|`string` - The name to identify the client instance in the events. +
|
||||
_Default:_ `elasticsearch-js`
|
||||
|===
|
||||
|
||||
=== Advanced configuration
|
||||
@ -229,4 +246,4 @@ class MySerializer extends Serializer {
|
||||
const client = new Client({
|
||||
Serializer: MySerializer
|
||||
})
|
||||
----
|
||||
----
|
||||
|
||||
@ -9,6 +9,7 @@ include::configuration.asciidoc[]
|
||||
include::reference.asciidoc[]
|
||||
include::breaking-changes.asciidoc[]
|
||||
include::authentication.asciidoc[]
|
||||
include::observability.asciidoc[]
|
||||
include::child.asciidoc[]
|
||||
include::extend.asciidoc[]
|
||||
include::typescript.asciidoc[]
|
||||
|
||||
250
docs/observability.asciidoc
Normal file
250
docs/observability.asciidoc
Normal file
@ -0,0 +1,250 @@
|
||||
[[observability]]
|
||||
== Observability
|
||||
|
||||
The client does not provide a default logger, but instead it offers an event emitter interfaces to hook into internal events, such as `request` and `response`.
|
||||
|
||||
Correlating those events can be quite hard, especially if your applications have a large codebase with many events happening at the same time.
|
||||
|
||||
To help you with this, the client offers you a correlation id system and other features, let's see them in action.
|
||||
|
||||
=== Events
|
||||
The client is an event emitter, this means that you can listen for its event and add additional logic to your code, without need to change the client internals or your normal usage. +
|
||||
You can find the events names by access the `events` key of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { events } = require('@elastic/elasticsearch')
|
||||
console.log(events)
|
||||
----
|
||||
|
||||
The event emitter functionality can be useful if you want to log every request, response and error that is happening during the use of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const logger = require('my-logger')()
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
client.on('response', (err, result) => {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
} else {
|
||||
logger.info(result)
|
||||
}
|
||||
})
|
||||
----
|
||||
|
||||
The client emits the following events:
|
||||
[cols=2*]
|
||||
|===
|
||||
|`request`
|
||||
a|Emitted before sending the actual request to Elasticsearch _(emitted multiple times in case of retries)_.
|
||||
[source,js]
|
||||
----
|
||||
client.on('request', (err, result) => {
|
||||
console.log(err, result)
|
||||
})
|
||||
----
|
||||
|
||||
|`response`
|
||||
a|Emitted once Elasticsearch response has been received and parsed.
|
||||
[source,js]
|
||||
----
|
||||
client.on('response', (err, result) => {
|
||||
console.log(err, result)
|
||||
})
|
||||
----
|
||||
|
||||
|`sniff`
|
||||
a|Emitted when the client ends a sniffing request.
|
||||
[source,js]
|
||||
----
|
||||
client.on('sniff', (err, result) => {
|
||||
console.log(err, result)
|
||||
})
|
||||
----
|
||||
|
||||
|`resurrect`
|
||||
a|Emitted if the client is able to resurrect a dead node.
|
||||
[source,js]
|
||||
----
|
||||
client.on('resurrect', (err, result) => {
|
||||
console.log(err, result)
|
||||
})
|
||||
----
|
||||
|
||||
|===
|
||||
|
||||
The values of `result` in `request`, `response` and `sniff` will be:
|
||||
[source,ts]
|
||||
----
|
||||
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;
|
||||
};
|
||||
};
|
||||
----
|
||||
|
||||
While the `result` value in `resurrect` will be:
|
||||
[source,ts]
|
||||
----
|
||||
strategy: string;
|
||||
isAlive: boolean;
|
||||
connection: Connection;
|
||||
name: string;
|
||||
request: {
|
||||
id: any;
|
||||
};
|
||||
----
|
||||
|
||||
=== Correlation id
|
||||
Correlating events can be quite hard, especially if there are many events at the same time. The client offers you an automatic (and configurable) system to help you handle this problem.
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
client.on('request', (err, result) => {
|
||||
const { id } = result.meta.request
|
||||
if (err) {
|
||||
console.log({ error: err, reqId: id })
|
||||
}
|
||||
})
|
||||
|
||||
client.on('response', (err, result) => {
|
||||
const { id } = result.meta.request
|
||||
if (err) {
|
||||
console.log({ error: err, reqId: id })
|
||||
}
|
||||
})
|
||||
|
||||
client.search({
|
||||
index: 'my-index',
|
||||
body: { foo: 'bar' }
|
||||
}, (err, result) => {
|
||||
if (err) console.log(err)
|
||||
})
|
||||
----
|
||||
|
||||
By default the id is an incremental integer, but you can easily configure that with the `generateRequestId` option:
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({
|
||||
node: 'http://localhost:9200',
|
||||
// it takes two parameters, the request parameters and options
|
||||
generateRequestId: function (params, options) {
|
||||
// your id generation logic
|
||||
// must be syncronous
|
||||
return 'id'
|
||||
}
|
||||
})
|
||||
----
|
||||
|
||||
You can also specify a custom id per request:
|
||||
[source,js]
|
||||
----
|
||||
client.search({
|
||||
index: 'my-index',
|
||||
body: { foo: 'bar' }
|
||||
}, {
|
||||
id: 'custom-id'
|
||||
}, (err, result) => {
|
||||
if (err) console.log(err)
|
||||
})
|
||||
----
|
||||
|
||||
=== Context object
|
||||
Sometimes, you might need to make some custom data available in your events, you can do that via the `context` option of a request:
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
client.on('request', (err, result) => {
|
||||
const { id } = result.meta.request
|
||||
const { context } = result.meta
|
||||
if (err) {
|
||||
console.log({ error: err, reqId: id, context })
|
||||
}
|
||||
})
|
||||
|
||||
client.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',
|
||||
body: { foo: 'bar' }
|
||||
}, {
|
||||
context: { winter: 'is coming' }
|
||||
}, (err, result) => {
|
||||
if (err) console.log(err)
|
||||
})
|
||||
----
|
||||
|
||||
=== Client name
|
||||
If you are using multiple instances of the client or if you are using multiple child clients _(which is the recommended way to have multiple instances of the client)_, you might need to recognize which client you are using, the `name` options will help you in this regard:
|
||||
[source,js]
|
||||
----
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({
|
||||
node: 'http://localhost:9200',
|
||||
name: 'parent-client' // default to 'elasticsearch-js'
|
||||
})
|
||||
|
||||
const child = client.child({
|
||||
name: 'child-client'
|
||||
})
|
||||
|
||||
console.log(client.name, child.name)
|
||||
|
||||
client.on('request', (err, result) => {
|
||||
const { id } = result.meta.request
|
||||
const { name } = result.meta
|
||||
if (err) {
|
||||
console.log({ error: err, reqId: id, name })
|
||||
}
|
||||
})
|
||||
|
||||
client.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',
|
||||
body: { foo: 'bar' }
|
||||
}, (err, result) => {
|
||||
if (err) console.log(err)
|
||||
})
|
||||
|
||||
child.search({
|
||||
index: 'my-index',
|
||||
body: { foo: 'bar' }
|
||||
}, (err, result) => {
|
||||
if (err) console.log(err)
|
||||
})
|
||||
----
|
||||
@ -164,6 +164,14 @@ _Default:_ `null`
|
||||
|`querystring`
|
||||
|`object` - Custom querystring for the request. +
|
||||
_Default:_ `null`
|
||||
|
||||
|`id`
|
||||
|`any` - Custom request id. _(overrides the top level request id generator)_ +
|
||||
_Default:_ `null`
|
||||
|
||||
|`context`
|
||||
|`any` - Custom object per request. _(you can use it to pass some data to the clients events)_ +
|
||||
_Default:_ `null`
|
||||
|===
|
||||
|
||||
=== Error handling
|
||||
@ -203,103 +211,3 @@ Following you can find the errors exported by the client.
|
||||
|`ResponseError`
|
||||
|Generated when in case of a `4xx` or `5xx` response.
|
||||
|===
|
||||
|
||||
=== Events
|
||||
The client is an event emitter, this means that you can listen for its event and add additional logic to your code, without need to change the client internals or your normal usage. +
|
||||
You can find the events names by access the `events` key of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const { events } = require('@elastic/elasticsearch')
|
||||
console.log(events)
|
||||
----
|
||||
|
||||
The event emitter functionality can be useful if you want to log every request, response and error that is happening during the use of the client.
|
||||
|
||||
[source,js]
|
||||
----
|
||||
const logger = require('my-logger')()
|
||||
const { Client } = require('@elastic/elasticsearch')
|
||||
const client = new Client({ node: 'http://localhost:9200' })
|
||||
|
||||
client.on('response', (err, req) => {
|
||||
if (err) {
|
||||
logger.error(err)
|
||||
} else {
|
||||
logger.info(req)
|
||||
}
|
||||
})
|
||||
----
|
||||
|
||||
The client emits the following events:
|
||||
[cols=2*]
|
||||
|===
|
||||
|`request`
|
||||
a|Emitted before to send the actual request to Elasticsearch.
|
||||
[source,js]
|
||||
----
|
||||
client.on('request', (err, req) => {
|
||||
console.log(err, req)
|
||||
})
|
||||
----
|
||||
|
||||
|`response`
|
||||
a|Emitted before to send the actual request to Elasticsearch.
|
||||
[source,js]
|
||||
----
|
||||
client.on('response', (err, req) => {
|
||||
console.log(err, req)
|
||||
})
|
||||
----
|
||||
|
||||
|`sniff`
|
||||
a|Emitted before to send the actual request to Elasticsearch.
|
||||
[source,js]
|
||||
----
|
||||
client.on('sniff', (err, req) => {
|
||||
console.log(err, req)
|
||||
})
|
||||
----
|
||||
|
||||
|`resurrect`
|
||||
a|Emitted before to send the actual request to Elasticsearch.
|
||||
[source,js]
|
||||
----
|
||||
client.on('resurrect', (err, req) => {
|
||||
console.log(err, req)
|
||||
})
|
||||
----
|
||||
|
||||
|===
|
||||
|
||||
The values of `req` in `request`, `response` and `sniff` will be:
|
||||
[source,ts]
|
||||
----
|
||||
body: any;
|
||||
statusCode: number | null;
|
||||
headers: anyObject | null;
|
||||
warnings: string[] | null;
|
||||
meta: {
|
||||
request: {
|
||||
params: TransportRequestParams;
|
||||
options: TransportRequestOptions;
|
||||
};
|
||||
connection: Connection;
|
||||
attempts: number;
|
||||
aborted: boolean;
|
||||
sniff?: {
|
||||
hosts: any[];
|
||||
reason: string;
|
||||
};
|
||||
};
|
||||
----
|
||||
|
||||
While the `req` value in `resurrect` will be:
|
||||
[source,ts]
|
||||
----
|
||||
export interface ResurrectEvent {
|
||||
strategy: string;
|
||||
isAlive: boolean;
|
||||
connection: Connection;
|
||||
}
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user