* Move src/apm.js to @kbn/apm-config-loader * add unit tests for `initApm` * return undefined instead of empty config * use ?.
This commit is contained in:
parent
ff03d9bad3
commit
3c84c7ca58
12 changed files with 169 additions and 69 deletions
|
@ -8,13 +8,16 @@
|
||||||
|
|
||||||
import { getConfigurationFilePaths, getConfigFromFiles, applyConfigOverrides } from './utils';
|
import { getConfigurationFilePaths, getConfigFromFiles, applyConfigOverrides } from './utils';
|
||||||
import { ApmConfiguration } from './config';
|
import { ApmConfiguration } from './config';
|
||||||
|
import { ApmAgentConfig } from './types';
|
||||||
|
|
||||||
|
let apmConfig: ApmConfiguration | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the APM configuration.
|
* Load the APM configuration.
|
||||||
*
|
*
|
||||||
* @param argv the `process.argv` arguments
|
* @param argv the `process.argv` arguments
|
||||||
* @param rootDir The root directory of kibana (where the sources and the `package.json` file are)
|
* @param rootDir The root directory of kibana (where the sources and the `package.json` file are)
|
||||||
* @param production true for production builds, false otherwise
|
* @param isDistributable true for production builds, false otherwise
|
||||||
*/
|
*/
|
||||||
export const loadConfiguration = (
|
export const loadConfiguration = (
|
||||||
argv: string[],
|
argv: string[],
|
||||||
|
@ -24,5 +27,19 @@ export const loadConfiguration = (
|
||||||
const configPaths = getConfigurationFilePaths(argv);
|
const configPaths = getConfigurationFilePaths(argv);
|
||||||
const rawConfiguration = getConfigFromFiles(configPaths);
|
const rawConfiguration = getConfigFromFiles(configPaths);
|
||||||
applyConfigOverrides(rawConfiguration, argv);
|
applyConfigOverrides(rawConfiguration, argv);
|
||||||
return new ApmConfiguration(rootDir, rawConfiguration, isDistributable);
|
|
||||||
|
apmConfig = new ApmConfiguration(rootDir, rawConfiguration, isDistributable);
|
||||||
|
return apmConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getConfiguration = (serviceName: string): ApmAgentConfig | undefined => {
|
||||||
|
// integration test runner starts a kibana server that import the module without initializing APM.
|
||||||
|
// so we need to check initialization of the config.
|
||||||
|
// note that we can't just load the configuration during this module's import
|
||||||
|
// because jest IT are ran with `--config path-to-jest-config.js` which conflicts with the CLI's `config` arg
|
||||||
|
// causing the config loader to try to load the jest js config as yaml and throws.
|
||||||
|
if (apmConfig) {
|
||||||
|
return apmConfig.getConfig(serviceName);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { loadConfiguration } from './config_loader';
|
export { getConfiguration } from './config_loader';
|
||||||
|
export { initApm } from './init_apm';
|
||||||
export type { ApmConfiguration } from './config';
|
export type { ApmConfiguration } from './config';
|
||||||
export type { ApmAgentConfig } from './types';
|
export type { ApmAgentConfig } from './types';
|
||||||
|
|
12
packages/kbn-apm-config-loader/src/init_apm.test.mocks.ts
Normal file
12
packages/kbn-apm-config-loader/src/init_apm.test.mocks.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const mockLoadConfiguration = jest.fn();
|
||||||
|
jest.doMock('./config_loader', () => ({
|
||||||
|
loadConfiguration: mockLoadConfiguration,
|
||||||
|
}));
|
66
packages/kbn-apm-config-loader/src/init_apm.test.ts
Normal file
66
packages/kbn-apm-config-loader/src/init_apm.test.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { mockLoadConfiguration } from './init_apm.test.mocks';
|
||||||
|
|
||||||
|
import { initApm } from './init_apm';
|
||||||
|
import apm from 'elastic-apm-node';
|
||||||
|
|
||||||
|
describe('initApm', () => {
|
||||||
|
let apmAddFilterSpy: jest.SpyInstance;
|
||||||
|
let apmStartSpy: jest.SpyInstance;
|
||||||
|
let getConfig: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
apmAddFilterSpy = jest.spyOn(apm, 'addFilter').mockImplementation(() => undefined);
|
||||||
|
apmStartSpy = jest.spyOn(apm, 'start').mockImplementation(() => undefined as any);
|
||||||
|
getConfig = jest.fn();
|
||||||
|
|
||||||
|
mockLoadConfiguration.mockImplementation(() => ({
|
||||||
|
getConfig,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
mockLoadConfiguration.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls `loadConfiguration` with the correct options', () => {
|
||||||
|
initApm(['foo', 'bar'], 'rootDir', true, 'service-name');
|
||||||
|
|
||||||
|
expect(mockLoadConfiguration).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockLoadConfiguration).toHaveBeenCalledWith(['foo', 'bar'], 'rootDir', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls `apmConfigLoader.getConfig` with the correct options', () => {
|
||||||
|
initApm(['foo', 'bar'], 'rootDir', true, 'service-name');
|
||||||
|
|
||||||
|
expect(getConfig).toHaveBeenCalledTimes(1);
|
||||||
|
expect(getConfig).toHaveBeenCalledWith('service-name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registers a filter using `addFilter`', () => {
|
||||||
|
initApm(['foo', 'bar'], 'rootDir', true, 'service-name');
|
||||||
|
|
||||||
|
expect(apmAddFilterSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(apmAddFilterSpy).toHaveBeenCalledWith(expect.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('starts apm with the config returned from `getConfig`', () => {
|
||||||
|
const config = {
|
||||||
|
foo: 'bar',
|
||||||
|
};
|
||||||
|
getConfig.mockReturnValue(config);
|
||||||
|
|
||||||
|
initApm(['foo', 'bar'], 'rootDir', true, 'service-name');
|
||||||
|
|
||||||
|
expect(apmStartSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(apmStartSpy).toHaveBeenCalledWith(config);
|
||||||
|
});
|
||||||
|
});
|
39
packages/kbn-apm-config-loader/src/init_apm.ts
Normal file
39
packages/kbn-apm-config-loader/src/init_apm.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { loadConfiguration } from './config_loader';
|
||||||
|
|
||||||
|
export const initApm = (
|
||||||
|
argv: string[],
|
||||||
|
rootDir: string,
|
||||||
|
isDistributable: boolean,
|
||||||
|
serviceName: string
|
||||||
|
) => {
|
||||||
|
const apmConfigLoader = loadConfiguration(argv, rootDir, isDistributable);
|
||||||
|
const apmConfig = apmConfigLoader.getConfig(serviceName);
|
||||||
|
|
||||||
|
// we want to only load the module when effectively used
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const apm = require('elastic-apm-node');
|
||||||
|
|
||||||
|
// Filter out all user PII
|
||||||
|
apm.addFilter((payload: Record<string, any>) => {
|
||||||
|
try {
|
||||||
|
if (payload.context?.user && typeof payload.context.user === 'object') {
|
||||||
|
Object.keys(payload.context.user).forEach((key) => {
|
||||||
|
payload.context.user[key] = '[REDACTED]';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// just silently ignore the error
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
});
|
||||||
|
|
||||||
|
apm.start(apmConfig);
|
||||||
|
};
|
52
src/apm.js
52
src/apm.js
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
||||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
||||||
* Side Public License, v 1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { join } = require('path');
|
|
||||||
const { name, build } = require('../package.json');
|
|
||||||
const { loadConfiguration } = require('@kbn/apm-config-loader');
|
|
||||||
|
|
||||||
const ROOT_DIR = join(__dirname, '..');
|
|
||||||
let apmConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to disable APM RUM support on all kibana builds by default
|
|
||||||
*/
|
|
||||||
const isKibanaDistributable = Boolean(build && build.distributable === true);
|
|
||||||
|
|
||||||
module.exports = function (serviceName = name) {
|
|
||||||
apmConfig = loadConfiguration(process.argv, ROOT_DIR, isKibanaDistributable);
|
|
||||||
const conf = apmConfig.getConfig(serviceName);
|
|
||||||
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) => {
|
|
||||||
// integration test runner starts a kibana server that import the module without initializing APM.
|
|
||||||
// so we need to check initialization of the config.
|
|
||||||
// note that we can't just load the configuration during this module's import
|
|
||||||
// because jest IT are ran with `--config path-to-jest-config.js` which conflicts with the CLI's `config` arg
|
|
||||||
// causing the config loader to try to load the jest js config as yaml and throws.
|
|
||||||
if (apmConfig) {
|
|
||||||
return apmConfig.getConfig(serviceName);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
};
|
|
18
src/cli/apm.js
Normal file
18
src/cli/apm.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { join } = require('path');
|
||||||
|
const { name, build } = require('../../package.json');
|
||||||
|
const { initApm } = require('@kbn/apm-config-loader');
|
||||||
|
|
||||||
|
const rootDir = join(__dirname, '../..');
|
||||||
|
const isKibanaDistributable = Boolean(build && build.distributable === true);
|
||||||
|
|
||||||
|
module.exports = function (serviceName = name) {
|
||||||
|
initApm(process.argv, rootDir, isKibanaDistributable, serviceName);
|
||||||
|
};
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require('../apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy');
|
require('./apm')(process.env.ELASTIC_APM_SERVICE_NAME || 'kibana-proxy');
|
||||||
require('../setup_node_env');
|
require('../setup_node_env');
|
||||||
require('../setup_node_env/root');
|
require('../setup_node_env/root');
|
||||||
require('./cli');
|
require('./cli');
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require('../apm')();
|
require('./apm')();
|
||||||
require('../setup_node_env/dist');
|
require('../setup_node_env/dist');
|
||||||
require('../setup_node_env/root');
|
require('../setup_node_env/root');
|
||||||
require('./cli');
|
require('./cli');
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const getConfigMock = jest.fn();
|
export const getConfigurationMock = jest.fn();
|
||||||
jest.doMock('../../../apm', () => ({
|
jest.doMock('@kbn/apm-config-loader', () => ({
|
||||||
getConfig: getConfigMock,
|
getConfiguration: getConfigurationMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const agentMock = {} as Record<string, any>;
|
export const agentMock = {} as Record<string, any>;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getConfigMock, agentMock } from './get_apm_config.test.mocks';
|
import { getConfigurationMock, agentMock } from './get_apm_config.test.mocks';
|
||||||
import { getApmConfig } from './get_apm_config';
|
import { getApmConfig } from './get_apm_config';
|
||||||
|
|
||||||
const defaultApmConfig = {
|
const defaultApmConfig = {
|
||||||
|
@ -16,28 +16,28 @@ const defaultApmConfig = {
|
||||||
|
|
||||||
describe('getApmConfig', () => {
|
describe('getApmConfig', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getConfigMock.mockReturnValue(defaultApmConfig);
|
getConfigurationMock.mockReturnValue(defaultApmConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
getConfigMock.mockReset();
|
getConfigurationMock.mockReset();
|
||||||
agentMock.currentTransaction = null;
|
agentMock.currentTransaction = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns null if apm is disabled', () => {
|
it('returns null if apm is disabled', () => {
|
||||||
getConfigMock.mockReturnValue({
|
getConfigurationMock.mockReturnValue({
|
||||||
active: false,
|
active: false,
|
||||||
});
|
});
|
||||||
expect(getApmConfig('/path')).toBeNull();
|
expect(getApmConfig('/path')).toBeNull();
|
||||||
|
|
||||||
getConfigMock.mockReturnValue(undefined);
|
getConfigurationMock.mockReturnValue(undefined);
|
||||||
expect(getApmConfig('/path')).toBeNull();
|
expect(getApmConfig('/path')).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls `getConfig` with the correct parameters', () => {
|
it('calls `getConfig` with the correct parameters', () => {
|
||||||
getApmConfig('/path');
|
getApmConfig('/path');
|
||||||
|
|
||||||
expect(getConfigMock).toHaveBeenCalledWith('kibana-frontend');
|
expect(getConfigurationMock).toHaveBeenCalledWith('kibana-frontend');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the configuration from the `getConfig` call', () => {
|
it('returns the configuration from the `getConfig` call', () => {
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import agent from 'elastic-apm-node';
|
import agent from 'elastic-apm-node';
|
||||||
// @ts-expect-error apm module is a js file outside of core (need to split APM/rum configuration)
|
import { getConfiguration } from '@kbn/apm-config-loader';
|
||||||
import { getConfig } from '../../../apm';
|
|
||||||
|
|
||||||
export const getApmConfig = (requestPath: string) => {
|
export const getApmConfig = (requestPath: string) => {
|
||||||
const baseConfig = getConfig('kibana-frontend');
|
const baseConfig = getConfiguration('kibana-frontend');
|
||||||
if (!baseConfig?.active) {
|
if (!baseConfig?.active) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue