Skip to content

Commit 8b16699

Browse files
author
Liza Katz
authored
[7.17] Debugging with apm - fixes and tutorial (#127892) (#129600)
* Debugging with apm - fixes and tutorial (#127892) * fixes + tutorial * cors config * omit secretToken so its not sent to FE Add config tests * lint * empty * swallow errors when parsing configs * read config test adjustment * apm docs review * new line * doc review Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit c97bfc8) # Conflicts: # packages/kbn-apm-config-loader/src/config.test.ts # packages/kbn-apm-config-loader/src/config.ts # src/core/server/http_resources/get_apm_config.ts * fix merge
1 parent c9c18f0 commit 8b16699

File tree

12 files changed

+182
-117
lines changed

12 files changed

+182
-117
lines changed

dev_docs/tutorials/apm_ui.png

43.1 KB
Loading

dev_docs/tutorials/debugging.mdx

+46
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,49 @@ logging:
6161
- name: elasticsearch.query
6262
level: debug
6363
```
64+
65+
## Debugging Kibana with APM
66+
67+
Kibana is integrated with APM's node and RUM agents.
68+
To learn more about how APM works and what it reports, refer to the [documentation](https://www.elastic.co/guide/en/apm/guide/current/index.html).
69+
70+
We currently track the following types of transactions from Kibana:
71+
72+
Frontend (APM RUM):
73+
* `http-request`- tracks all outgoing API requests
74+
* `page-load` - tracks the inidial loading time of kibana
75+
* `app-change` - tracks application changes
76+
77+
Backend (APM Node):
78+
* `request` - tracks all incoming API requests
79+
* `kibana-platform` - tracks server initiation phases (preboot, setup and start)
80+
* `task-manager` - tracks the operation of the task manager, including claiming pending tasks and marking them as running
81+
* `task-run` - tracks the execution of individual tasks
82+
83+
### Enabling APM on a local environment
84+
85+
In some cases, it is beneficial to enable APM on a local development environment to get an initial undesrtanding of a feature's performance during manual or automatic tests.
86+
87+
1. Create a secondary monitoring deployment to collect APM data. The easiest option is to use [Elastic Cloud](https://cloud.elastic.co/deployments) to create a new deployment.
88+
2. Open Kibana, go to `Integrations` and enable the Elastic APM integration.
89+
3. Scroll down and copy the server URL and secret token. You may also find them in your cloud console under APM & Fleet.
90+
4. Create or open `config\kibana.dev.yml` on your local development environment.
91+
5. Add the following settings:
92+
```
93+
elastic.apm.active: true
94+
elastic.apm.serverUrl: <serverUrl>
95+
elastic.apm.secretToken: <secretToken>
96+
```
97+
6. Once you run kibana and start using it, two new services (kibana, kibana-frontend) should appear under the APM UI on the APM deployment.
98+
![APM UI](./apm_ui.png)
99+
100+
### Enabling APM via environment variables
101+
102+
It is possible to enable APM via environment variables as well.
103+
They take precedence over any values defined in `kibana.yml` or `kibana.dev.yml`
104+
105+
Set the following environment variables to enable APM:
106+
107+
* ELASTIC_APM_ACTIVE
108+
* ELASTIC_APM_SERVER_URL
109+
* ELASTIC_APM_SECRET_TOKEN

packages/kbn-apm-config-loader/src/config.test.mocks.ts

-8
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,6 @@ export const packageMock = {
1717
};
1818
jest.doMock(join(mockedRootDir, 'package.json'), () => packageMock.raw, { virtual: true });
1919

20-
export const devConfigMock = {
21-
raw: {} as any,
22-
};
23-
jest.doMock(join(mockedRootDir, 'config', 'apm.dev.js'), () => devConfigMock.raw, {
24-
virtual: true,
25-
});
26-
2720
export const gitRevExecMock = jest.fn();
2821
jest.doMock('child_process', () => ({
2922
...childProcessModule,
@@ -48,7 +41,6 @@ jest.doMock('fs', () => ({
4841

4942
export const resetAllMocks = () => {
5043
packageMock.raw = {};
51-
devConfigMock.raw = {};
5244
gitRevExecMock.mockReset();
5345
readUuidFileMock.mockReset();
5446
jest.resetModules();

packages/kbn-apm-config-loader/src/config.test.ts

+54-81
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,21 @@
55
* in compliance with, at your election, the Elastic License 2.0 or the Server
66
* Side Public License, v 1.
77
*/
8-
import type { Labels } from 'elastic-apm-node';
8+
import type { AgentConfigOptions, Labels } from 'elastic-apm-node';
99
import {
1010
packageMock,
1111
mockedRootDir,
1212
gitRevExecMock,
13-
devConfigMock,
1413
readUuidFileMock,
1514
resetAllMocks,
1615
} from './config.test.mocks';
1716

18-
import { ApmConfiguration } from './config';
17+
import { ApmConfiguration, CENTRALIZED_SERVICE_BASE_CONFIG } from './config';
1918

2019
describe('ApmConfiguration', () => {
2120
beforeEach(() => {
2221
// start with an empty env to avoid CI from spoiling snapshots, env is unique for each jest file
2322
process.env = {};
24-
2523
packageMock.raw = {
2624
version: '8.0.0',
2725
build: {
@@ -148,82 +146,57 @@ describe('ApmConfiguration', () => {
148146
);
149147
});
150148

151-
it('loads the configuration from the dev config is present', () => {
152-
devConfigMock.raw = {
153-
active: true,
154-
serverUrl: 'https://dev-url.co',
155-
};
156-
const config = new ApmConfiguration(mockedRootDir, {}, false);
157-
expect(config.getConfig('serviceName')).toEqual(
158-
expect.objectContaining({
159-
active: true,
160-
serverUrl: 'https://dev-url.co',
161-
})
162-
);
163-
});
164-
165-
it('does not load the configuration from the dev config in distributable', () => {
166-
devConfigMock.raw = {
167-
active: true,
168-
serverUrl: 'https://dev-url.co',
169-
};
170-
const config = new ApmConfiguration(mockedRootDir, {}, true);
171-
expect(config.getConfig('serviceName')).toEqual(
172-
expect.objectContaining({
173-
active: false,
174-
})
175-
);
176-
});
177-
178-
it('overwrites the standard config file with the dev config', () => {
179-
const kibanaConfig = {
180-
elastic: {
181-
apm: {
182-
active: true,
183-
serverUrl: 'https://url',
184-
secretToken: 'secret',
185-
},
186-
},
187-
};
188-
devConfigMock.raw = {
189-
active: true,
190-
serverUrl: 'https://dev-url.co',
191-
};
192-
const config = new ApmConfiguration(mockedRootDir, kibanaConfig, false);
193-
expect(config.getConfig('serviceName')).toEqual(
194-
expect.objectContaining({
195-
active: true,
196-
serverUrl: 'https://dev-url.co',
197-
secretToken: 'secret',
198-
})
199-
);
200-
});
201-
202-
it('correctly sets environment by reading env vars', () => {
203-
delete process.env.ELASTIC_APM_ENVIRONMENT;
204-
delete process.env.NODE_ENV;
205-
206-
let config = new ApmConfiguration(mockedRootDir, {}, false);
207-
expect(config.getConfig('serviceName')).toEqual(
208-
expect.objectContaining({
209-
environment: 'development',
210-
})
211-
);
212-
213-
process.env.NODE_ENV = 'production';
214-
config = new ApmConfiguration(mockedRootDir, {}, false);
215-
expect(config.getConfig('serviceName')).toEqual(
216-
expect.objectContaining({
217-
environment: 'production',
218-
})
219-
);
220-
221-
process.env.ELASTIC_APM_ENVIRONMENT = 'ci';
222-
config = new ApmConfiguration(mockedRootDir, {}, false);
223-
expect(config.getConfig('serviceName')).toEqual(
224-
expect.objectContaining({
225-
environment: 'ci',
226-
})
227-
);
149+
describe('env vars', () => {
150+
beforeEach(() => {
151+
delete process.env.ELASTIC_APM_ENVIRONMENT;
152+
delete process.env.ELASTIC_APM_SECRET_TOKEN;
153+
delete process.env.ELASTIC_APM_SERVER_URL;
154+
delete process.env.NODE_ENV;
155+
});
156+
157+
it('correctly sets environment by reading env vars', () => {
158+
let config = new ApmConfiguration(mockedRootDir, {}, false);
159+
expect(config.getConfig('serviceName')).toEqual(
160+
expect.objectContaining({
161+
environment: 'development',
162+
})
163+
);
164+
165+
process.env.NODE_ENV = 'production';
166+
config = new ApmConfiguration(mockedRootDir, {}, false);
167+
expect(config.getConfig('serviceName')).toEqual(
168+
expect.objectContaining({
169+
environment: 'production',
170+
})
171+
);
172+
173+
process.env.ELASTIC_APM_ENVIRONMENT = 'ci';
174+
config = new ApmConfiguration(mockedRootDir, {}, false);
175+
expect(config.getConfig('serviceName')).toEqual(
176+
expect.objectContaining({
177+
environment: 'ci',
178+
})
179+
);
180+
});
181+
182+
it('uses default config if serverUrl is not set', () => {
183+
process.env.ELASTIC_APM_SECRET_TOKEN = 'banana';
184+
const config = new ApmConfiguration(mockedRootDir, {}, false);
185+
const serverConfig = config.getConfig('serviceName');
186+
expect(serverConfig).toHaveProperty(
187+
'secretToken',
188+
(CENTRALIZED_SERVICE_BASE_CONFIG as AgentConfigOptions).secretToken
189+
);
190+
expect(serverConfig).toHaveProperty('serverUrl', CENTRALIZED_SERVICE_BASE_CONFIG.serverUrl);
191+
});
192+
193+
it('uses env vars config if serverUrl is set', () => {
194+
process.env.ELASTIC_APM_SECRET_TOKEN = 'banana';
195+
process.env.ELASTIC_APM_SERVER_URL = 'http://banana.com/';
196+
const config = new ApmConfiguration(mockedRootDir, {}, false);
197+
const serverConfig = config.getConfig('serviceName');
198+
expect(serverConfig).toHaveProperty('secretToken', process.env.ELASTIC_APM_SECRET_TOKEN);
199+
expect(serverConfig).toHaveProperty('serverUrl', process.env.ELASTIC_APM_SERVER_URL);
200+
});
228201
});
229202
});

packages/kbn-apm-config-loader/src/config.ts

+15-21
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const DEFAULT_CONFIG: AgentConfigOptions = {
2323
globalLabels: {},
2424
};
2525

26-
const CENTRALIZED_SERVICE_BASE_CONFIG: AgentConfigOptions | RUMAgentConfigOptions = {
26+
export const CENTRALIZED_SERVICE_BASE_CONFIG: AgentConfigOptions | RUMAgentConfigOptions = {
2727
serverUrl: 'https://kibana-cloud-apm.apm.us-east-1.aws.found.io',
2828

2929
// The secretToken below is intended to be hardcoded in this file even though
@@ -74,6 +74,8 @@ export class ApmConfiguration {
7474

7575
private getBaseConfig() {
7676
if (!this.baseConfig) {
77+
const configFromSources = this.getConfigFromAllSources();
78+
7779
this.baseConfig = merge(
7880
{
7981
serviceVersion: this.kibanaVersion,
@@ -82,9 +84,7 @@ export class ApmConfiguration {
8284
this.getUuidConfig(),
8385
this.getGitConfig(),
8486
this.getCiConfig(),
85-
this.getConfigFromKibanaConfig(),
86-
this.getDevConfig(),
87-
this.getConfigFromEnv()
87+
configFromSources
8888
);
8989

9090
/**
@@ -129,6 +129,10 @@ export class ApmConfiguration {
129129
config.serverUrl = process.env.ELASTIC_APM_SERVER_URL;
130130
}
131131

132+
if (process.env.ELASTIC_APM_SECRET_TOKEN) {
133+
config.secretToken = process.env.ELASTIC_APM_SECRET_TOKEN;
134+
}
135+
132136
if (process.env.ELASTIC_APM_GLOBAL_LABELS) {
133137
config.globalLabels = Object.fromEntries(
134138
process.env.ELASTIC_APM_GLOBAL_LABELS.split(',').map((p) => {
@@ -149,23 +153,6 @@ export class ApmConfiguration {
149153
return this.rawKibanaConfig?.elastic?.apm ?? {};
150154
}
151155

152-
/**
153-
* Get the configuration from the apm.dev.js file, supersedes config
154-
* from the --config file, disabled when running the distributable
155-
*/
156-
private getDevConfig(): AgentConfigOptions {
157-
if (this.isDistributable) {
158-
return {};
159-
}
160-
161-
try {
162-
const apmDevConfigPath = join(this.rootDir, 'config', 'apm.dev.js');
163-
return require(apmDevConfigPath);
164-
} catch (e) {
165-
return {};
166-
}
167-
}
168-
169156
/**
170157
* Determine the Kibana UUID, initialized the value of `globalLabels.kibana_uuid`
171158
* when the UUID can be determined.
@@ -254,4 +241,11 @@ export class ApmConfiguration {
254241
return {};
255242
}
256243
}
244+
245+
/**
246+
* Reads APM configuration from different sources and merges them together.
247+
*/
248+
private getConfigFromAllSources(): AgentConfigOptions {
249+
return merge({}, this.getConfigFromKibanaConfig(), this.getConfigFromEnv());
250+
}
257251
}

packages/kbn-apm-config-loader/src/utils/__snapshots__/read_config.test.ts.snap

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/kbn-apm-config-loader/src/utils/get_config_file_paths.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ describe('getConfigurationFilePaths', () => {
2323
});
2424

2525
it('fallbacks to `getConfigPath` value', () => {
26-
expect(getConfigurationFilePaths([])).toEqual([getConfigPath()]);
26+
const path = getConfigPath();
27+
expect(getConfigurationFilePaths([])).toEqual([
28+
path,
29+
path.replace('kibana.yml', 'kibana.dev.yml'),
30+
]);
2731
});
2832
});

packages/kbn-apm-config-loader/src/utils/get_config_file_paths.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@ export const getConfigurationFilePaths = (argv: string[]): string[] => {
2222
if (rawPaths.length) {
2323
return rawPaths.map((path) => resolve(process.cwd(), path));
2424
}
25-
return [getConfigPath()];
25+
26+
const configPath = getConfigPath();
27+
28+
// Pick up settings from dev.yml as well
29+
return [configPath, configPath.replace('kibana.yml', 'kibana.dev.yml')];
2630
};

packages/kbn-apm-config-loader/src/utils/read_config.test.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ test('reads and merges multiple yaml files from file system and parses to json',
2929
expect(config).toMatchSnapshot();
3030
});
3131

32+
test('reads yaml files from file system and parses to json, even if one is missing', () => {
33+
const config = getConfigFromFiles([fixtureFile('one.yml'), fixtureFile('boo.yml')]);
34+
35+
expect(config).toMatchSnapshot();
36+
});
37+
3238
test('should inject an environment variable value when setting a value with ${ENV_VAR}', () => {
3339
process.env.KBN_ENV_VAR1 = 'val1';
3440
process.env.KBN_ENV_VAR2 = 'val2';
@@ -61,8 +67,9 @@ describe('different cwd()', () => {
6167
expect(config).toMatchSnapshot();
6268
});
6369

64-
test('fails to load relative paths, not found because of the cwd', () => {
70+
test('ignores errors loading relative paths', () => {
6571
const relativePath = relative(resolve(__dirname, '..', '..'), fixtureFile('one.yml'));
66-
expect(() => getConfigFromFiles([relativePath])).toThrowError(/ENOENT/);
72+
const config = getConfigFromFiles([relativePath]);
73+
expect(config).toStrictEqual({});
6774
});
6875
});

0 commit comments

Comments
 (0)