From 72db6869e5adfe263f2fa11a3f8f893d542e8dd9 Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Thu, 26 Sep 2024 10:39:09 +0200 Subject: [PATCH 1/3] refactor: make timestamp.us optional and try to use @timestamp --- packages/kbn-apm-types/src/es_fields/apm.ts | 3 +- .../apm/common/waterfall/typings.ts | 3 ++ .../error_sampler/error_sample_detail.tsx | 5 ++- .../transaction_tabs.tsx | 5 ++- .../waterfall/span_flyout/index.tsx | 5 ++- .../waterfall_helpers/waterfall_helpers.ts | 36 +++++++++++++++---- .../shared/summary/transaction_summary.tsx | 4 ++- .../transaction_action_menu/sections.ts | 5 +-- .../apm/public/utils/get_timestamp.ts | 14 ++++++++ .../server/routes/traces/get_trace_items.ts | 3 ++ 10 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts diff --git a/packages/kbn-apm-types/src/es_fields/apm.ts b/packages/kbn-apm-types/src/es_fields/apm.ts index 6b0a68379f5d4..10e5b0fcf5a45 100644 --- a/packages/kbn-apm-types/src/es_fields/apm.ts +++ b/packages/kbn-apm-types/src/es_fields/apm.ts @@ -7,7 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export const TIMESTAMP = 'timestamp.us'; +export const TIMESTAMP = '@timestamp'; +export const FALLBACK_TIMESTAMP = 'timestamp.us'; export const AGENT = 'agent'; export const AGENT_NAME = 'agent.name'; export const AGENT_VERSION = 'agent.version'; diff --git a/x-pack/plugins/observability_solution/apm/common/waterfall/typings.ts b/x-pack/plugins/observability_solution/apm/common/waterfall/typings.ts index 49f282473e9dd..a6947deb21c84 100644 --- a/x-pack/plugins/observability_solution/apm/common/waterfall/typings.ts +++ b/x-pack/plugins/observability_solution/apm/common/waterfall/typings.ts @@ -12,6 +12,7 @@ import { TimestampUs } from '../../typings/es_schemas/raw/fields/timestamp_us'; import { AgentName } from '../../typings/es_schemas/ui/fields/agent'; export interface WaterfallTransaction { + '@timestamp': string; timestamp: TimestampUs; trace: { id: string }; service: { @@ -38,6 +39,7 @@ export interface WaterfallTransaction { } export interface WaterfallSpan { + '@timestamp': string; timestamp: TimestampUs; trace: { id: string }; service: { @@ -70,6 +72,7 @@ export interface WaterfallSpan { } export interface WaterfallError { + '@timestamp': string; timestamp: TimestampUs; trace?: { id: string }; transaction?: { id: string }; diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx index a38c4dfc96f63..886dc403900f5 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx @@ -29,6 +29,7 @@ import { first } from 'lodash'; import React, { useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; +import { getTimestamp } from '../../../../utils/get_timestamp'; import { ERROR_GROUP_ID } from '../../../../../common/es_fields/apm'; import { TraceSearchType } from '../../../../../common/trace_explorer'; import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; @@ -175,6 +176,8 @@ export function ErrorSampleDetails({ }, }); + const timestamp = getTimestamp(errorData?.error?.['@timestamp'], errorData?.error?.timestamp?.us); + return ( @@ -245,7 +248,7 @@ export function ErrorSampleDetails({ ) : ( , + , errorUrl && method ? ( ) : null, diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx index ead237d3d374d..beef25dd95c81 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/transaction_tabs.tsx @@ -9,6 +9,7 @@ import { EuiSpacer, EuiTab, EuiTabs, EuiSkeletonText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LogStream } from '@kbn/logs-shared-plugin/public'; import React, { useMemo } from 'react'; +import { getTimestamp } from '../../../../utils/get_timestamp'; import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { TransactionMetadata } from '../../../shared/metadata_table/transaction_metadata'; import { WaterfallContainer } from './waterfall_container'; @@ -43,6 +44,7 @@ export function TransactionTabs({ showCriticalPath, onShowCriticalPathChange, }: Props) { + const timestamp = getTimestamp(transaction?.['@timestamp'], transaction?.timestamp?.us); const tabs: Record = useMemo( () => ({ [TransactionTab.timeline]: { @@ -73,7 +75,7 @@ export function TransactionTabs({ <> {transaction && ( @@ -89,6 +91,7 @@ export function TransactionTabs({ transaction, waterfall, waterfallItemId, + timestamp, ] ); diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx index e4c9f0cf2816e..90b29591c56e0 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/span_flyout/index.tsx @@ -25,6 +25,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { isEmpty } from 'lodash'; import React, { Fragment } from 'react'; +import { getTimestamp } from '../../../../../../../utils/get_timestamp'; import { PlaintextStacktrace } from '../../../../../error_group_details/error_sampler/plaintext_stacktrace'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; @@ -261,13 +262,15 @@ function SpanFlyoutBody({ ]; const initialTab = tabs.find(({ id }) => id === flyoutDetailTab) ?? tabs[0]; + const timestamp = getTimestamp(span['@timestamp'], span.timestamp.us); + return ( <> , + , <> ; @@ -166,18 +167,23 @@ function getErrorItem( items: IWaterfallItem[], entryWaterfallTransaction?: IWaterfallTransaction ): IWaterfallError { - const entryTimestamp = entryWaterfallTransaction?.doc.timestamp.us ?? 0; + const timestamp = getTimestamp( + entryWaterfallTransaction?.doc['@timestamp'], + entryWaterfallTransaction?.doc.timestamp.us + ); + const entryTimestamp = timestamp ?? 0; const parent = items.find((waterfallItem) => waterfallItem.id === error.parent?.id) as | IWaterfallSpanOrTransaction | undefined; + const errorTimestamp = getTimestamp(error['@timestamp'], error.timestamp.us); const errorItem: IWaterfallError = { docType: 'error', doc: error, id: error.error.id, parent, parentId: parent?.id, - offset: error.timestamp.us - entryTimestamp, + offset: errorTimestamp - entryTimestamp, skew: 0, color: '', }; @@ -202,10 +208,15 @@ export function getClockSkew( return parentItem.skew; // transaction is the initial entry in a service. Calculate skew for this, and it will be propagated to all child spans case 'transaction': { - const parentStart = parentItem.doc.timestamp.us + parentItem.skew; + const parentTimestamp = getTimestamp( + parentItem.doc['@timestamp'], + parentItem.doc.timestamp.us + ); + const parentStart = parentTimestamp + parentItem.skew; // determine if child starts before the parent - const offsetStart = parentStart - item.doc.timestamp.us; + const itemTimestamp = getTimestamp(item.doc['@timestamp'], item.doc.timestamp.us); + const offsetStart = parentStart - itemTimestamp; if (offsetStart > 0) { const latency = Math.max(parentItem.duration - item.duration, 0) / 2; return offsetStart + latency; @@ -224,7 +235,11 @@ export function getOrderedWaterfallItems( if (!entryWaterfallTransaction) { return []; } - const entryTimestamp = entryWaterfallTransaction.doc.timestamp.us; + const timestamp = getTimestamp( + entryWaterfallTransaction.doc['@timestamp'], + entryWaterfallTransaction.doc.timestamp.us + ); + const entryTimestamp = timestamp; const visitedWaterfallItemSet = new Set(); function getSortedChildren( @@ -236,11 +251,18 @@ export function getOrderedWaterfallItems( } visitedWaterfallItemSet.add(item); - const children = sortBy(childrenByParentId[item.id] || [], 'doc.timestamp.us'); + const children = sortBy(childrenByParentId[item.id] || [], (child) => { + if (!child.doc.timestamp.us && child.doc['@timestamp']) { + return `doc['@timestamp']`; + } + + return 'doc.timestamp.us'; + }); item.parent = parentItem; // get offset from the beginning of trace - item.offset = item.doc.timestamp.us - entryTimestamp; + const itemTimestamp = getTimestamp(item.doc['@timestamp'], item.doc.timestamp.us); + item.offset = itemTimestamp - entryTimestamp; // move the item to the right if it starts before its parent item.skew = getClockSkew(item, parentItem); diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/summary/transaction_summary.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/summary/transaction_summary.tsx index db5ab9b4c7af5..9bba08700594f 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/summary/transaction_summary.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/summary/transaction_summary.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { getTimestamp } from '../../../utils/get_timestamp'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { Summary } from '.'; import { TimestampTooltip } from '../timestamp_tooltip'; @@ -42,8 +43,9 @@ function getTransactionResultSummaryItem(transaction: Transaction) { } function TransactionSummary({ transaction, totalDuration, errorCount, coldStartBadge }: Props) { + const timestamp = getTimestamp(transaction['@timestamp'], transaction.timestamp.us); const items = [ - , + , Date: Thu, 26 Sep 2024 13:33:47 +0200 Subject: [PATCH 2/3] refactor: keep microseconds precision --- .../apm/public/utils/get_timestamp.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts b/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts index 575b9fcc9dd55..207cba1921c56 100644 --- a/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts +++ b/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts @@ -7,7 +7,13 @@ export function getTimestamp(timestamp?: string, fallbackTimestamp?: number): number { if (!fallbackTimestamp && timestamp) { - return new Date(timestamp).getTime() * 1000; + const date = new Date(timestamp); + const milliseconds = date.getTime() * 1000; + + const microsecondsMatch = timestamp.match(/\.(\d{3})(\d{3})?Z$/); + const microseconds = microsecondsMatch ? parseInt(microsecondsMatch[2] || '000', 10) : 0; + + return milliseconds + microseconds; } return fallbackTimestamp || 0; From 678d41e684940e35d5598aa08fdfaa4bea73ae89 Mon Sep 17 00:00:00 2001 From: Sergi Romeu Date: Thu, 26 Sep 2024 16:45:08 +0200 Subject: [PATCH 3/3] refactor: update naming --- packages/kbn-apm-types/src/es_fields/apm.ts | 2 +- .../apm/public/utils/get_timestamp.ts | 6 +++--- .../apm/server/routes/traces/get_trace_items.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/kbn-apm-types/src/es_fields/apm.ts b/packages/kbn-apm-types/src/es_fields/apm.ts index 10e5b0fcf5a45..4f0b12cc4bc6b 100644 --- a/packages/kbn-apm-types/src/es_fields/apm.ts +++ b/packages/kbn-apm-types/src/es_fields/apm.ts @@ -8,7 +8,7 @@ */ export const TIMESTAMP = '@timestamp'; -export const FALLBACK_TIMESTAMP = 'timestamp.us'; +export const TIMESTAMP_US = 'timestamp.us'; export const AGENT = 'agent'; export const AGENT_NAME = 'agent.name'; export const AGENT_VERSION = 'agent.version'; diff --git a/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts b/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts index 207cba1921c56..8ee5d8801fded 100644 --- a/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts +++ b/x-pack/plugins/observability_solution/apm/public/utils/get_timestamp.ts @@ -5,8 +5,8 @@ * 2.0. */ -export function getTimestamp(timestamp?: string, fallbackTimestamp?: number): number { - if (!fallbackTimestamp && timestamp) { +export function getTimestamp(timestamp?: string, timestampUs?: number): number { + if (!timestampUs && timestamp) { const date = new Date(timestamp); const milliseconds = date.getTime() * 1000; @@ -16,5 +16,5 @@ export function getTimestamp(timestamp?: string, fallbackTimestamp?: number): nu return milliseconds + microseconds; } - return fallbackTimestamp || 0; + return timestampUs || 0; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts index ec51b0bb298e6..780882f6e0dca 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts @@ -22,7 +22,6 @@ import { ERROR_LOG_MESSAGE, EVENT_OUTCOME, FAAS_COLDSTART, - FALLBACK_TIMESTAMP, PARENT_ID, PROCESSOR_EVENT, SERVICE_ENVIRONMENT, @@ -39,6 +38,7 @@ import { SPAN_SYNC, SPAN_TYPE, TIMESTAMP, + TIMESTAMP_US, TRACE_ID, TRANSACTION_DURATION, TRANSACTION_ID, @@ -99,7 +99,7 @@ export async function getTraceItems({ size: 1000, _source: [ TIMESTAMP, - FALLBACK_TIMESTAMP, + TIMESTAMP_US, TRACE_ID, TRANSACTION_ID, PARENT_ID, @@ -231,7 +231,7 @@ async function getTraceDocsPerPage({ search_after: searchAfter, _source: [ TIMESTAMP, - FALLBACK_TIMESTAMP, + TIMESTAMP_US, TRACE_ID, PARENT_ID, SERVICE_NAME,