Skip to content

Commit

Permalink
Merge branch 'master' into astorm-span-url-hostname-not-ip-issue-2035-x
Browse files Browse the repository at this point in the history
  • Loading branch information
astorm committed Apr 15, 2021
2 parents c3ed448 + df84561 commit a2c4777
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Notes:
[float]
===== Features
* Add `apm.addMetadataFilter(fn)` for filtering the
https://www.elastic.co/guide/en/apm/server/current/metadata-api.html[metadata object]
sent to APM server.
* The handling of sending events (transactions, spans, errors) to APM server
has improved in a few ways. During temporary spikes in load and/or an APM
server that is unresponsive, the agent will buffer a number of events and
Expand Down
62 changes: 53 additions & 9 deletions docs/agent-api.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Set or change the <<framework-name,`frameworkName`>> or <<framework-version,`fra
These config options can also be provided as part of the <<configuration,regular agent configuration>>.

[[apm-add-filter]]
==== `apm.addFilter(callback)`
==== `apm.addFilter(fn)`

[small]#Added in: v0.1.0#

Expand All @@ -97,6 +97,8 @@ Use `addFilter()` to supply a filter function.
Each filter function will be called just before data is being sent to the APM Server.
This will allow you to manipulate the data being sent,
for instance to remove sensitive information like passwords etc.
(Note: Filters added via `addFilter` are *not* applied to the "metadata"
object sent to the APM Server -- use `addMetadataFilter` instead.)

Each filter function will be called in the order they were added,
and will receive a `payload` object as the only argument,
Expand All @@ -114,8 +116,11 @@ Example usage:

[source,js]
----
apm.addFilter(function (payload) {
if (payload.context.request && payload.context.request.headers) {
apm.addFilter(function redactSecretHeader(payload) {
if (payload.context &&
payload.context.request &&
payload.context.request.headers &&
payload.context.request.headers['x-secret']) {
// redact sensitive data
payload.context.request.headers['x-secret'] = '[REDACTED]'
}
Expand All @@ -132,28 +137,67 @@ Though you can also use filter functions to add new contextual information to th
it's recommended that you use <<apm-set-user-context,`apm.setUserContext()`>> and <<apm-set-custom-context,`apm.setCustomContext()`>> for that purpose.

[[apm-add-error-filter]]
==== `apm.addErrorFilter(callback)`
==== `apm.addErrorFilter(fn)`

[small]#Added in: v2.0.0#

Similar to <<apm-add-filter,`apm.addFilter()`>>,
but the `callback` will only be called with error payloads.
but the `fn` will only be called with error payloads.

[[apm-add-transaction-filter]]
==== `apm.addTransactionFilter(callback)`
==== `apm.addTransactionFilter(fn)`

[small]#Added in: v2.0.0#

Similar to <<apm-add-filter,`apm.addFilter()`>>,
but the `callback` will only be called with transaction payloads.
but the `fn` will only be called with transaction payloads.

[[apm-add-span-filter]]
==== `apm.addSpanFilter(callback)`
==== `apm.addSpanFilter(fn)`

[small]#Added in: v2.0.0#

Similar to <<apm-add-filter,`apm.addFilter()`>>,
but the `callback` will only be called with span payloads.
but the `fn` will only be called with span payloads.

[[apm-add-metadata-filter]]
==== `apm.addMetadataFilter(fn)`

[small]#Added in: v3.14.0#

Use `addMetadataFilter(fn)` to supply a filter function for the
{apm-server-ref-v}/metadata-api.html[metadata object]
sent to the APM Server. This will allow you to manipulate the data being
sent, for instance to remove possibly sensitive information.

Each filter function will be called in the order they were added, and will
receive a `metadata` object as the only argument. The filter function is
synchronous and must return the manipulated object. Example usage:

[source,js]
----
apm.addMetadataFilter(function dropArgv(metadata) {
if (metadata.process && metadata.process.argv) {
delete metadata.process.argv
}
return metadata
})
----

Warning: It is the responsibility of the author to ensure the returned object
conforms to the
{apm-server-ref-v}/metadata-api.html#metadata-schema[metadata schema]
otherwise all APM data injest will fail. A metadata filter that breaks the
metadata will result in error logging from the agent, something like:

[source,text]
----
ERROR (elastic-apm-node): APM Server transport error (400): Unexpected APM Server response
APM Server accepted 0 events in the last request
Error: validation error: 'metadata' required
Document: {"metadata":null}
----


[[apm-set-user-context]]
==== `apm.setUserContext(context)`
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ declare class Agent implements Taggable, StartSpanFn {
addErrorFilter (fn: FilterFn): void;
addSpanFilter (fn: FilterFn): void;
addTransactionFilter (fn: FilterFn): void;
addMetadataFilter (fn: FilterFn): void;
flush (callback?: Function): void;
destroy (): void;

Expand Down
22 changes: 22 additions & 0 deletions lib/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ Agent.prototype.addFilter = function (fn) {
this.addErrorFilter(fn)
this.addTransactionFilter(fn)
this.addSpanFilter(fn)
// Note: This does *not* add to *metadata* filters, partly for backward
// compat -- the structure of metadata objects is quite different and could
// break existing filters -- and partly because that different structure
// means it makes less sense to re-use the same function to filter them.
}

Agent.prototype.addErrorFilter = function (fn) {
Expand Down Expand Up @@ -291,6 +295,24 @@ Agent.prototype.addSpanFilter = function (fn) {
this._spanFilters.push(fn)
}

Agent.prototype.addMetadataFilter = function (fn) {
if (typeof fn !== 'function') {
this.logger.error('Can\'t add filter of type %s', typeof fn)
return
} else if (!this._transport) {
this.logger.error('cannot add metadata filter to inactive or unconfigured agent (agent has no transport)')
return
} else if (typeof this._transport.addMetadataFilter !== 'function') {
// Graceful failure if unexpectedly using a too-old APM client.
this.logger.error('cannot add metadata filter: transport does not support addMetadataFilter')
return
}

// Metadata filters are handled by the APM client, where metadata is
// processed.
this._transport.addMetadataFilter(fn)
}

Agent.prototype.captureError = function (err, opts, cb) {
if (typeof opts === 'function') return this.captureError(err, null, opts)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"basic-auth": "^2.0.1",
"cookie": "^0.4.0",
"core-util-is": "^1.0.2",
"elastic-apm-http-client": "^9.7.1",
"elastic-apm-http-client": "^9.8.0",
"end-of-stream": "^1.4.4",
"error-stack-parser": "^2.0.6",
"escape-string-regexp": "^4.0.0",
Expand Down
33 changes: 33 additions & 0 deletions test/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,39 @@ test('filters', function (t) {
})
})

t.test('#addMetadataFilter()', function (t) {
t.plan(5 + APMServerWithDefaultAsserts.asserts)
APMServerWithDefaultAsserts(t, {}, { expect: ['metadata', 'transaction'] })
.on('listening', function () {
this.agent.addErrorFilter(function () {
t.fail('should not call error filter')
})
this.agent.addSpanFilter(function () {
t.fail('should not call span filter')
})
this.agent.addMetadataFilter(function (obj) {
t.strictEqual(obj.service.agent.name, 'nodejs')
obj.order = 1
return obj
})
this.agent.addMetadataFilter('invalid')
this.agent.addMetadataFilter(function (obj) {
t.strictEqual(obj.service.agent.name, 'nodejs')
t.strictEqual(++obj.order, 2)
return obj
})

this.agent.startTransaction()
this.agent.endTransaction()
this.agent.flush()
})
.on('data-metadata', function (metadata) {
t.strictEqual(metadata.service.agent.name, 'nodejs')
t.strictEqual(metadata.order, 2)
t.end()
})
})

const falsyValues = [undefined, null, false, 0, '', NaN]

falsyValues.forEach(falsy => {
Expand Down
3 changes: 3 additions & 0 deletions test/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ agent.addTransactionFilter(filter3)
agent.addSpanFilter(filter1)
agent.addSpanFilter(filter2)
agent.addSpanFilter(filter3)
agent.addMetadataFilter(filter1)
agent.addMetadataFilter(filter2)
agent.addMetadataFilter(filter3)

agent.flush()
agent.flush(() => {})
Expand Down

0 comments on commit a2c4777

Please sign in to comment.