Skip to content

Commit

Permalink
apm stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
tsullivan committed Mar 12, 2020
1 parent 1cf768d commit 4a5af92
Show file tree
Hide file tree
Showing 16 changed files with 196 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
*/

import { i18n } from '@kbn/i18n';
import apm from 'elastic-apm-node';
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
import { LayoutInstance } from '../../layouts/layout';
import { AttributesMap, ElementsPositionAndAttribute } from './types';
import { Logger } from '../../../../types';
import { LayoutInstance } from '../../layouts/layout';
import { CONTEXT_ELEMENTATTRIBUTES } from './constants';
import { ApmTransaction, AttributesMap, ElementsPositionAndAttribute } from './types';

export const getElementPositionAndAttributes = async (
browser: HeadlessBrowser,
layout: LayoutInstance,
logger: Logger
logger: Logger,
txn: ApmTransaction
): Promise<ElementsPositionAndAttribute[] | null> => {
const apmSpan = txn?.startSpan('get_element_position_data', 'read');
const { screenshot: screenshotSelector } = layout.selectors; // data-shared-items-container
let elementsPositionAndAttributes: ElementsPositionAndAttribute[] | null;
try {
Expand Down Expand Up @@ -66,8 +69,10 @@ export const getElementPositionAndAttributes = async (
);
}
} catch (err) {
apm.captureError(err);
elementsPositionAndAttributes = null;
}

if (apmSpan) apmSpan.end();
return elementsPositionAndAttributes;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
* you may not use this file except in compliance with the Elastic License.
*/

import apm from 'elastic-apm-node';
import { i18n } from '@kbn/i18n';
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
import { LevelLogger } from '../../../../server/lib';
import { ServerFacade } from '../../../../types';
import { LayoutInstance } from '../../layouts/layout';
import { CONTEXT_GETNUMBEROFITEMS, CONTEXT_READMETADATA } from './constants';
import { ApmTransaction } from './types';

export const getNumberOfItems = async (
server: ServerFacade,
browser: HeadlessBrowser,
layout: LayoutInstance,
logger: LevelLogger
logger: LevelLogger,
txn: ApmTransaction | null
): Promise<number> => {
const apmSpan = txn?.startSpan('get_number_of_items', 'read');
const config = server.config();
const { renderComplete: renderCompleteSelector, itemsCountAttribute } = layout.selectors;
let itemsCount: number;
Expand Down Expand Up @@ -59,6 +63,7 @@ export const getNumberOfItems = async (
logger
);
} catch (err) {
apm.captureError(err);
throw new Error(
i18n.translate('xpack.reporting.screencapture.readVisualizationsError', {
defaultMessage: `An error occurred when trying to read the page for visualization panel info. You may need to increase '{configKey}'. {error}`,
Expand All @@ -71,5 +76,6 @@ export const getNumberOfItems = async (
itemsCount = 1;
}

if (apmSpan) apmSpan.end();
return itemsCount;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,34 @@
import { i18n } from '@kbn/i18n';
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
import { LevelLogger } from '../../../../server/lib';
import { Screenshot, ElementsPositionAndAttribute } from './types';

const getAsyncDurationLogger = (logger: LevelLogger) => {
return async (description: string, promise: Promise<any>) => {
const start = Date.now();
const result = await promise;
logger.debug(
i18n.translate('xpack.reporting.screencapture.asyncTook', {
defaultMessage: '{description} took {took}ms',
values: {
description,
took: Date.now() - start,
},
})
);
return result;
};
};
import { ApmTransaction, Screenshot, ElementsPositionAndAttribute } from './types';

export const getScreenshots = async (
browser: HeadlessBrowser,
elementsPositionAndAttributes: ElementsPositionAndAttribute[],
logger: LevelLogger
logger: LevelLogger,
txn: ApmTransaction
): Promise<Screenshot[]> => {
logger.info(
i18n.translate('xpack.reporting.screencapture.takingScreenshots', {
defaultMessage: `taking screenshots`,
})
);

const asyncDurationLogger = getAsyncDurationLogger(logger);
const screenshots: Screenshot[] = [];

for (let i = 0; i < elementsPositionAndAttributes.length; i++) {
const apmSpan = txn?.startSpan('get_screenshots', 'read');
const item = elementsPositionAndAttributes[i];
const base64EncodedData = await asyncDurationLogger(
`screenshot #${i + 1}`,
browser.screenshot(item.position)
);
const base64EncodedData = (await browser.screenshot(item.position)).toString('base64');

screenshots.push({
base64EncodedData,
title: item.attributes.title,
description: item.attributes.description,
});

if (apmSpan) apmSpan.end();
}

logger.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import { LevelLogger } from '../../../../server/lib';
import { LayoutInstance } from '../../layouts/layout';
import { CONTEXT_GETTIMERANGE } from './constants';
import { TimeRange } from './types';
import { ApmTransaction } from './types';

export const getTimeRange = async (
browser: HeadlessBrowser,
layout: LayoutInstance,
logger: LevelLogger
logger: LevelLogger,
txn: ApmTransaction
): Promise<TimeRange | null> => {
const apmSpan = txn?.startSpan('get_time_range', 'read');
logger.debug('getting timeRange');

const timeRange: TimeRange | null = await browser.evaluate(
Expand Down Expand Up @@ -45,5 +48,6 @@ export const getTimeRange = async (
logger.debug('no timeRange');
}

if (apmSpan) apmSpan.end();
return timeRange;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@
* you may not use this file except in compliance with the Elastic License.
*/

import apm from 'elastic-apm-node';
import { i18n } from '@kbn/i18n';
import fs from 'fs';
import { promisify } from 'util';
import { LevelLogger } from '../../../../server/lib';
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
import { Layout } from '../../layouts/layout';
import { CONTEXT_INJECTCSS } from './constants';
import { ApmTransaction } from './types';

const fsp = { readFile: promisify(fs.readFile) };

export const injectCustomCss = async (
browser: HeadlessBrowser,
layout: Layout,
logger: LevelLogger
logger: LevelLogger,
txn: ApmTransaction
): Promise<void> => {
const apmSpan = txn?.startSpan('inject_css', 'correction');
logger.debug(
i18n.translate('xpack.reporting.screencapture.injectingCss', {
defaultMessage: 'injecting custom css',
Expand All @@ -42,11 +46,14 @@ export const injectCustomCss = async (
logger
);
} catch (err) {
apm.captureError(err);
throw new Error(
i18n.translate('xpack.reporting.screencapture.injectCss', {
defaultMessage: `An error occurred when trying to update Kibana CSS for reporting. {error}`,
values: { error: err },
})
);
}

if (apmSpan) apmSpan.end();
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/

import apm from 'elastic-apm-node';
import * as Rx from 'rxjs';
import { catchError, concatMap, first, mergeMap, take, takeUntil, toArray } from 'rxjs/operators';
import {
catchError,
concatMap,
first,
mergeMap,
take,
takeUntil,
tap,
toArray,
} from 'rxjs/operators';
import { CaptureConfig, HeadlessChromiumDriverFactory, ServerFacade } from '../../../../types';
import { getElementPositionAndAttributes } from './get_element_position_data';
import { getNumberOfItems } from './get_number_of_items';
import { getScreenshots } from './get_screenshots';
import { getTimeRange } from './get_time_range';
import { injectCustomCss } from './inject_css';
import { openUrl } from './open_url';
import { skipTelemetry } from './skip_telemetry';
import { ScreenSetupData, ScreenshotObservableOpts, ScreenshotResults } from './types';
import { waitForRenderComplete } from './wait_for_render';
import { waitForVisualizations } from './wait_for_visualizations';
import { injectCustomCss } from './inject_css';

export function screenshotsObservableFactory(
server: ServerFacade,
Expand All @@ -32,48 +42,57 @@ export function screenshotsObservableFactory(
layout,
browserTimezone,
}: ScreenshotObservableOpts): Rx.Observable<ScreenshotResults[]> {
const txn = apm.startTransaction(`reporting screenshot pipeline`, 'reporting');

const apmCreatePage = txn?.startSpan('create_page', 'wait');
const create$ = browserDriverFactory.createPage(
{ viewport: layout.getBrowserViewport(), browserTimezone },
logger
);

return Rx.from(urls).pipe(
concatMap(url => {
return create$.pipe(
mergeMap(({ driver, exit$ }) => {
if (apmCreatePage) apmCreatePage.end();

const setup$: Rx.Observable<ScreenSetupData> = Rx.of(1).pipe(
takeUntil(exit$),
mergeMap(() => openUrl(server, driver, url, conditionalHeaders, logger)),
mergeMap(() => skipTelemetry(driver, logger)),
mergeMap(() => getNumberOfItems(server, driver, layout, logger)),
mergeMap(() => openUrl(server, driver, url, conditionalHeaders, logger, txn)),
mergeMap(() => skipTelemetry(driver, logger, txn)),
mergeMap(() => getNumberOfItems(server, driver, layout, logger, txn)),
mergeMap(async itemsCount => {
const viewport = layout.getViewport(itemsCount);
await Promise.all([
driver.setViewport(viewport, logger),
waitForVisualizations(server, driver, itemsCount, layout, logger),
waitForVisualizations(server, driver, itemsCount, layout, logger, txn),
]);
}),
mergeMap(async () => {
// Waiting till _after_ elements have rendered before injecting our CSS
// allows for them to be displayed properly in many cases
await injectCustomCss(driver, layout, logger);
await injectCustomCss(driver, layout, logger, txn);

const apmPositionElements = txn?.startSpan('position_elements', 'correction');
if (layout.positionElements) {
// position panel elements for print layout
await layout.positionElements(driver, logger);
}
if (apmPositionElements) apmPositionElements.end();

await waitForRenderComplete(driver, layout, captureConfig, logger);
await waitForRenderComplete(driver, layout, captureConfig, logger, txn);
}),
mergeMap(async () => {
return await Promise.all([
getTimeRange(driver, layout, logger),
getElementPositionAndAttributes(driver, layout, logger),
getTimeRange(driver, layout, logger, txn),
getElementPositionAndAttributes(driver, layout, logger, txn),
]).then(([timeRange, elementsPositionAndAttributes]) => ({
elementsPositionAndAttributes,
timeRange,
}));
}),
catchError(err => {
apm.captureError(err);
logger.error(err);
return Rx.of({ elementsPositionAndAttributes: null, timeRange: null, error: err });
})
Expand All @@ -85,7 +104,7 @@ export function screenshotsObservableFactory(
const elements = data.elementsPositionAndAttributes
? data.elementsPositionAndAttributes
: getDefaultElementPosition(layout.getViewport(1));
const screenshots = await getScreenshots(driver, elements, logger);
const screenshots = await getScreenshots(driver, elements, logger, txn);
const { timeRange, error: setupError } = data;
return { timeRange, screenshots, error: setupError };
}
Expand All @@ -96,7 +115,10 @@ export function screenshotsObservableFactory(
);
}),
take(urls.length),
toArray()
toArray(),
tap(() => {
if (txn) txn.end();
})
);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@
*/

import { i18n } from '@kbn/i18n';
import { ConditionalHeaders, ServerFacade } from '../../../../types';
import { LevelLogger } from '../../../../server/lib';
import apm from 'elastic-apm-node';
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers';
import { LevelLogger } from '../../../../server/lib';
import { ConditionalHeaders, ServerFacade } from '../../../../types';
import { PAGELOAD_SELECTOR } from '../../constants';
import { ApmTransaction } from './types';

export const openUrl = async (
server: ServerFacade,
browser: HeadlessBrowser,
url: string,
conditionalHeaders: ConditionalHeaders,
logger: LevelLogger
logger: LevelLogger,
txn: ApmTransaction
): Promise<void> => {
const apmSpan = txn?.startSpan('open_url', 'wait');
const config = server.config();

try {
await browser.open(
url,
Expand All @@ -30,14 +33,13 @@ export const openUrl = async (
logger
);
} catch (err) {
apm.captureError(err);
throw new Error(
i18n.translate('xpack.reporting.screencapture.couldntLoadKibana', {
defaultMessage: `An error occurred when trying to open the Kibana URL. You may need to increase '{configKey}'. {error}`,
values: {
configKey: 'xpack.reporting.capture.timeouts.openUrl',
error: err,
},
values: { configKey: 'xpack.reporting.capture.timeouts.openUrl', error: err },
})
);
}
if (apmSpan) apmSpan.end();
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
import { HeadlessChromiumDriver as HeadlessBrowser } from '../../../../server/browsers/chromium/driver';
import { LevelLogger } from '../../../../server/lib';
import { CONTEXT_SKIPTELEMETRY } from './constants';
import { ApmTransaction } from './types';

const LAST_REPORT_STORAGE_KEY = 'xpack.data';

export async function skipTelemetry(browser: HeadlessBrowser, logger: LevelLogger) {
export async function skipTelemetry(
browser: HeadlessBrowser,
logger: LevelLogger,
txn: ApmTransaction
) {
const apmSpan = txn?.startSpan('skip_telemetry', 'correction');
const storageData = await browser.evaluate(
{
fn: storageKey => {
Expand All @@ -31,4 +37,6 @@ export async function skipTelemetry(browser: HeadlessBrowser, logger: LevelLogge
);

logger.debug(`added data to localStorage to skip telmetry: ${storageData}`);

if (apmSpan) apmSpan.end();
}
Loading

0 comments on commit 4a5af92

Please sign in to comment.