Skip to content

Commit

Permalink
feat: Add "queryMaxLength" config var
Browse files Browse the repository at this point in the history
Before this the 10000 length truncation of the "query" field for
database spans was hardcoded.

Fixes: #1921
  • Loading branch information
trentm committed Aug 13, 2021
1 parent d7923d8 commit 71efcd6
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 56 deletions.
19 changes: 19 additions & 0 deletions docs/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,25 @@ This applies to the following properties:
- `error.exception.message`
- `error.log.message`


[[query-max-length]]
==== `queryMaxLength`

* *Type:* String
* *Default:* `10000b`
* *Env:* `ELASTIC_APM_QUERY_MAX_LENGTH`

The query captured for database spans will be truncated at this length.
It is expressed as an integer size and a unit (one of `b`, `kb`, `mb`, `gb`,
case-insensitive). For example, `100b` is 100 bytes, `2kb` is 2048 bytes, `1mb`
is 1048576 bytes.

Note that span data is limited at the upstream APM server to
{apm-server-ref-v}/configuration-process.html#max_event_size[`max_event_size`],
which defaults to 300kB. If you configure `queryMaxLength` too large, it could
result in spans that are rejected by APM server.


[[stack-trace-limit]]
==== `stackTraceLimit`

Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ interface AgentConfigOptions {
metricsLimit?: number;
payloadLogFile?: string;
centralConfig?: boolean;
queryMaxLength?: string;
sanitizeFieldNames?: Array<string>;
secretToken?: string;
serverCaCertFile?: string;
Expand Down
54 changes: 50 additions & 4 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ var DEFAULTS = {
maxQueueSize: 1024,
metricsInterval: '30s',
metricsLimit: 1000,
queryMaxLength: '10000b',
sanitizeFieldNames: ['password', 'passwd', 'pwd', 'secret', '*key', '*token*',
'*session*', '*credit*', '*card*', 'authorization', 'set-cookie',
'pw', 'pass', 'connect.sid'
Expand Down Expand Up @@ -135,6 +136,7 @@ var ENV_TABLE = {
metricsInterval: 'ELASTIC_APM_METRICS_INTERVAL',
metricsLimit: 'ELASTIC_APM_METRICS_LIMIT',
payloadLogFile: 'ELASTIC_APM_PAYLOAD_LOG_FILE',
queryMaxLength: 'ELASTIC_APM_QUERY_MAX_LENGTH',
sanitizeFieldNames: 'ELASTIC_SANITIZE_FIELD_NAMES',
serverCaCertFile: 'ELASTIC_APM_SERVER_CA_CERT_FILE',
secretToken: 'ELASTIC_APM_SECRET_TOKEN',
Expand Down Expand Up @@ -205,10 +207,16 @@ var TIME_OPTS = [
'spanFramesMinDuration'
]

// All new config vars should use STRICT_BYTES_OPTS rather than BYTES_OPTS
// to require the unit.
// https://github.com/elastic/apm/blob/master/specs/agents/configuration.md#durationsize-config-legacy-considerations
var BYTES_OPTS = [
'apiRequestSize',
'errorMessageMaxLength'
]
var STRICT_BYTES_OPTS = [
'queryMaxLength'
]

var MINUS_ONE_EQUAL_INFINITY = [
'transactionMaxSpans'
Expand Down Expand Up @@ -399,6 +407,7 @@ function normalize (opts, logger) {
normalizeKeyValuePairs(opts)
normalizeNumbers(opts)
normalizeBytes(opts)
normalizeStrictBytes(opts, logger)
normalizeArrays(opts)
normalizeTime(opts)
normalizeBools(opts, logger)
Expand Down Expand Up @@ -508,7 +517,29 @@ function normalizeNumbers (opts) {

function normalizeBytes (opts) {
for (const key of BYTES_OPTS) {
if (key in opts) opts[key] = bytes(String(opts[key]))
if (key in opts) {
opts[key] = numBytesFromStr(String(opts[key]), false)
}
}
}

function normalizeStrictBytes (opts, logger) {
for (const key of STRICT_BYTES_OPTS) {
if (key in opts) {
const normalized = numBytesFromStr(String(opts[key]), true)
if (normalized === undefined) {
// Limitation: `opts` needs to have *some* value for this config key.
// However, it has already merged all sources of config, so we no longer
// have appropriate information to fallback to a lower-precedence valid
// value -- except for DEFAULTS. The *right* handling would
// validate/normalize values *before* merging.
logger.warn('invalid size value "%s" for "%s": falling back to default value %j',
opts[key], key, DEFAULTS[key])
opts[key] = numBytesFromStr(DEFAULTS[key], true)
} else {
opts[key] = normalized
}
}
}
}

Expand Down Expand Up @@ -566,14 +597,28 @@ function truncateOptions (opts) {
if (opts.hostname) opts.hostname = truncate(String(opts.hostname), config.INTAKE_STRING_MAX_SIZE)
}

function bytes (input) {
// Translate a string byte size, e.g. '10kb', into an integer number of bytes.
//
// If `strict` is `false` then eliding the units, e.g. '42', is allowed,
// defaulting to 'b'. If the value is invalid, this returns `undefined`.
function numBytesFromStr (input, strict) {
const matches = input.match(/^(\d+)(b|kb|mb|gb)$/i)
if (!matches) return Number(input)
if (!matches) {
if (strict) {
return undefined
} else {
// Note: This currently allows float values and can return a NaN, which is
// unintentional.
// Not changing this for backwards compat. At the next major, the goal
// is to make all bytes-type config vars *strict*.
return Number(input)
}
}

const suffix = matches[2].toLowerCase()
let value = Number(matches[1])

if (!suffix || suffix === 'b') {
if (suffix === 'b') {
return value
}

Expand Down Expand Up @@ -687,6 +732,7 @@ function getBaseClientConfig (conf, agent) {
// Sanitize conf
truncateKeywordsAt: config.INTAKE_STRING_MAX_SIZE,
truncateErrorMessagesAt: conf.errorMessageMaxLength,
truncateQueriesAt: conf.queryMaxLength,

// HTTP conf
secretToken: conf.secretToken,
Expand Down
Loading

0 comments on commit 71efcd6

Please sign in to comment.