mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[kbn-journeys] add synthtrace support (#178599)
## Summary Moving synthtrace clients init inside kbn-journeys: esArchiver does not always solve the issue with data generation. We already have afew journeys using Synthtrace instead and expect more to come. In order to simplify the process of creating new journeys, this PR moves Synthtrace client initialisation into kbn-journey package and exposes a way to define client type, generator function & its input arguments: ``` import { Journey, SynthtraceOptions } from '@kbn/journeys'; import { subj } from '@kbn/test-subj-selector'; import { generateApmData } from '../synthtrace_data/apm_data'; export const journey = new Journey({ synthtrace: { type: 'apm', generator: generateApmData, options: { from: new Date(Date.now() - 1000 * 60 * 15), to: new Date(Date.now() + 1000 * 60 * 15), }, }, }) ``` PR also needs review from teams who use Synthtrace to understand if the implementation is matching expectations. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
45dbfae666
commit
aa45c2aee3
13 changed files with 297 additions and 171 deletions
|
@ -8,6 +8,7 @@ tags: ['kibana', 'onboarding', 'setup', 'performance', 'development']
|
|||
---
|
||||
|
||||
## Overview
|
||||
|
||||
In order to achieve our goal of creating best user experience in Kibana, it is important to keep track on its features performance.
|
||||
To make things easier, we introduced performance journeys, that mimics end-user experience with Kibana.
|
||||
|
||||
|
@ -19,6 +20,7 @@ Journeys core is [kbn-journeys](packages/kbn-journeys/README.mdx) package. It is
|
|||
by [Playwright](https://playwright.dev/) end-to-end testing tool.
|
||||
|
||||
### Adding a new performance journey
|
||||
|
||||
Let's assume we instrumented dashboard with load time metrics and want to track sample data flights dashboard performance.
|
||||
Journey supports loading test data with esArchiver or kbnArchiver. Similar to functional tests, it might require to implement custom wait
|
||||
for UI rendering to be completed.
|
||||
|
@ -42,18 +44,36 @@ export const journey = new Journey({
|
|||
});
|
||||
```
|
||||
|
||||
Alternative to archives is to use Synthtrace ES client:
|
||||
|
||||
```
|
||||
export const journey = new Journey({
|
||||
synthtrace: {
|
||||
type: 'apm',
|
||||
generator: generateApmData,
|
||||
options: {
|
||||
from: new Date(Date.now() - 1000 * 60 * 15),
|
||||
to: new Date(Date.now() + 1000 * 60 * 15),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
In oder to get correct and consistent metrics, it is important to design journey properly:
|
||||
- use archives to generate test data
|
||||
|
||||
- use archives or synthtrace to generate test data
|
||||
- decouple complex scenarios into multiple simple journeys
|
||||
- use waiting for page loading / UI component rendering
|
||||
- test locally and check if journey is stable.
|
||||
- make sure performance metrics are collected on every run.
|
||||
|
||||
### Running performance journey locally for troubleshooting purposes
|
||||
|
||||
Use the Node script:
|
||||
`node scripts/run_performance.js --journey-path x-pack/performance/journeys_e2e/$YOUR_JOURNEY_NAME.ts`
|
||||
`node scripts/run_performance.js --journey-path x-pack/performance/journeys_e2e/$YOUR_JOURNEY_NAME.ts`
|
||||
|
||||
Scripts steps include:
|
||||
|
||||
- start Elasticsearch
|
||||
- start Kibana and run journey first time (warmup) only APM metrics being reported
|
||||
- start Kibana and run journey second time (test): both EBT and APM metrics being reported
|
||||
|
@ -65,6 +85,7 @@ Since the tests are run on a local machine, there is also realistic throttling a
|
|||
simulate real life internet connection. This means that all requests have a fixed latency and limited bandwidth.
|
||||
|
||||
### Benchmarking performance on CI
|
||||
|
||||
In order to keep track on performance metrics stability, journeys are run on main branch with a scheduled interval.
|
||||
Bare metal machine is used to produce results as stable and reproducible as possible.
|
||||
|
||||
|
@ -77,9 +98,8 @@ RAM: 128 GB
|
|||
SSD: 1.92 TB Data center Gen4 NVMe
|
||||
|
||||
#### Track performance results
|
||||
|
||||
APM metrics are reported to [kibana-ops-e2e-perf](https://kibana-ops-e2e-perf.kb.us-central1.gcp.cloud.es.io/) cluster.
|
||||
You can filter transactions using labels, e.g. `labels.journeyName : "flight_dashboard"`
|
||||
|
||||
Custom metrics reported with EBT are available in [Telemetry Staging](https://telemetry-v2-staging.elastic.dev/) cluster, `kibana-performance` space.
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,16 @@ import Path from 'path';
|
|||
|
||||
import { REPO_ROOT } from '@kbn/repo-info';
|
||||
|
||||
import { SynthtraceGenerator } from '@kbn/apm-synthtrace-client/src/types';
|
||||
import { Readable } from 'stream';
|
||||
import { BaseStepCtx } from './journey';
|
||||
import { SynthtraceClientType } from '../services/synthtrace';
|
||||
|
||||
interface JourneySynthtrace<T extends { '@timestamp'?: number | undefined }, O = any> {
|
||||
type: SynthtraceClientType;
|
||||
generator: (options: O) => Readable | SynthtraceGenerator<T>;
|
||||
options: O;
|
||||
}
|
||||
|
||||
export interface RampConcurrentUsersAction {
|
||||
action: 'rampConcurrentUsers';
|
||||
|
@ -69,7 +78,7 @@ export interface ScalabilitySetup {
|
|||
test: ScalabilityAction[];
|
||||
}
|
||||
|
||||
export interface JourneyConfigOptions<CtxExt> {
|
||||
export interface JourneyConfigOptions<CtxExt extends { '@timestamp'?: number | undefined }> {
|
||||
/**
|
||||
* Relative path to FTR config file. Use to override the default ones:
|
||||
* 'x-pack/test/functional/config.base.js', 'test/functional/config.base.js'
|
||||
|
@ -116,9 +125,23 @@ export interface JourneyConfigOptions<CtxExt> {
|
|||
extendContext?: (ctx: BaseStepCtx) => CtxExt;
|
||||
/**
|
||||
* Use this to define actions that will be executed after Kibana & ES were started,
|
||||
* but before archives are loaded. APM traces are not collected for this hook.
|
||||
* but before archives are loaded or synthtrace is run. APM traces are not collected for this hook.
|
||||
*/
|
||||
beforeSteps?: (ctx: BaseStepCtx & CtxExt) => Promise<void>;
|
||||
/**
|
||||
* Use to setup ES data ingestion with APM Synthtrace
|
||||
*
|
||||
* synthtrace: {
|
||||
* type: 'infra',
|
||||
* generator: generateHostsData,
|
||||
* options: {
|
||||
* from: new Date(Date.now() - 1000 * 60 * 10),
|
||||
* to: new Date(),
|
||||
* count: 1000,
|
||||
* },
|
||||
* },
|
||||
*/
|
||||
synthtrace?: JourneySynthtrace<CtxExt>;
|
||||
}
|
||||
|
||||
export class JourneyConfig<CtxExt extends object> {
|
||||
|
@ -192,4 +215,8 @@ export class JourneyConfig<CtxExt extends object> {
|
|||
new Promise<void>((resolve) => resolve());
|
||||
}
|
||||
}
|
||||
|
||||
getSynthtraceConfig() {
|
||||
return this.#opts.synthtrace;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,11 @@ import type { Step, AnyStep } from './journey';
|
|||
import type { JourneyConfig } from './journey_config';
|
||||
import { JourneyScreenshots } from './journey_screenshots';
|
||||
import { getNewPageObject } from '../services/page';
|
||||
import { getSynthtraceClient } from '../services/synthtrace';
|
||||
|
||||
export class JourneyFtrHarness {
|
||||
private readonly screenshots: JourneyScreenshots;
|
||||
private readonly kbnUrl: KibanaUrl;
|
||||
|
||||
constructor(
|
||||
private readonly log: ToolingLog,
|
||||
|
@ -44,6 +46,15 @@ export class JourneyFtrHarness {
|
|||
private readonly journeyConfig: JourneyConfig<any>
|
||||
) {
|
||||
this.screenshots = new JourneyScreenshots(this.journeyConfig.getName());
|
||||
this.kbnUrl = new KibanaUrl(
|
||||
new URL(
|
||||
Url.format({
|
||||
protocol: this.config.get('servers.kibana.protocol'),
|
||||
hostname: this.config.get('servers.kibana.hostname'),
|
||||
port: this.config.get('servers.kibana.port'),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private browser: ChromiumBrowser | undefined;
|
||||
|
@ -61,6 +72,7 @@ export class JourneyFtrHarness {
|
|||
// journey can be run to collect EBT/APM metrics or just as a functional test
|
||||
// TEST_PERFORMANCE_PHASE is defined via scripts/run_perfomance.js run only
|
||||
private readonly isPerformanceRun = process.env.TEST_PERFORMANCE_PHASE || false;
|
||||
private readonly isWarmupPhase = process.env.TEST_PERFORMANCE_PHASE === 'WARMUP';
|
||||
|
||||
// Update the Telemetry and APM global labels to link traces with journey
|
||||
private async updateTelemetryAndAPMLabels(labels: { [k: string]: string }) {
|
||||
|
@ -158,16 +170,54 @@ export class JourneyFtrHarness {
|
|||
await this.interceptBrowserRequests(this.page);
|
||||
}
|
||||
|
||||
private async runSynthtrace() {
|
||||
const config = this.journeyConfig.getSynthtraceConfig();
|
||||
if (config) {
|
||||
const client = await getSynthtraceClient(config.type, {
|
||||
log: this.log,
|
||||
es: this.es,
|
||||
auth: this.auth,
|
||||
kbnUrl: this.kbnUrl,
|
||||
});
|
||||
const generator = config.generator(config.options);
|
||||
await client.index(generator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onSetup is part of high level 'before' hook and does the following sequentially:
|
||||
* 1. Start browser
|
||||
* 2. Load test data (opt-in)
|
||||
* 3. Run BeforeSteps (opt-in)
|
||||
* 4. Setup APM
|
||||
*/
|
||||
private async onSetup() {
|
||||
// We start browser and init page in the first place
|
||||
await this.setupBrowserAndPage();
|
||||
// We allow opt-in beforeSteps hook to manage Kibana/ES state
|
||||
|
||||
// We allow opt-in beforeSteps hook to manage Kibana/ES after start, install integrations, etc.
|
||||
await this.journeyConfig.getBeforeStepsFn(this.getCtx());
|
||||
// Loading test data
|
||||
|
||||
/**
|
||||
* Loading test data, optionally but following the order:
|
||||
* 1. Synthtrace client
|
||||
* 2. ES archives
|
||||
* 3. Kbn archives (Saved objects)
|
||||
*/
|
||||
|
||||
// To insure we ingest data with synthtrace only once during performance run
|
||||
if (!this.isPerformanceRun || this.isWarmupPhase) {
|
||||
await this.runSynthtrace();
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
asyncForEach(this.journeyConfig.getEsArchives(), async (esArchive) => {
|
||||
if (this.isPerformanceRun) {
|
||||
// we start Elasticsearch only once and keep ES data persisitent.
|
||||
//
|
||||
/**
|
||||
* During performance run we ingest data to ES before WARMUP phase, and avoid re-indexing
|
||||
* before TEST phase by insuring index already exists
|
||||
*/
|
||||
await this.esArchiver.loadIfNeeded(esArchive);
|
||||
} else {
|
||||
await this.esArchiver.load(esArchive);
|
||||
|
@ -242,7 +292,9 @@ export class JourneyFtrHarness {
|
|||
await this.teardownApm();
|
||||
await Promise.all([
|
||||
asyncForEach(this.journeyConfig.getEsArchives(), async (esArchive) => {
|
||||
// Keep ES data when journey is run twice (avoid unload after "Warmup" phase)
|
||||
/**
|
||||
* Keep ES data after WARMUP phase to avoid re-indexing
|
||||
*/
|
||||
if (!this.isPerformanceRun) {
|
||||
await this.esArchiver.unload(esArchive);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"type": "test-helper",
|
||||
"id": "@kbn/journeys",
|
||||
"owner": ["@elastic/kibana-operations", "@elastic/appex-qa"],
|
||||
"devOnly": true
|
||||
|
|
123
packages/kbn-journeys/services/synthtrace.ts
Normal file
123
packages/kbn-journeys/services/synthtrace.ts
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 {
|
||||
ApmSynthtraceEsClient,
|
||||
ApmSynthtraceKibanaClient,
|
||||
InfraSynthtraceEsClient,
|
||||
InfraSynthtraceKibanaClient,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
import Url from 'url';
|
||||
import { Logger } from '@kbn/apm-synthtrace/src/lib/utils/create_logger';
|
||||
import { Auth, Es } from '.';
|
||||
import { KibanaUrl } from './kibana_url';
|
||||
|
||||
export interface SynthtraceClientOptions {
|
||||
kbnUrl: KibanaUrl;
|
||||
auth: Auth;
|
||||
es: Es;
|
||||
log: ToolingLog;
|
||||
}
|
||||
export type SynthtraceClient = InfraSynthtraceEsClient | ApmSynthtraceEsClient;
|
||||
export type SynthtraceClientType = 'infra' | 'apm';
|
||||
|
||||
export async function getSynthtraceClient(
|
||||
type: SynthtraceClientType,
|
||||
options: SynthtraceClientOptions
|
||||
): Promise<SynthtraceClient> {
|
||||
if (type === 'infra') {
|
||||
return initInfraSynthtraceClient(options);
|
||||
} else {
|
||||
return initApmSynthtraceClient(options);
|
||||
}
|
||||
}
|
||||
|
||||
// Adapting ToolingLog instance to Logger interface
|
||||
class LoggerAdapter implements Logger {
|
||||
private log: ToolingLog;
|
||||
private joiner = ', ';
|
||||
|
||||
constructor(log: ToolingLog) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
debug(...args: any[]): void {
|
||||
this.log.debug(args.join(this.joiner));
|
||||
}
|
||||
|
||||
info(...args: any[]): void {
|
||||
this.log.info(args.join(this.joiner));
|
||||
}
|
||||
|
||||
error(arg: string | Error): void {
|
||||
this.log.error(arg);
|
||||
}
|
||||
|
||||
perf<T>(name: string, cb: () => T): T {
|
||||
const startTime = Date.now();
|
||||
const result = cb();
|
||||
const duration = Date.now() - startTime;
|
||||
const durationInSeconds = duration / 1000;
|
||||
const formattedTime = durationInSeconds.toFixed(3) + 's';
|
||||
this.log.info(`${name} took ${formattedTime}.`);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
async function initInfraSynthtraceClient(options: SynthtraceClientOptions) {
|
||||
const { log, es, auth, kbnUrl } = options;
|
||||
const logger: Logger = new LoggerAdapter(log);
|
||||
|
||||
const synthKbnClient = new InfraSynthtraceKibanaClient({
|
||||
logger,
|
||||
target: kbnUrl.get(),
|
||||
username: auth.getUsername(),
|
||||
password: auth.getPassword(),
|
||||
});
|
||||
const pkgVersion = await synthKbnClient.fetchLatestSystemPackageVersion();
|
||||
await synthKbnClient.installSystemPackage(pkgVersion);
|
||||
|
||||
const synthEsClient = new InfraSynthtraceEsClient({
|
||||
logger,
|
||||
client: es,
|
||||
refreshAfterIndex: true,
|
||||
});
|
||||
|
||||
return synthEsClient;
|
||||
}
|
||||
|
||||
async function initApmSynthtraceClient(options: SynthtraceClientOptions) {
|
||||
const { log, es, auth, kbnUrl } = options;
|
||||
const logger: Logger = new LoggerAdapter(log);
|
||||
const kibanaUrl = new URL(kbnUrl.get());
|
||||
const kibanaUrlWithAuth = Url.format({
|
||||
protocol: kibanaUrl.protocol,
|
||||
hostname: kibanaUrl.hostname,
|
||||
port: kibanaUrl.port,
|
||||
auth: `${auth.getUsername()}:${auth.getPassword()}`,
|
||||
});
|
||||
|
||||
const synthKbnClient = new ApmSynthtraceKibanaClient({
|
||||
logger,
|
||||
target: kibanaUrlWithAuth,
|
||||
});
|
||||
const packageVersion = await synthKbnClient.fetchLatestApmPackageVersion();
|
||||
await synthKbnClient.installApmPackage(packageVersion);
|
||||
|
||||
const synthEsClient = new ApmSynthtraceEsClient({
|
||||
client: es,
|
||||
logger,
|
||||
refreshAfterIndex: true,
|
||||
version: packageVersion,
|
||||
});
|
||||
|
||||
synthEsClient.pipeline(synthEsClient.getDefaultPipeline(false));
|
||||
|
||||
return synthEsClient;
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
"@kbn/std",
|
||||
"@kbn/test-subj-selector",
|
||||
"@kbn/core-http-common",
|
||||
"@kbn/apm-synthtrace-client",
|
||||
"@kbn/apm-synthtrace",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"type": "test-helper",
|
||||
"id": "@kbn/performance-testing-dataset-extractor",
|
||||
"devOnly": true,
|
||||
"owner": "@elastic/kibana-performance-testing"
|
||||
|
|
|
@ -7,36 +7,19 @@
|
|||
|
||||
import { Journey } from '@kbn/journeys';
|
||||
import { subj } from '@kbn/test-subj-selector';
|
||||
import { SynthtraceClient } from '../services/synthtrace';
|
||||
import { generateData } from '../synthtrace_data/apm_data';
|
||||
import { generateApmData } from '../synthtrace_data/apm_data';
|
||||
|
||||
export const journey = new Journey({
|
||||
beforeSteps: async ({ kbnUrl, log, auth, es }) => {
|
||||
// Install APM Package
|
||||
const synthClient = new SynthtraceClient({
|
||||
kbnBaseUrl: kbnUrl.get(),
|
||||
logger: log,
|
||||
username: auth.getUsername(),
|
||||
password: auth.getPassword(),
|
||||
esClient: es,
|
||||
});
|
||||
|
||||
await synthClient.installApmPackage();
|
||||
// Setup Synthtrace Client
|
||||
await synthClient.initialiseEsClient();
|
||||
// Generate data using Synthtrace
|
||||
const start = Date.now() - 1000 * 60 * 15;
|
||||
const end = Date.now() + 1000 * 60 * 15;
|
||||
await synthClient.index(
|
||||
generateData({
|
||||
from: new Date(start).getTime(),
|
||||
to: new Date(end).getTime(),
|
||||
})
|
||||
);
|
||||
synthtrace: {
|
||||
type: 'apm',
|
||||
generator: generateApmData,
|
||||
options: {
|
||||
from: new Date(Date.now() - 1000 * 60 * 15),
|
||||
to: new Date(Date.now() + 1000 * 60 * 15),
|
||||
},
|
||||
},
|
||||
|
||||
ftrConfigPath: 'x-pack/performance/configs/apm_config.ts',
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/162813
|
||||
skipped: true,
|
||||
})
|
||||
.step('Navigate to Service Inventory Page', async ({ page, kbnUrl }) => {
|
||||
await page.goto(kbnUrl.get(`app/apm/services`));
|
||||
|
|
|
@ -6,42 +6,18 @@
|
|||
*/
|
||||
|
||||
import { Journey } from '@kbn/journeys';
|
||||
import {
|
||||
createLogger,
|
||||
InfraSynthtraceEsClient,
|
||||
LogLevel,
|
||||
InfraSynthtraceKibanaClient,
|
||||
} from '@kbn/apm-synthtrace';
|
||||
import { infra, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { subj } from '@kbn/test-subj-selector';
|
||||
import { generateHostsData } from '../synthtrace_data/hosts_data';
|
||||
|
||||
export const journey = new Journey({
|
||||
beforeSteps: async ({ kbnUrl, auth, es }) => {
|
||||
const logger = createLogger(LogLevel.debug);
|
||||
const synthKibanaClient = new InfraSynthtraceKibanaClient({
|
||||
logger,
|
||||
target: kbnUrl.get(),
|
||||
username: auth.getUsername(),
|
||||
password: auth.getPassword(),
|
||||
});
|
||||
|
||||
const pkgVersion = await synthKibanaClient.fetchLatestSystemPackageVersion();
|
||||
await synthKibanaClient.installSystemPackage(pkgVersion);
|
||||
|
||||
const synthEsClient = new InfraSynthtraceEsClient({
|
||||
logger,
|
||||
client: es,
|
||||
refreshAfterIndex: true,
|
||||
});
|
||||
|
||||
const start = Date.now() - 1000 * 60 * 10;
|
||||
await synthEsClient.index(
|
||||
generateHostsData({
|
||||
from: new Date(start).toISOString(),
|
||||
to: new Date().toISOString(),
|
||||
count: 1000,
|
||||
})
|
||||
);
|
||||
synthtrace: {
|
||||
type: 'infra',
|
||||
generator: generateHostsData,
|
||||
options: {
|
||||
from: new Date(Date.now() - 1000 * 60 * 10),
|
||||
to: new Date(),
|
||||
count: 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
.step('Navigate to Hosts view and load 500 hosts', async ({ page, kbnUrl, kibanaPage }) => {
|
||||
|
@ -63,33 +39,3 @@ export const journey = new Journey({
|
|||
// wait for metric charts on the asset details view to be loaded
|
||||
await kibanaPage.waitForCharts({ count: 4, timeout: 60000 });
|
||||
});
|
||||
|
||||
export function generateHostsData({
|
||||
from,
|
||||
to,
|
||||
count = 1,
|
||||
}: {
|
||||
from: string;
|
||||
to: string;
|
||||
count: number;
|
||||
}) {
|
||||
const range = timerange(from, to);
|
||||
|
||||
const hosts = Array(count)
|
||||
.fill(0)
|
||||
.map((_, idx) => infra.host(`my-host-${idx}`));
|
||||
|
||||
return range
|
||||
.interval('30s')
|
||||
.rate(1)
|
||||
.generator((timestamp, index) =>
|
||||
hosts.flatMap((host) => [
|
||||
host.cpu().timestamp(timestamp),
|
||||
host.memory().timestamp(timestamp),
|
||||
host.network().timestamp(timestamp),
|
||||
host.load().timestamp(timestamp),
|
||||
host.filesystem().timestamp(timestamp),
|
||||
host.diskio().timestamp(timestamp),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,66 +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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ApmSynthtraceEsClient, ApmSynthtraceKibanaClient } from '@kbn/apm-synthtrace';
|
||||
import Url from 'url';
|
||||
import { Readable } from 'stream';
|
||||
import { ApmFields, SynthtraceGenerator } from '@kbn/apm-synthtrace-client';
|
||||
|
||||
export interface SynthtraceClientParams {
|
||||
kbnBaseUrl: string;
|
||||
logger: any;
|
||||
username: string;
|
||||
password: string;
|
||||
esClient: any;
|
||||
}
|
||||
|
||||
export class SynthtraceClient {
|
||||
private synthtraceEsClient: ApmSynthtraceEsClient | undefined;
|
||||
private packageVersion: string = '';
|
||||
private readonly kibanaUrlWithAuth: string;
|
||||
|
||||
constructor(private readonly baseParams: SynthtraceClientParams) {
|
||||
const kibanaUrl = new URL(this.baseParams.kbnBaseUrl);
|
||||
this.kibanaUrlWithAuth = Url.format({
|
||||
protocol: kibanaUrl.protocol,
|
||||
hostname: kibanaUrl.hostname,
|
||||
port: kibanaUrl.port,
|
||||
auth: `${this.baseParams.username}:${this.baseParams.password}`,
|
||||
});
|
||||
}
|
||||
|
||||
async installApmPackage() {
|
||||
const kibanaClient = new ApmSynthtraceKibanaClient({
|
||||
logger: this.baseParams.logger,
|
||||
target: this.kibanaUrlWithAuth,
|
||||
});
|
||||
this.packageVersion = await kibanaClient.fetchLatestApmPackageVersion();
|
||||
|
||||
await kibanaClient.installApmPackage(this.packageVersion);
|
||||
}
|
||||
|
||||
async initialiseEsClient() {
|
||||
this.synthtraceEsClient = new ApmSynthtraceEsClient({
|
||||
client: this.baseParams.esClient,
|
||||
logger: this.baseParams.logger,
|
||||
refreshAfterIndex: true,
|
||||
version: this.packageVersion,
|
||||
});
|
||||
|
||||
this.synthtraceEsClient.pipeline(this.synthtraceEsClient.getDefaultPipeline(false));
|
||||
}
|
||||
|
||||
async index(events: SynthtraceGenerator<ApmFields>) {
|
||||
if (this.synthtraceEsClient) {
|
||||
await this.synthtraceEsClient.index(
|
||||
Readable.from(Array.from(events).flatMap((event) => event.serialize()))
|
||||
);
|
||||
} else {
|
||||
throw new Error('ES Client not initialised');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { apm, httpExitSpan, timerange } from '@kbn/apm-synthtrace-client';
|
||||
import { Readable } from 'stream';
|
||||
|
||||
export function generateData({ from, to }: { from: number; to: number }) {
|
||||
const range = timerange(from, to);
|
||||
export function generateApmData({ from, to }: { from: Date; to: Date }) {
|
||||
const range = timerange(from.toISOString(), to.toISOString());
|
||||
const transactionName = '240rpm/75% 1000ms';
|
||||
|
||||
const synthRum = apm
|
||||
|
@ -20,7 +21,7 @@ export function generateData({ from, to }: { from: number; to: number }) {
|
|||
.service({ name: 'synth-go', environment: 'production', agentName: 'go' })
|
||||
.instance('my-instance');
|
||||
|
||||
return range.interval('1m').generator((timestamp) => {
|
||||
const data = range.interval('1m').generator((timestamp) => {
|
||||
return synthRum
|
||||
.transaction({ transactionName })
|
||||
.duration(400)
|
||||
|
@ -74,4 +75,6 @@ export function generateData({ from, to }: { from: number; to: number }) {
|
|||
)
|
||||
);
|
||||
});
|
||||
|
||||
return Readable.from(Array.from(data).flatMap((event) => event.serialize()));
|
||||
}
|
||||
|
|
37
x-pack/performance/synthtrace_data/hosts_data.ts
Normal file
37
x-pack/performance/synthtrace_data/hosts_data.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { infra, timerange } from '@kbn/apm-synthtrace-client';
|
||||
|
||||
export function generateHostsData({
|
||||
from,
|
||||
to,
|
||||
count = 1,
|
||||
}: {
|
||||
from: Date;
|
||||
to: Date;
|
||||
count?: number;
|
||||
}) {
|
||||
const range = timerange(from.toISOString(), to.toISOString());
|
||||
|
||||
const hosts = Array(count)
|
||||
.fill(0)
|
||||
.map((_, idx) => infra.host(`my-host-${idx}`));
|
||||
|
||||
return range
|
||||
.interval('30s')
|
||||
.rate(1)
|
||||
.generator((timestamp, index) =>
|
||||
hosts.flatMap((host) => [
|
||||
host.cpu().timestamp(timestamp),
|
||||
host.memory().timestamp(timestamp),
|
||||
host.network().timestamp(timestamp),
|
||||
host.load().timestamp(timestamp),
|
||||
host.filesystem().timestamp(timestamp),
|
||||
host.diskio().timestamp(timestamp),
|
||||
])
|
||||
);
|
||||
}
|
|
@ -15,7 +15,6 @@
|
|||
"@kbn/test",
|
||||
"@kbn/expect",
|
||||
"@kbn/dev-utils",
|
||||
"@kbn/apm-synthtrace",
|
||||
"@kbn/apm-synthtrace-client",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue