mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[APM][OTel] Fix span url link when transactionId missing in Span Links (#218232)
Closes #214557 ### Summary Use `traceId` to generate url link to Span as fallback when `transactionId` is missing. ### How to test 1. Run the following synthtrace scenario: `node scripts/synthtrace span_links.ts --live --uniqueIds --clean --logLevel=debug --scenarioOpts pipeline=apmToOtel` 2. Go to **Service** -> **Transactions** -> in **Transaction waterfall** click **Span Links** label -> select **Outgoing links** from downdown -> check if **Span** link works https://github.com/user-attachments/assets/c22fdc5e-7ba9-4817-a78b-bf5fb9a53651 --------- Co-authored-by: jennypavlova <dzheni.pavlova@elastic.co>
This commit is contained in:
parent
56b3e21fc3
commit
8caec69036
24 changed files with 221 additions and 190 deletions
|
@ -14,6 +14,7 @@ export const TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR = 'TRANSACTION_DETAILS_BY_T
|
|||
export interface TransactionDetailsByTraceIdLocatorParams extends SerializableRecord {
|
||||
rangeFrom?: string;
|
||||
rangeTo?: string;
|
||||
waterfallItemId?: string;
|
||||
traceId: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,11 +43,13 @@ export {
|
|||
setIdGeneratorStrategy,
|
||||
} from './src/lib/utils/generate_id';
|
||||
export { appendHash, hashKeysOf } from './src/lib/utils/hash';
|
||||
export type {
|
||||
ESDocumentWithOperation,
|
||||
SynthtraceESAction,
|
||||
SynthtraceGenerator,
|
||||
SynthtraceDynamicTemplate,
|
||||
export {
|
||||
type ESDocumentWithOperation,
|
||||
type SynthtraceESAction,
|
||||
type SynthtraceGenerator,
|
||||
type SynthtraceDynamicTemplate,
|
||||
type ApmSynthtracePipelines,
|
||||
ApmSynthtracePipelineSchema,
|
||||
} from './src/types';
|
||||
export { log, type LogDocument, LONG_FIELD_NAME } from './src/lib/logs';
|
||||
export { otelLog, type OtelLogDocument } from './src/lib/otel_logs';
|
||||
|
|
|
@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export enum ApmSynthtracePipelineSchema {
|
||||
Default = 'default', // classic APM
|
||||
Otel = 'otel', // OTel native through APM server
|
||||
ApmToOtel = 'apmToOtel', // convert classic APM synthtrace scenario into OTel native (useful to run existing scenarios as OTel)
|
||||
}
|
||||
export type ApmSynthtracePipelines =
|
||||
| ApmSynthtracePipelineSchema.Default
|
||||
| ApmSynthtracePipelineSchema.Otel
|
||||
| ApmSynthtracePipelineSchema.ApmToOtel;
|
|
@ -25,3 +25,8 @@ export type SynthtraceGenerator<TFields extends Fields> = Generator<Serializable
|
|||
export type SynthtraceProcessor<TFields extends Fields> = (
|
||||
fields: ESDocumentWithOperation<TFields>
|
||||
) => ESDocumentWithOperation<TFields>;
|
||||
|
||||
export {
|
||||
ApmSynthtracePipelineSchema,
|
||||
type ApmSynthtracePipelines,
|
||||
} from './apm_synthtrace_pipelines';
|
||||
|
|
|
@ -14,10 +14,7 @@ export {
|
|||
extendToolingLog,
|
||||
} from './src/lib/utils/create_logger';
|
||||
|
||||
export {
|
||||
ApmSynthtraceEsClient,
|
||||
type ApmSynthtracePipelines,
|
||||
} from './src/lib/apm/client/apm_synthtrace_es_client';
|
||||
export { ApmSynthtraceEsClient } from './src/lib/apm/client/apm_synthtrace_es_client';
|
||||
export { ApmSynthtraceKibanaClient } from './src/lib/apm/client/apm_synthtrace_kibana_client';
|
||||
export { InfraSynthtraceEsClient } from './src/lib/infra/infra_synthtrace_es_client';
|
||||
export { InfraSynthtraceKibanaClient } from './src/lib/infra/infra_synthtrace_kibana_client';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import { Client, estypes } from '@elastic/elasticsearch';
|
||||
import { ApmFields, ApmOtelFields } from '@kbn/apm-synthtrace-client';
|
||||
import { ApmFields, ApmOtelFields, ApmSynthtracePipelines } from '@kbn/apm-synthtrace-client';
|
||||
import { ValuesType } from 'utility-types';
|
||||
import { SynthtraceEsClient, SynthtraceEsClientOptions } from '../../../shared/base_client';
|
||||
import { Logger } from '../../../utils/create_logger';
|
||||
|
@ -25,7 +25,6 @@ export enum ComponentTemplateName {
|
|||
TracesApmRum = 'traces-apm.rum@custom',
|
||||
TracesApmSampled = 'traces-apm.sampled@custom',
|
||||
}
|
||||
export type ApmSynthtracePipelines = 'default' | 'otelToApm' | 'apmToOtel';
|
||||
|
||||
export interface ApmSynthtraceEsClientOptions extends Omit<SynthtraceEsClientOptions, 'pipeline'> {
|
||||
version: string;
|
||||
|
@ -96,7 +95,7 @@ export class ApmSynthtraceEsClient extends SynthtraceEsClient<ApmFields | ApmOte
|
|||
} = { includeSerialization: true }
|
||||
) {
|
||||
switch (pipeline) {
|
||||
case 'otelToApm': {
|
||||
case 'otel': {
|
||||
return otelToApmPipeline(this.logger, options.includeSerialization);
|
||||
}
|
||||
case 'apmToOtel': {
|
||||
|
|
|
@ -7,16 +7,20 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ApmSynthtracePipelines } from '../../lib/apm/client/apm_synthtrace_es_client';
|
||||
import { ApmSynthtracePipelineSchema, ApmSynthtracePipelines } from '@kbn/apm-synthtrace-client';
|
||||
|
||||
const validPipelines: ApmSynthtracePipelines[] = ['apmToOtel', 'otelToApm', 'default'];
|
||||
const validPipelines: ApmSynthtracePipelines[] = [
|
||||
ApmSynthtracePipelineSchema.ApmToOtel,
|
||||
ApmSynthtracePipelineSchema.Otel,
|
||||
ApmSynthtracePipelineSchema.Default,
|
||||
];
|
||||
const parseApmPipeline = (value: ApmSynthtracePipelines): ApmSynthtracePipelines => {
|
||||
if (!value) return 'default';
|
||||
if (!value) return ApmSynthtracePipelineSchema.Default;
|
||||
|
||||
if (validPipelines.includes(value)) {
|
||||
return value;
|
||||
} else {
|
||||
return 'default';
|
||||
return ApmSynthtracePipelineSchema.Default;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { OtelInstance, ApmOtelFields } from '@kbn/apm-synthtrace-client';
|
||||
import {
|
||||
OtelInstance,
|
||||
ApmOtelFields,
|
||||
ApmSynthtracePipelineSchema,
|
||||
} from '@kbn/apm-synthtrace-client';
|
||||
import { apm } from '@kbn/apm-synthtrace-client/src/lib/apm';
|
||||
import { Scenario } from '../cli/scenario';
|
||||
import { withClient } from '../lib/utils/with_client';
|
||||
|
@ -18,7 +22,7 @@ const ENVIRONMENT = getSynthtraceEnvironment(__filename);
|
|||
const scenario: Scenario<ApmOtelFields> = async (runOptions) => {
|
||||
return {
|
||||
bootstrap: async ({ apmEsClient }) => {
|
||||
apmEsClient.pipeline(apmEsClient.getPipeline('otelToApm'));
|
||||
apmEsClient.pipeline(apmEsClient.getPipeline(ApmSynthtracePipelineSchema.Otel));
|
||||
},
|
||||
generate: ({ range, clients: { apmEsClient } }) => {
|
||||
const transactionName = 'oteldemo.AdServiceSynth/GetAds';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ApmFields, apm } from '@kbn/apm-synthtrace-client';
|
||||
import { ApmFields, ApmSynthtracePipelineSchema, apm } from '@kbn/apm-synthtrace-client';
|
||||
import { random } from 'lodash';
|
||||
import semver from 'semver';
|
||||
import { Scenario } from '../cli/scenario';
|
||||
|
@ -25,7 +25,7 @@ const scenario: Scenario<ApmFields> = async ({
|
|||
bootstrap: async ({ apmEsClient }) => {
|
||||
if (isLegacy) {
|
||||
apmEsClient.pipeline(
|
||||
apmEsClient.getPipeline('default', {
|
||||
apmEsClient.getPipeline(ApmSynthtracePipelineSchema.Default, {
|
||||
versionOverride: version,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ApmFields, apm, Instance } from '@kbn/apm-synthtrace-client';
|
||||
import { ApmFields, apm, Instance, ApmSynthtracePipelineSchema } from '@kbn/apm-synthtrace-client';
|
||||
import { Scenario } from '../cli/scenario';
|
||||
import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment';
|
||||
import { withClient } from '../lib/utils/with_client';
|
||||
|
@ -18,7 +18,9 @@ const ENVIRONMENT = getSynthtraceEnvironment(__filename);
|
|||
|
||||
const scenario: Scenario<ApmFields> = async (runOptions) => {
|
||||
const { logger } = runOptions;
|
||||
const { numServices = 3, pipeline = 'default' } = parseApmScenarioOpts(runOptions.scenarioOpts);
|
||||
const { numServices = 3, pipeline = ApmSynthtracePipelineSchema.Default } = parseApmScenarioOpts(
|
||||
runOptions.scenarioOpts
|
||||
);
|
||||
|
||||
return {
|
||||
bootstrap: async ({ apmEsClient }) => {
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Readable } from 'stream';
|
|||
import {
|
||||
apm,
|
||||
ApmFields,
|
||||
ApmSynthtracePipelineSchema,
|
||||
generateLongId,
|
||||
generateShortId,
|
||||
Serializable,
|
||||
|
@ -40,7 +41,7 @@ function getSpanLinksFromEvents(events: ApmFields[]) {
|
|||
}
|
||||
|
||||
const scenario: Scenario<ApmFields> = async ({ logger, scenarioOpts }) => {
|
||||
const { pipeline = 'default' } = parseApmScenarioOpts(scenarioOpts);
|
||||
const { pipeline = ApmSynthtracePipelineSchema.Default } = parseApmScenarioOpts(scenarioOpts);
|
||||
return {
|
||||
bootstrap: async ({ apmEsClient }) => {
|
||||
apmEsClient.pipeline(apmEsClient.getPipeline(pipeline));
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import url from 'url';
|
||||
import { ApmSynthtracePipelineSchema } from '@kbn/apm-synthtrace-client';
|
||||
import { synthtrace } from '../../../synthtrace';
|
||||
import { adserviceEdot } from '../../fixtures/synthtrace/adservice_edot';
|
||||
import { checkA11y } from '../../support/commands';
|
||||
|
@ -33,7 +34,7 @@ describe('Service Overview', () => {
|
|||
from: new Date(start).getTime(),
|
||||
to: new Date(end).getTime(),
|
||||
}),
|
||||
'otelToApm'
|
||||
ApmSynthtracePipelineSchema.Otel
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import url from 'url';
|
||||
import { ApmSynthtracePipelineSchema } from '@kbn/apm-synthtrace-client';
|
||||
import { synthtrace } from '../../../synthtrace';
|
||||
import { sendotlp } from '../../fixtures/synthtrace/sendotlp';
|
||||
import { checkA11y } from '../../support/commands';
|
||||
|
@ -33,7 +34,7 @@ describe('Service Overview', () => {
|
|||
from: new Date(start).getTime(),
|
||||
to: new Date(end).getTime(),
|
||||
}),
|
||||
'otelToApm'
|
||||
ApmSynthtracePipelineSchema.Otel
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ApmSynthtracePipelines } from '@kbn/apm-synthtrace';
|
||||
import { ApmSynthtraceEsClient, createLogger, LogLevel } from '@kbn/apm-synthtrace';
|
||||
import { createEsClientForTesting } from '@kbn/test';
|
||||
// eslint-disable-next-line @kbn/imports/no_unresolvable_imports
|
||||
import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
|
||||
import { Readable } from 'stream';
|
||||
import type { ApmSynthtracePipelines } from '@kbn/apm-synthtrace-client';
|
||||
|
||||
export function setupNodeEvents(on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) {
|
||||
const logger = createLogger(LogLevel.info);
|
||||
|
|
|
@ -4,18 +4,19 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ApmSynthtracePipelines } from '@kbn/apm-synthtrace';
|
||||
import type {
|
||||
Serializable,
|
||||
ApmFields,
|
||||
ApmOtelFields,
|
||||
SynthtraceGenerator,
|
||||
import {
|
||||
type Serializable,
|
||||
type ApmFields,
|
||||
type ApmOtelFields,
|
||||
type SynthtraceGenerator,
|
||||
type ApmSynthtracePipelines,
|
||||
ApmSynthtracePipelineSchema,
|
||||
} from '@kbn/apm-synthtrace-client';
|
||||
|
||||
export const synthtrace = {
|
||||
index: <TFields extends ApmFields | ApmOtelFields>(
|
||||
events: SynthtraceGenerator<TFields> | Array<Serializable<TFields>>,
|
||||
pipeline: ApmSynthtracePipelines = 'default'
|
||||
pipeline: ApmSynthtracePipelines = ApmSynthtracePipelineSchema.Default
|
||||
) =>
|
||||
cy.task('synthtrace:index', {
|
||||
events: Array.from(events).flatMap((event) => event.serialize()),
|
||||
|
|
|
@ -29,7 +29,7 @@ export function TraceLink() {
|
|||
const timeRange = dataService.query.timefilter.timefilter.getTime();
|
||||
const {
|
||||
path: { traceId },
|
||||
query: { rangeFrom = timeRange.from, rangeTo = timeRange.to },
|
||||
query: { rangeFrom = timeRange.from, rangeTo = timeRange.to, waterfallItemId },
|
||||
} = useApmParams('/link-to/trace/{traceId}');
|
||||
|
||||
const { start, end } = useTimeRange({
|
||||
|
@ -53,6 +53,7 @@ export function TraceLink() {
|
|||
transaction: data.transaction,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
waterfallItemId,
|
||||
})
|
||||
: getRedirectToTracePageUrl({ traceId, rangeFrom, rangeTo });
|
||||
return <Redirect to={to} />;
|
||||
|
|
|
@ -102,7 +102,7 @@ describe('TraceLink', () => {
|
|||
});
|
||||
|
||||
describe('transaction page', () => {
|
||||
it('renders with date range params', () => {
|
||||
it('renders with date range and waterfall params', () => {
|
||||
const transaction = {
|
||||
service: { name: 'foo' },
|
||||
transaction: {
|
||||
|
@ -125,13 +125,14 @@ describe('TraceLink', () => {
|
|||
query: {
|
||||
rangeFrom: 'now-24h',
|
||||
rangeTo: 'now',
|
||||
waterfallItemId: '789',
|
||||
},
|
||||
});
|
||||
|
||||
const component = shallow(<TraceLink />);
|
||||
|
||||
expect(component.prop('to')).toEqual(
|
||||
'/services/foo/transactions/view?traceId=123&transactionId=456&transactionName=bar&transactionType=GET&rangeFrom=now-24h&rangeTo=now&waterfallItemId='
|
||||
'/services/foo/transactions/view?traceId=123&transactionId=456&transactionName=bar&transactionType=GET&rangeFrom=now-24h&rangeTo=now&waterfallItemId=789'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ const apmRoutes = {
|
|||
query: t.partial({
|
||||
rangeFrom: t.string,
|
||||
rangeTo: t.string,
|
||||
waterfallItemId: t.string,
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
|
|
|
@ -20,6 +20,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useState } from 'react';
|
||||
import { TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR } from '@kbn/deeplinks-observability/locators';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import type { SpanLinkDetails } from '../../../../common/span_links';
|
||||
import { asDuration } from '../../../../common/utils/formatters';
|
||||
import { useAnyOfApmParams } from '../../../hooks/use_apm_params';
|
||||
|
@ -40,6 +42,18 @@ export function SpanLinksTable({ items }: Props) {
|
|||
'/mobile-services/{serviceName}/transactions/view'
|
||||
);
|
||||
const [idActionMenuOpen, setIdActionMenuOpen] = useState<string | undefined>();
|
||||
const {
|
||||
share: {
|
||||
url: { locators },
|
||||
},
|
||||
} = useApmPluginContext();
|
||||
|
||||
const apmLinkToTransactionByTraceIdLocator = locators.get<{
|
||||
traceId: string;
|
||||
rangeFrom: string;
|
||||
rangeTo: string;
|
||||
waterfallItemId: string;
|
||||
}>(TRANSACTION_DETAILS_BY_TRACE_ID_LOCATOR);
|
||||
|
||||
const columns: Array<EuiBasicTableColumn<SpanLinkDetails>> = [
|
||||
{
|
||||
|
@ -100,7 +114,7 @@ export function SpanLinksTable({ items }: Props) {
|
|||
}),
|
||||
sortable: true,
|
||||
render: (_, { spanId, traceId, details }) => {
|
||||
if (details && details.transactionId) {
|
||||
if (details) {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" gutterSize="xs" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -109,10 +123,19 @@ export function SpanLinksTable({ items }: Props) {
|
|||
<EuiFlexItem>
|
||||
<EuiLink
|
||||
data-test-subj="apmColumnsLink"
|
||||
href={router.link('/link-to/transaction/{transactionId}', {
|
||||
path: { transactionId: details.transactionId },
|
||||
query: { waterfallItemId: spanId },
|
||||
})}
|
||||
href={
|
||||
details.transactionId
|
||||
? router.link('/link-to/transaction/{transactionId}', {
|
||||
path: { transactionId: details.transactionId },
|
||||
query: { waterfallItemId: spanId },
|
||||
})
|
||||
: apmLinkToTransactionByTraceIdLocator?.getRedirectUrl({
|
||||
traceId,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
waterfallItemId: spanId,
|
||||
})
|
||||
}
|
||||
>
|
||||
{details.spanName}
|
||||
</EuiLink>
|
||||
|
|
|
@ -98,7 +98,6 @@ export { useTimeBuckets } from './hooks/use_time_buckets';
|
|||
export { createUseRulesLink } from './hooks/create_use_rules_link';
|
||||
export { useSummaryTimeRange } from './hooks/use_summary_time_range';
|
||||
|
||||
export { getApmTraceUrl } from './utils/get_apm_trace_url';
|
||||
export { buildEsQuery } from './utils/build_es_query';
|
||||
|
||||
export type {
|
||||
|
|
|
@ -1,16 +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 { getApmTraceUrl } from './get_apm_trace_url';
|
||||
|
||||
describe('getApmTraceUrl', () => {
|
||||
it('returns a trace url', () => {
|
||||
expect(getApmTraceUrl({ traceId: 'foo', rangeFrom: '123', rangeTo: '456' })).toEqual(
|
||||
'/link-to/trace/foo?rangeFrom=123&rangeTo=456'
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,18 +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.
|
||||
*/
|
||||
|
||||
export function getApmTraceUrl({
|
||||
traceId,
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
}: {
|
||||
traceId: string;
|
||||
rangeFrom: string;
|
||||
rangeTo: string;
|
||||
}) {
|
||||
return `/link-to/trace/${traceId}?` + new URLSearchParams({ rangeFrom, rangeTo }).toString();
|
||||
}
|
|
@ -24,9 +24,10 @@ export class TransactionDetailsByTraceIdLocatorDefinition
|
|||
public readonly getLocation = async ({
|
||||
rangeFrom,
|
||||
rangeTo,
|
||||
waterfallItemId,
|
||||
traceId,
|
||||
}: TransactionDetailsByTraceIdLocatorParams) => {
|
||||
const params = { rangeFrom, rangeTo };
|
||||
const params = { rangeFrom, rangeTo, waterfallItemId };
|
||||
return {
|
||||
app: 'apm',
|
||||
path: `/link-to/trace/${encodeURIComponent(traceId)}?${qs.stringify(params)}`,
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
import expect from '@kbn/expect';
|
||||
import { ProcessorEvent } from '@kbn/observability-plugin/common';
|
||||
import { Readable } from 'stream';
|
||||
import type { ApmSynthtraceEsClient, ApmSynthtracePipelines } from '@kbn/apm-synthtrace';
|
||||
import { type ApmSynthtraceEsClient } from '@kbn/apm-synthtrace';
|
||||
import moment from 'moment';
|
||||
import { ApmSynthtracePipelineSchema, ApmSynthtracePipelines } from '@kbn/apm-synthtrace-client';
|
||||
import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context';
|
||||
import { generateSpanLinksData } from './data_generator';
|
||||
|
||||
|
@ -20,17 +21,16 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
const start = moment(baseTime).subtract(15, 'minutes');
|
||||
const end = moment(baseTime);
|
||||
|
||||
const scenarios: ApmSynthtracePipelines[] = ['default', 'apmToOtel'];
|
||||
|
||||
// skips all tests that validate the `transaction.id` in `span` docs for Otel
|
||||
// TODO: remove this once a solution for that has been found
|
||||
const maybeIt = (pipeline: ApmSynthtracePipelines) => (pipeline === 'apmToOtel' ? it.skip : it);
|
||||
const scenarios: ApmSynthtracePipelines[] = [
|
||||
ApmSynthtracePipelineSchema.Default,
|
||||
ApmSynthtracePipelineSchema.ApmToOtel,
|
||||
];
|
||||
|
||||
describe('Span Links', () => {
|
||||
scenarios.forEach((pipeline) => {
|
||||
describe(`contains linked children - ${
|
||||
pipeline === 'default' ? 'elastic APM' : 'Otel'
|
||||
}`, () => {
|
||||
const isDefaultPipeline = pipeline === ApmSynthtracePipelineSchema.Default;
|
||||
|
||||
describe(`contains linked children - ${isDefaultPipeline ? 'elastic APM' : 'Otel'}`, () => {
|
||||
let ids: ReturnType<typeof generateSpanLinksData>['ids'];
|
||||
let apmSynthtraceEsClient: ApmSynthtraceEsClient;
|
||||
|
||||
|
@ -234,7 +234,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
expect(spanALinksDetails.parentsLinks.spanLinksDetails).to.eql([]);
|
||||
});
|
||||
|
||||
maybeIt(pipeline)('returns two children on Span A', () => {
|
||||
it('returns two children on Span A', () => {
|
||||
expect(spanALinksDetails.childrenLinks.spanLinksDetails.length).to.eql(2);
|
||||
const serviceCDetails = spanALinksDetails.childrenLinks.spanLinksDetails.find(
|
||||
(childDetails) => {
|
||||
|
@ -312,7 +312,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
]);
|
||||
});
|
||||
|
||||
maybeIt(pipeline)('returns consumer-multiple as child on Span B', () => {
|
||||
it('returns consumer-multiple as child on Span B', () => {
|
||||
expect(spanBLinksDetails.childrenLinks.spanLinksDetails.length).to.be(1);
|
||||
expect(spanBLinksDetails.childrenLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
|
@ -321,12 +321,14 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
details: {
|
||||
serviceName: 'consumer-multiple',
|
||||
agentName: 'nodejs',
|
||||
transactionId: ids.producerMultiple.transactionDId,
|
||||
spanName: 'Span E',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerMultiple.transactionDId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -358,46 +360,45 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
spanCLinksDetails = spanALinksDetailsResponse;
|
||||
});
|
||||
|
||||
maybeIt(pipeline)(
|
||||
'returns producer-internal-only Span A, producer-external-only Transaction B, and External link as parents of Transaction C',
|
||||
() => {
|
||||
expect(transactionCLinksDetails.parentsLinks.spanLinksDetails.length).to.be(3);
|
||||
expect(transactionCLinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerInternalOnly.traceId,
|
||||
spanId: ids.producerInternalOnly.spanAId,
|
||||
details: {
|
||||
serviceName: 'producer-internal-only',
|
||||
agentName: 'go',
|
||||
it('returns producer-internal-only Span A, producer-external-only Transaction B, and External link as parents of Transaction C', () => {
|
||||
expect(transactionCLinksDetails.parentsLinks.spanLinksDetails.length).to.be(3);
|
||||
expect(transactionCLinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerInternalOnly.traceId,
|
||||
spanId: ids.producerInternalOnly.spanAId,
|
||||
details: {
|
||||
serviceName: 'producer-internal-only',
|
||||
agentName: 'go',
|
||||
spanName: 'Span A',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerInternalOnly.transactionAId,
|
||||
spanName: 'Span A',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
traceId: ids.producerExternalOnly.traceId,
|
||||
spanId: ids.producerExternalOnly.transactionBId,
|
||||
details: {
|
||||
serviceName: 'producer-external-only',
|
||||
agentName: 'java',
|
||||
transactionId: ids.producerExternalOnly.transactionBId,
|
||||
duration: 1000000,
|
||||
spanName: 'Transaction B',
|
||||
environment: 'production',
|
||||
},
|
||||
},
|
||||
{
|
||||
traceId: ids.producerExternalOnly.traceId,
|
||||
spanId: ids.producerExternalOnly.transactionBId,
|
||||
details: {
|
||||
serviceName: 'producer-external-only',
|
||||
agentName: 'java',
|
||||
transactionId: ids.producerExternalOnly.transactionBId,
|
||||
duration: 1000000,
|
||||
spanName: 'Transaction B',
|
||||
environment: 'production',
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.externalTraceId,
|
||||
spanId: ids.producerExternalOnly.spanBId,
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.externalTraceId,
|
||||
spanId: ids.producerExternalOnly.spanBId,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
maybeIt(pipeline)('returns consumer-multiple Span E as child of Transaction C', () => {
|
||||
it('returns consumer-multiple Span E as child of Transaction C', () => {
|
||||
expect(transactionCLinksDetails.childrenLinks.spanLinksDetails.length).to.be(1);
|
||||
expect(transactionCLinksDetails.childrenLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
|
@ -406,12 +407,14 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
details: {
|
||||
serviceName: 'consumer-multiple',
|
||||
agentName: 'nodejs',
|
||||
transactionId: ids.producerMultiple.transactionDId,
|
||||
spanName: 'Span E',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerMultiple.transactionDId,
|
||||
}),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
@ -421,7 +424,7 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
expect(spanCLinksDetails.parentsLinks.spanLinksDetails.length).to.be(0);
|
||||
});
|
||||
|
||||
maybeIt(pipeline)('returns consumer-multiple as Child on producer-consumer', () => {
|
||||
it('returns consumer-multiple as Child on producer-consumer', () => {
|
||||
expect(spanCLinksDetails.childrenLinks.spanLinksDetails.length).to.be(1);
|
||||
expect(spanCLinksDetails.childrenLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
|
@ -465,82 +468,82 @@ export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderCon
|
|||
spanELinksDetails = spanALinksDetailsResponse;
|
||||
});
|
||||
|
||||
maybeIt(pipeline)(
|
||||
'returns producer-internal-only Span A and producer-consumer Span C as parents of Transaction D',
|
||||
() => {
|
||||
expect(transactionDLinksDetails.parentsLinks.spanLinksDetails.length).to.be(2);
|
||||
expect(transactionDLinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerInternalOnly.traceId,
|
||||
spanId: ids.producerInternalOnly.spanAId,
|
||||
details: {
|
||||
serviceName: 'producer-internal-only',
|
||||
agentName: 'go',
|
||||
it('returns producer-internal-only Span A and producer-consumer Span C as parents of Transaction D', () => {
|
||||
expect(transactionDLinksDetails.parentsLinks.spanLinksDetails.length).to.be(2);
|
||||
expect(transactionDLinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerInternalOnly.traceId,
|
||||
spanId: ids.producerInternalOnly.spanAId,
|
||||
details: {
|
||||
serviceName: 'producer-internal-only',
|
||||
agentName: 'go',
|
||||
spanName: 'Span A',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerInternalOnly.transactionAId,
|
||||
spanName: 'Span A',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.traceId,
|
||||
spanId: ids.producerConsumer.spanCId,
|
||||
details: {
|
||||
serviceName: 'producer-consumer',
|
||||
agentName: 'ruby',
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.traceId,
|
||||
spanId: ids.producerConsumer.spanCId,
|
||||
details: {
|
||||
serviceName: 'producer-consumer',
|
||||
agentName: 'ruby',
|
||||
spanName: 'Span C',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerConsumer.transactionCId,
|
||||
spanName: 'Span C',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
},
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns no children on Transaction D', () => {
|
||||
expect(transactionDLinksDetails.childrenLinks.spanLinksDetails.length).to.be(0);
|
||||
});
|
||||
|
||||
maybeIt(pipeline)(
|
||||
'returns producer-external-only Span B and producer-consumer Transaction C as parents of Span E',
|
||||
() => {
|
||||
expect(spanELinksDetails.parentsLinks.spanLinksDetails.length).to.be(2);
|
||||
it('returns producer-external-only Span B and producer-consumer Transaction C as parents of Span E', () => {
|
||||
expect(spanELinksDetails.parentsLinks.spanLinksDetails.length).to.be(2);
|
||||
|
||||
expect(spanELinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerExternalOnly.traceId,
|
||||
spanId: ids.producerExternalOnly.spanBId,
|
||||
details: {
|
||||
serviceName: 'producer-external-only',
|
||||
agentName: 'java',
|
||||
expect(spanELinksDetails.parentsLinks.spanLinksDetails).to.eql([
|
||||
{
|
||||
traceId: ids.producerExternalOnly.traceId,
|
||||
spanId: ids.producerExternalOnly.spanBId,
|
||||
details: {
|
||||
serviceName: 'producer-external-only',
|
||||
agentName: 'java',
|
||||
spanName: 'Span B',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
...(isDefaultPipeline && {
|
||||
transactionId: ids.producerExternalOnly.transactionBId,
|
||||
spanName: 'Span B',
|
||||
duration: 100000,
|
||||
spanSubtype: 'http',
|
||||
spanType: 'external',
|
||||
environment: 'production',
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.traceId,
|
||||
spanId: ids.producerConsumer.transactionCId,
|
||||
details: {
|
||||
serviceName: 'producer-consumer',
|
||||
agentName: 'ruby',
|
||||
transactionId: ids.producerConsumer.transactionCId,
|
||||
spanName: 'Transaction C',
|
||||
duration: 1000000,
|
||||
environment: 'production',
|
||||
},
|
||||
},
|
||||
{
|
||||
traceId: ids.producerConsumer.traceId,
|
||||
spanId: ids.producerConsumer.transactionCId,
|
||||
details: {
|
||||
serviceName: 'producer-consumer',
|
||||
agentName: 'ruby',
|
||||
transactionId: ids.producerConsumer.transactionCId,
|
||||
spanName: 'Transaction C',
|
||||
duration: 1000000,
|
||||
environment: 'production',
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns no children on Span E', () => {
|
||||
expect(spanELinksDetails.childrenLinks.spanLinksDetails.length).to.be(0);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue