Skip to content

Commit

Permalink
Merge branch 'main' into issue-99581-flaky-test
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine committed Nov 3, 2021
2 parents 1346bbc + 6c4373f commit 40fa5b7
Show file tree
Hide file tree
Showing 183 changed files with 2,612 additions and 2,546 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
/src/core/ @elastic/kibana-core
/src/plugins/saved_objects_tagging_oss @elastic/kibana-core
/config/kibana.yml @elastic/kibana-core
/typings/ @elastic/kibana-core
/x-pack/plugins/banners/ @elastic/kibana-core
/x-pack/plugins/features/ @elastic/kibana-core
/x-pack/plugins/licensing/ @elastic/kibana-core
Expand Down
2 changes: 1 addition & 1 deletion docs/CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ The default values for the session timeout `xpack.security.session.{lifespan|idl
*Impact* +
Use the following default values:
* `xpack.security.session.idleTimeout: 1h`
* `xpack.security.session.idleTimeout: 8h`
* `xpack.security.session.lifespan: 30d`
====

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace",
"@elastic/apm-rum": "^5.9.1",
"@elastic/apm-rum-react": "^1.3.1",
"@elastic/charts": "38.1.0",
"@elastic/charts": "38.1.3",
"@elastic/datemath": "link:bazel-bin/packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.35",
"@elastic/ems-client": "8.0.0",
Expand Down
47 changes: 47 additions & 0 deletions src/core/public/apm_resource_counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 class CachedResourceObserver {
private loaded = {
networkOrDisk: 0,
memory: 0,
};
private observer?: PerformanceObserver;

constructor() {
if (!window.PerformanceObserver) return;

const cb = (entries: PerformanceObserverEntryList) => {
const e = entries.getEntries();
e.forEach((entry: Record<string, any>) => {
if (entry.initiatorType === 'script' || entry.initiatorType === 'link') {
// If the resource is fetched from a local cache, or if it is a cross-origin resource, this property returns zero.
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/transferSize
if (entry.name.indexOf(window.location.host) > -1 && entry.transferSize === 0) {
this.loaded.memory++;
} else {
this.loaded.networkOrDisk++;
}
}
});
};
this.observer = new PerformanceObserver(cb);
this.observer.observe({
type: 'resource',
buffered: true,
});
}

public getCounts() {
return this.loaded;
}

public destroy() {
this.observer?.disconnect();
}
}
117 changes: 116 additions & 1 deletion src/core/public/apm_system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
*/

jest.mock('@elastic/apm-rum');
import type { DeeplyMockedKeys } from '@kbn/utility-types/jest';
import type { DeeplyMockedKeys, MockedKeys } from '@kbn/utility-types/jest';
import { init, apm } from '@elastic/apm-rum';
import { ApmSystem } from './apm_system';
import { Subject } from 'rxjs';
import { InternalApplicationStart } from './application/types';

const initMock = init as jest.Mocked<typeof init>;
const apmMock = apm as DeeplyMockedKeys<typeof apm>;
Expand Down Expand Up @@ -39,6 +41,119 @@ describe('ApmSystem', () => {
expect(apm.addLabels).toHaveBeenCalledWith({ alpha: 'one' });
});

describe('manages the page load transaction', () => {
it('does nothing if theres no transaction', async () => {
const apmSystem = new ApmSystem({ active: true });
const mockTransaction: MockedKeys<Transaction> = {
type: 'wrong',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
expect(mockTransaction.mark).not.toHaveBeenCalled();
// @ts-expect-error 2345
expect(mockTransaction.block).not.toHaveBeenCalled();
});

it('blocks a page load transaction', async () => {
const apmSystem = new ApmSystem({ active: true });
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
expect(mockTransaction.mark).toHaveBeenCalledTimes(1);
expect(mockTransaction.mark).toHaveBeenCalledWith('apm-setup');
// @ts-expect-error 2345
expect(mockTransaction.block).toHaveBeenCalledTimes(1);
});

it('marks apm start', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mark = jest.fn();
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
mark,
// @ts-expect-error 2345
block: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};

apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();

mark.mockReset();

await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});

expect(mark).toHaveBeenCalledWith('apm-start');
});

it('closes the page load transaction once', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mockTransaction: MockedKeys<Transaction> = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});
currentAppId$.next('myapp');

expect(mockTransaction.end).toHaveBeenCalledTimes(1);

currentAppId$.next('another-app');

expect(mockTransaction.end).toHaveBeenCalledTimes(1);
});

it('adds resource load labels', async () => {
const apmSystem = new ApmSystem({ active: true });
const currentAppId$ = new Subject<string>();
const mockTransaction: Transaction = {
type: 'page-load',
// @ts-expect-error 2345
block: jest.fn(),
mark: jest.fn(),
end: jest.fn(),
addLabels: jest.fn(),
};
apmMock.getCurrentTransaction.mockReturnValue(mockTransaction);
await apmSystem.setup();
await apmSystem.start({
application: {
currentAppId$,
} as any as InternalApplicationStart,
});
currentAppId$.next('myapp');

expect(mockTransaction.addLabels).toHaveBeenCalledWith({
'loaded-resources': 0,
'cached-resources': 0,
});
});
});

describe('http request normalization', () => {
let windowSpy: any;

Expand Down
63 changes: 44 additions & 19 deletions src/core/public/apm_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import type { ApmBase, AgentConfigOptions } from '@elastic/apm-rum';
import { modifyUrl } from '@kbn/std';
import { CachedResourceObserver } from './apm_resource_counter';
import type { InternalApplicationStart } from './application';

/** "GET protocol://hostname:port/pathname" */
Expand All @@ -31,17 +32,21 @@ interface StartDeps {
export class ApmSystem {
private readonly enabled: boolean;
private pageLoadTransaction?: Transaction;
private resourceObserver: CachedResourceObserver;
private apm?: ApmBase;
/**
* `apmConfig` would be populated with relevant APM RUM agent
* configuration if server is started with elastic.apm.* config.
*/
constructor(private readonly apmConfig?: ApmConfig, private readonly basePath = '') {
this.enabled = apmConfig != null && !!apmConfig.active;
this.resourceObserver = new CachedResourceObserver();
}

async setup() {
if (!this.enabled) return;
const { init, apm } = await import('@elastic/apm-rum');
this.apm = apm;
const { globalLabels, ...apmConfig } = this.apmConfig!;
if (globalLabels) {
apm.addLabels(globalLabels);
Expand All @@ -50,43 +55,63 @@ export class ApmSystem {
this.addHttpRequestNormalization(apm);

init(apmConfig);
this.pageLoadTransaction = apm.getCurrentTransaction();

// Keep the page load transaction open until all resources finished loading
if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
// @ts-expect-error 2339
this.pageLoadTransaction.block(true);
this.pageLoadTransaction.mark('apm-setup');
}
// hold page load transaction blocks a transaction implicitly created by init.
this.holdPageLoadTransaction(apm);
}

async start(start?: StartDeps) {
if (!this.enabled || !start) return;

if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
this.pageLoadTransaction.mark('apm-start');
}
this.markPageLoadStart();

/**
* Register listeners for navigation changes and capture them as
* route-change transactions after Kibana app is bootstrapped
*/
start.application.currentAppId$.subscribe((appId) => {
const apmInstance = (window as any).elasticApm;
if (appId && apmInstance && typeof apmInstance.startTransaction === 'function') {
// Close the page load transaction
if (this.pageLoadTransaction && this.pageLoadTransaction.type === 'page-load') {
this.pageLoadTransaction.end();
this.pageLoadTransaction = undefined;
}
apmInstance.startTransaction(`/app/${appId}`, 'route-change', {
if (appId && this.apm) {
this.closePageLoadTransaction();
this.apm.startTransaction(`/app/${appId}`, 'route-change', {
managed: true,
canReuse: true,
});
}
});
}

/* Hold the page load transaction open, until all resources actually finish loading */
private holdPageLoadTransaction(apm: ApmBase) {
const transaction = apm.getCurrentTransaction();

// Keep the page load transaction open until all resources finished loading
if (transaction && transaction.type === 'page-load') {
this.pageLoadTransaction = transaction;
// @ts-expect-error 2339 block is a private property of Transaction interface
this.pageLoadTransaction.block(true);
this.pageLoadTransaction.mark('apm-setup');
}
}

/* Close and clear the page load transaction */
private closePageLoadTransaction() {
if (this.pageLoadTransaction) {
const loadCounts = this.resourceObserver.getCounts();
this.pageLoadTransaction.addLabels({
'loaded-resources': loadCounts.networkOrDisk,
'cached-resources': loadCounts.memory,
});
this.resourceObserver.destroy();
this.pageLoadTransaction.end();
this.pageLoadTransaction = undefined;
}
}

private markPageLoadStart() {
if (this.pageLoadTransaction) {
this.pageLoadTransaction.mark('apm-start');
}
}

/**
* Adds an observer to the APM configuration for normalizing transactions of the 'http-request' type to remove the
* hostname, protocol, port, and base path. Allows for coorelating data cross different deployments.
Expand Down
1 change: 1 addition & 0 deletions src/plugins/charts/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
ColorMode,
LabelRotation,
defaultCountLabel,
MULTILAYER_TIME_AXIS_STYLE,
} from './static';

export { ColorSchemaParams, Labels, Style } from './types';
2 changes: 2 additions & 0 deletions src/plugins/charts/common/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export {
} from './color_maps';

export { ColorMode, LabelRotation, defaultCountLabel } from './components';

export * from './styles';
9 changes: 9 additions & 0 deletions src/plugins/charts/common/static/styles/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 * from './multilayer_timeaxis';
26 changes: 26 additions & 0 deletions src/plugins/charts/common/static/styles/multilayer_timeaxis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { Position, RecursivePartial, AxisStyle } from '@elastic/charts';

export const MULTILAYER_TIME_AXIS_STYLE: RecursivePartial<AxisStyle> = {
tickLabel: {
visible: true,
padding: 0,
rotation: 0,
alignment: {
vertical: Position.Bottom,
horizontal: Position.Left,
},
},
tickLine: {
visible: true,
size: 0.0001,
padding: 4,
},
};
1 change: 1 addition & 0 deletions src/plugins/charts/public/static/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
export * from './colors';
export * from './components';
export * from './utils';
export * from '../../common/static/styles';
Loading

0 comments on commit 40fa5b7

Please sign in to comment.