elastic-apm-http-client
Advanced tools
Comparing version 10.2.0 to 10.3.0
# elastic-apm-http-client changelog | ||
## v10.3.0 | ||
- Add the `expectExtraMetadata: true` configuration option and | ||
`Client#setExtraMetadata(metadata)` method to provide a mechanism for the | ||
Node.js APM Agent to pass in metadata asynchronously and be sure that the | ||
client will not begin an intake request until that metadata is provided. | ||
This is to support passing in [AWS Lambda metadata that cannot be gathered | ||
until the first Lambda function | ||
invocation](https://github.com/elastic/apm-agent-nodejs/issues/2404). | ||
(Note: The `expectExtraMetadata` option cannot be used in combination with | ||
`cloudMetadataFetcher`.) | ||
- Use `Z_BEST_SPEED` for gzip compression per | ||
https://github.com/elastic/apm/blob/master/specs/agents/transport.md#compression | ||
## v10.2.0 | ||
@@ -4,0 +19,0 @@ |
146
index.js
@@ -95,6 +95,5 @@ 'use strict' | ||
this._intakeRequestGracefulExitFn = null // set in makeIntakeRequest | ||
// _encodedMetadata is pre-encoded JSON of metadata from `_conf.metadata` and | ||
// `_cloudMetadata` (asynchronously fetched via `_conf.cloudMetadataFetcher`) | ||
this._encodedMetadata = null | ||
this._cloudMetadata = null | ||
this._extraMetadata = null | ||
this._metadataFilters = new Filters() | ||
@@ -119,20 +118,31 @@ | ||
// start stream in corked mode, uncork when cloud | ||
// metadata is fetched and assigned. Also, the | ||
// _maybeUncork will not uncork until _encodedMetadata | ||
// is set | ||
this.cork() | ||
this._fetchAndEncodeMetadata(() => { | ||
// _fetchAndEncodeMetadata will have set/memoized the encoded | ||
// metadata to the _encodedMetadata property. | ||
if (this._conf.cloudMetadataFetcher && this._conf.expectExtraMetadata) { | ||
throw new Error('it is an error to create a Client with both cloudMetadataFetcher and expectExtraMetadata') | ||
} else if (this._conf.cloudMetadataFetcher) { | ||
// Start stream in corked mode, uncork when cloud metadata is fetched and | ||
// assigned. Also, the _maybeUncork will not uncork until _encodedMetadata | ||
// is set. | ||
this._log.trace('corking (cloudMetadataFetcher)') | ||
this.cork() | ||
this._fetchAndEncodeMetadata(() => { | ||
// _fetchAndEncodeMetadata will have set/memoized the encoded | ||
// metadata to the _encodedMetadata property. | ||
// This reverses the cork() call in the constructor above. "Maybe" uncork, | ||
// in case the client has been destroyed before this callback is called. | ||
this._maybeUncork() | ||
// This reverses the cork() call in the constructor above. "Maybe" uncork, | ||
// in case the client has been destroyed before this callback is called. | ||
this._maybeUncork() | ||
this._log.trace('uncorked (cloudMetadataFetcher)') | ||
// the `cloud-metadata` event allows listeners to know when the | ||
// agent has finished fetching and encoding its metadata for the | ||
// first time | ||
this.emit('cloud-metadata', this._encodedMetadata) | ||
}) | ||
// the `cloud-metadata` event allows listeners to know when the | ||
// agent has finished fetching and encoding its metadata for the | ||
// first time | ||
this.emit('cloud-metadata', this._encodedMetadata) | ||
}) | ||
} else if (this._conf.expectExtraMetadata) { | ||
// Uncorking will happen in the expected `.setExtraMetadata()` call. | ||
this._log.trace('corking (expectExtraMetadata)') | ||
this.cork() | ||
} else { | ||
this._resetEncodedMetadata() | ||
} | ||
@@ -144,3 +154,5 @@ this._chopper = new StreamChopper({ | ||
transform () { | ||
return zlib.createGzip() | ||
return zlib.createGzip({ | ||
level: zlib.constants.Z_BEST_SPEED | ||
}) | ||
} | ||
@@ -269,2 +281,24 @@ }) | ||
/** | ||
* Set extra additional metadata to be sent to APM Server in intake requests. | ||
* | ||
* If the Client was configured with `expectExtraMetadata: true` then will | ||
* uncork the client to allow intake requests to begin. | ||
* | ||
* If this is called multiple times, it is additive. | ||
*/ | ||
Client.prototype.setExtraMetadata = function (extraMetadata) { | ||
if (!this._extraMetadata) { | ||
this._extraMetadata = extraMetadata | ||
} else { | ||
metadataMergeDeep(this._extraMetadata, extraMetadata) | ||
} | ||
this._resetEncodedMetadata() | ||
if (this._conf.expectExtraMetadata) { | ||
this._maybeUncork() | ||
this._log.trace('uncorked (expectExtraMetadata)') | ||
} | ||
} | ||
/** | ||
* Add a filter function used to filter the "metadata" object sent to APM | ||
@@ -283,3 +317,4 @@ * server. See the APM Agent `addMetadataFilter` documentation for details. | ||
/** | ||
* (Re)set `_encodedMetadata` from this._conf.metadata and this._cloudMetadata. | ||
* (Re)set `_encodedMetadata` from this._conf.metadata, this._cloudMetadata, | ||
* this._extraMetadata and possible this._metadataFilters. | ||
*/ | ||
@@ -290,6 +325,9 @@ Client.prototype._resetEncodedMetadata = function () { | ||
// cycle for cloning should suffice. | ||
let metadata = JSON.parse(JSON.stringify(this._conf.metadata)) | ||
let metadata = deepClone(this._conf.metadata) | ||
if (this._cloudMetadata) { | ||
metadata.cloud = JSON.parse(JSON.stringify(this._cloudMetadata)) | ||
metadata.cloud = deepClone(this._cloudMetadata) | ||
} | ||
if (this._extraMetadata) { | ||
metadataMergeDeep(metadata, deepClone(this._extraMetadata)) | ||
} | ||
@@ -299,3 +337,2 @@ // Possible filters from APM agent's `apm.addMetadataFilter()`. | ||
metadata = this._metadataFilters.process(metadata) | ||
this._log.trace({ filteredMetadata: metadata }, 'filtered metadata') | ||
} | ||
@@ -305,2 +342,3 @@ | ||
this._encodedMetadata = this._encode({ metadata }, Client.encoding.METADATA) | ||
this._log.trace({ _encodedMetadata: this._encodedMetadata }, '_resetEncodedMetadata') | ||
} | ||
@@ -993,21 +1031,16 @@ | ||
Client.prototype._fetchAndEncodeMetadata = function (cb) { | ||
if (!this._conf.cloudMetadataFetcher) { | ||
// no metadata fetcher from the agent -- encode our data and move on | ||
assert(this._conf.cloudMetadataFetcher, '_fetchAndEncodeMetadata should not be called without a configured cloudMetadataFetcher') | ||
this._conf.cloudMetadataFetcher.getCloudMetadata((err, cloudMetadata) => { | ||
if (err) { | ||
// We ignore this error (other than logging it). A common case, when | ||
// not running on one of the big 3 clouds, is "all callbacks failed", | ||
// which is *fine*. Because it is a common "error" we don't log the | ||
// stack trace. | ||
this._log.trace('getCloudMetadata err: %s', err) | ||
} else if (cloudMetadata) { | ||
this._cloudMetadata = cloudMetadata | ||
} | ||
this._resetEncodedMetadata() | ||
process.nextTick(cb) | ||
} else { | ||
this._conf.cloudMetadataFetcher.getCloudMetadata((err, cloudMetadata) => { | ||
if (err) { | ||
// We ignore this error (other than logging it). A common case, when | ||
// not running on one of the big 3 clouds, is "all callbacks failed", | ||
// which is *fine*. Because it is a common "error" we don't log the | ||
// stack trace. | ||
this._log.trace('getCloudMetadata err: %s', err) | ||
} else if (cloudMetadata) { | ||
this._cloudMetadata = cloudMetadata | ||
} | ||
this._resetEncodedMetadata() | ||
cb() | ||
}) | ||
} | ||
cb() | ||
}) | ||
} | ||
@@ -1277,1 +1310,32 @@ | ||
} | ||
/** | ||
* Performs a deep merge of `source` into `target`. Mutates `target` only but | ||
* not its objects. Objects are merged, Arrays are not. | ||
* | ||
* @author inspired by [eden](https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6#gistcomment-2930530) | ||
*/ | ||
function metadataMergeDeep (target, source) { | ||
const isObject = (obj) => obj && typeof obj === 'object' && !Array.isArray(obj) | ||
if (!isObject(target) || !isObject(source)) { | ||
return source | ||
} | ||
Object.keys(source).forEach(key => { | ||
const targetValue = target[key] | ||
const sourceValue = source[key] | ||
if (isObject(targetValue) && isObject(sourceValue)) { | ||
target[key] = metadataMergeDeep(Object.assign({}, targetValue), sourceValue) | ||
} else { | ||
target[key] = sourceValue | ||
} | ||
}) | ||
return target | ||
} | ||
function deepClone (obj) { | ||
return JSON.parse(JSON.stringify(obj)) | ||
} |
{ | ||
"name": "elastic-apm-http-client", | ||
"version": "10.2.0", | ||
"version": "10.3.0", | ||
"description": "A low-level HTTP client for communicating with the Elastic APM intake API", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -108,2 +108,18 @@ # elastic-apm-http-client | ||
Cloud & Extra Metadata Configuration: | ||
- `cloudMetadataFetcher` - An object with a `getCloudMetadata(cb)` method | ||
for fetching metadata related to the current cloud environment. The callback | ||
is of the form `function (err, cloudMetadata)` and the returned `cloudMetadata` | ||
will be set on `metadata.cloud` for intake requests to APM Server. If | ||
provided, this client will not begin any intake requests until the callback | ||
is called. The `cloudMetadataFetcher` option must not be used with the | ||
`expectExtraMetadata` option. | ||
- `expectExtraMetadata` - A boolean option to indicate that the client should | ||
not allow any intake requests to begin until `cloud.setExtraMetadata(...)` | ||
has been called. It is the responsibility of the caller to call | ||
`cloud.setExtraMetadata()`. If not, then the Client will never perform an | ||
intake request. The `expectExtraMetadata` option must not be used with the | ||
`cloudMetadataFetcher` option. | ||
APM Agent Configuration via Kibana: | ||
@@ -302,3 +318,15 @@ | ||
### `client.setExtraMetadata([metadata])` | ||
Add extra metadata to be included in the "metadata" object sent to APM Server in | ||
intake requests. The given `metadata` object is merged into the metadata | ||
determined from the client configuration. | ||
The reason this exists is to allow some metadata to be provided asynchronously, | ||
especially in combination with the `expectExtraMetadata` configuration option | ||
to ensure that event data is not sent to APM Server until this extra metadata | ||
is provided. For example, in an AWS Lambda function some metadata is not | ||
available until the first function invocation -- which is some async time after | ||
Client creation. | ||
### `client.sendSpan(span[, callback])` | ||
@@ -305,0 +333,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
83673
1608
403