diff --git a/packages/kbn-apm-config-loader/src/config.test.ts b/packages/kbn-apm-config-loader/src/config.test.ts index fe6247673e312..83438215716ac 100644 --- a/packages/kbn-apm-config-loader/src/config.test.ts +++ b/packages/kbn-apm-config-loader/src/config.test.ts @@ -42,12 +42,13 @@ describe('ApmConfiguration', () => { resetAllMocks(); }); - it('sets the correct service name', () => { + it('sets the correct service name and version', () => { packageMock.raw = { version: '9.2.1', }; const config = new ApmConfiguration(mockedRootDir, {}, false); - expect(config.getConfig('myservice').serviceName).toBe('myservice-9_2_1'); + expect(config.getConfig('myservice').serviceName).toBe('myservice'); + expect(config.getConfig('myservice').serviceVersion).toBe('9.2.1'); }); it('sets the git revision from `git rev-parse` command in non distribution mode', () => { diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts index aab82c6c06a58..897e7fd7ca610 100644 --- a/packages/kbn-apm-config-loader/src/config.ts +++ b/packages/kbn-apm-config-loader/src/config.ts @@ -30,8 +30,15 @@ const getDefaultConfig = (isDistributable: boolean): ApmAgentConfig => { return { active: false, globalLabels: {}, + // Do not use a centralized controlled config + centralConfig: false, + // Capture all exceptions that are not caught + logUncaughtExceptions: true, + // Can be performance intensive, disabling by default + breakdownMetrics: false, }; } + return { active: false, serverUrl: 'https://f1542b814f674090afd914960583265f.apm.us-central1.gcp.cloud.es.io:443', @@ -60,14 +67,14 @@ export class ApmConfiguration { ) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { version, build } = require(join(this.rootDir, 'package.json')); - this.kibanaVersion = version.replace(/\./g, '_'); + this.kibanaVersion = version; this.pkgBuild = build; } public getConfig(serviceName: string): ApmAgentConfig { return { ...this.getBaseConfig(), - serviceName: `${serviceName}-${this.kibanaVersion}`, + serviceName, }; } @@ -76,7 +83,8 @@ export class ApmConfiguration { const apmConfig = merge( getDefaultConfig(this.isDistributable), this.getConfigFromKibanaConfig(), - this.getDevConfig() + this.getDevConfig(), + this.getDistConfig() ); const rev = this.getGitRev(); @@ -88,6 +96,8 @@ export class ApmConfiguration { if (uuid) { apmConfig.globalLabels.kibana_uuid = uuid; } + + apmConfig.serviceVersion = this.kibanaVersion; this.baseConfig = apmConfig; } @@ -123,6 +133,19 @@ export class ApmConfiguration { } } + /** Config keys that cannot be overridden in production builds */ + private getDistConfig(): ApmAgentConfig { + if (!this.isDistributable) { + return {}; + } + + return { + // Headers & body may contain sensitive info + captureHeaders: false, + captureBody: 'off', + }; + } + private getGitRev() { if (this.isDistributable) { return this.pkgBuild.sha; diff --git a/src/apm.js b/src/apm.js index 8a0c010d993f1..bde37fa006c61 100644 --- a/src/apm.js +++ b/src/apm.js @@ -36,7 +36,22 @@ module.exports = function (serviceName = name) { apmConfig = loadConfiguration(process.argv, ROOT_DIR, isKibanaDistributable); const conf = apmConfig.getConfig(serviceName); - require('elastic-apm-node').start(conf); + const apm = require('elastic-apm-node'); + + // Filter out all user PII + apm.addFilter((payload) => { + try { + if (payload.context && payload.context.user && typeof payload.context.user === 'object') { + Object.keys(payload.context.user).forEach((key) => { + payload.context.user[key] = '[REDACTED]'; + }); + } + } finally { + return payload; + } + }); + + apm.start(conf); }; module.exports.getConfig = (serviceName) => { @@ -50,4 +65,3 @@ module.exports.getConfig = (serviceName) => { } return {}; }; -module.exports.isKibanaDistributable = isKibanaDistributable; diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index 3d81185e8a313..931650a67687c 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -110,6 +110,7 @@ export class ClusterManager { type: 'server', log: this.log, argv: serverArgv, + apmServiceName: 'kibana', })), ]; diff --git a/src/cli/cluster/worker.ts b/src/cli/cluster/worker.ts index f6205b41ac5a5..d28065765070b 100644 --- a/src/cli/cluster/worker.ts +++ b/src/cli/cluster/worker.ts @@ -49,6 +49,7 @@ interface WorkerOptions { title?: string; watch?: boolean; baseArgv?: string[]; + apmServiceName?: string; } export class Worker extends EventEmitter { @@ -89,6 +90,7 @@ export class Worker extends EventEmitter { NODE_OPTIONS: process.env.NODE_OPTIONS || '', kbnWorkerType: this.type, kbnWorkerArgv: JSON.stringify([...(opts.baseArgv || baseArgv), ...(opts.argv || [])]), + ELASTIC_APM_SERVICE_NAME: opts.apmServiceName || '', }; } diff --git a/src/cli/dev.js b/src/cli/dev.js index 9d0cb35c3d730..a284c82dfeb6e 100644 --- a/src/cli/dev.js +++ b/src/cli/dev.js @@ -17,6 +17,6 @@ * under the License. */ -require('../apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); +require('../apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy'); require('../setup_node_env'); require('./cli'); diff --git a/src/core/public/apm_system.ts b/src/core/public/apm_system.ts index 0a18f02c97293..5e4953b96dc5b 100644 --- a/src/core/public/apm_system.ts +++ b/src/core/public/apm_system.ts @@ -28,6 +28,7 @@ import type { InternalApplicationStart } from './application'; interface ApmConfig { // AgentConfigOptions is not exported from @elastic/apm-rum + active?: boolean; globalLabels?: Record; } @@ -39,10 +40,10 @@ export class ApmSystem { private readonly enabled: boolean; /** * `apmConfig` would be populated with relevant APM RUM agent - * configuration if server is started with `ELASTIC_APM_ACTIVE=true` + * configuration if server is started with elastic.apm.* config. */ constructor(private readonly apmConfig?: ApmConfig) { - this.enabled = process.env.IS_KIBANA_DISTRIBUTABLE !== 'true' && apmConfig != null; + this.enabled = apmConfig != null && !!apmConfig.active; } async setup() { diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index a96c16314aea0..1843bdd75be54 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -25,6 +25,10 @@ const HANDLED_IN_NEW_PLATFORM = Joi.any().description( ); export default () => Joi.object({ + elastic: Joi.object({ + apm: HANDLED_IN_NEW_PLATFORM, + }).default(), + pkg: Joi.object({ version: Joi.string().default(Joi.ref('$version')), branch: Joi.string().default(Joi.ref('$branch')), diff --git a/src/legacy/ui/apm/index.js b/src/legacy/ui/apm/index.js index c43b7b01d1159..e58f6fb73320d 100644 --- a/src/legacy/ui/apm/index.js +++ b/src/legacy/ui/apm/index.js @@ -17,18 +17,10 @@ * under the License. */ -import { getConfig, isKibanaDistributable } from '../../../apm'; +import { getConfig } from '../../../apm'; import agent from 'elastic-apm-node'; -const apmEnabled = !isKibanaDistributable && process.env.ELASTIC_APM_ACTIVE === 'true'; - -export function apmImport() { - return apmEnabled ? 'import { init } from "@elastic/apm-rum"' : ''; -} - -export function apmInit(config) { - return apmEnabled ? `init(${config})` : ''; -} +const apmEnabled = getConfig()?.active; export function getApmConfig(requestPath) { if (!apmEnabled) { @@ -36,11 +28,9 @@ export function getApmConfig(requestPath) { } const config = { ...getConfig('kibana-frontend'), - ...{ - active: true, - pageLoadTransactionName: requestPath, - }, + pageLoadTransactionName: requestPath, }; + /** * Get current active backend transaction to make distrubuted tracing * work for rendering the app