[APM] Fix query for transaction marks (#215819)

## Summary

There is a bug in kibana 8.17, where no transaction marks are shown in
the APM's transaction waterfall ui.
The marks are stored in the field `transaction.marks.agent` of
documents, but kibana apm server is querying `transaction.agent.marks`.

This PR fixes the field name.


I also added `span.id` in the query source to include the marks in the
response, even if there is no `span.links` in the transaction info.
(I found the case from RUM data with `transaction.marks.agent` but
without `span.links`, so that the response does not include marks
because there is no `source` field in the query result)

I am not sure if it's the right way to fix it, as i have no
understanding about the relationsip between `transaction.marks.agent`
and `span.links`, so this PR is more like a bug report.



### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

Does this PR introduce any risks? For example, consider risks like hard
to test bugs, performance regression, potential of data loss.

Describe the risk, its severity, and mitigation for each identified
risk. Invite stakeholders and evaluate how to proceed before merging.

- [ ] [See some risk
examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx)
- [ ] ...
None
This commit is contained in:
Sunghyun Kim 2025-04-02 00:05:42 +09:00 committed by GitHub
parent 1548f32afd
commit d4d1c2b6dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 420 additions and 14 deletions

View file

@ -5120,13 +5120,13 @@
},
{
"parentPluginId": "@kbn/apm-types",
"id": "def-common.TRANSACTION_AGENT_MARKS",
"id": "def-common.TRANSACTION_MARKS_AGENT",
"type": "string",
"tags": [],
"label": "TRANSACTION_AGENT_MARKS",
"label": "TRANSACTION_MARKS_AGENT",
"description": [],
"signature": [
"\"transaction.agent.marks\""
"\"transaction.marks.agent\""
],
"path": "x-pack/platform/packages/shared/kbn-apm-types/src/es_fields/apm.ts",
"deprecated": false,
@ -5451,4 +5451,4 @@
],
"objects": []
}
}
}

View file

@ -57,7 +57,7 @@ export const OBSERVER_LISTENING = 'observer.listening';
export const PROCESSOR_EVENT = 'processor.event';
export const PROCESSOR_NAME = 'processor.name';
export const TRANSACTION_AGENT_MARKS = 'transaction.agent.marks';
export const TRANSACTION_MARKS_AGENT = 'transaction.marks.agent';
export const TRANSACTION_DURATION = 'transaction.duration.us';
export const TRANSACTION_DURATION_HISTOGRAM = 'transaction.duration.histogram';
export const TRANSACTION_DURATION_SUMMARY = 'transaction.duration.summary';

View file

@ -0,0 +1,406 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`transaction queries fetches a transaction 1`] = `
Object {
"apm": Object {
"sources": Array [
Object {
"documentType": "transactionEvent",
"rollupInterval": "none",
},
],
},
"body": Object {
"_source": Array [
"span.links",
"transaction.marks.agent",
],
"fields": Array [
"trace.id",
"agent.name",
"processor.event",
"@timestamp",
"timestamp.us",
"service.name",
"transaction.id",
"transaction.duration.us",
"transaction.name",
"transaction.sampled",
"transaction.type",
"processor.name",
"service.language.name",
"url.full",
"transaction.page.url",
"http.response.status_code",
"http.request.method",
"user_agent.name",
],
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"transaction.id": "foo",
},
},
Object {
"term": Object {
"trace.id": "bar",
},
},
Object {
"range": Object {
"@timestamp": Object {
"format": "epoch_millis",
"gte": 0,
"lte": 50000,
},
},
},
],
},
},
"size": 1,
"terminate_after": 1,
"track_total_hits": false,
},
}
`;
exports[`transaction queries fetches breakdown data for transactions 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"by_date": Object {
"aggs": Object {
"sum_all_self_times": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
"types": Object {
"aggs": Object {
"subtypes": Object {
"aggs": Object {
"total_self_time_per_subtype": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
},
"terms": Object {
"field": "span.subtype",
"missing": "",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"terms": Object {
"field": "span.type",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"date_histogram": Object {
"extended_bounds": Object {
"max": 50000,
"min": 0,
},
"field": "@timestamp",
"fixed_interval": "30s",
"min_doc_count": 0,
},
},
"sum_all_self_times": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
"types": Object {
"aggs": Object {
"subtypes": Object {
"aggs": Object {
"total_self_time_per_subtype": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
},
"terms": Object {
"field": "span.subtype",
"missing": "",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"terms": Object {
"field": "span.type",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"transaction.type": "bar",
},
},
Object {
"range": Object {
"@timestamp": Object {
"format": "epoch_millis",
"gte": 0,
"lte": 50000,
},
},
},
Object {
"exists": Object {
"field": "span.self_time.sum.us",
},
},
],
},
},
"size": 0,
"track_total_hits": false,
},
}
`;
exports[`transaction queries fetches breakdown data for transactions for a transaction name 1`] = `
Object {
"apm": Object {
"events": Array [
"metric",
],
},
"body": Object {
"aggs": Object {
"by_date": Object {
"aggs": Object {
"sum_all_self_times": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
"types": Object {
"aggs": Object {
"subtypes": Object {
"aggs": Object {
"total_self_time_per_subtype": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
},
"terms": Object {
"field": "span.subtype",
"missing": "",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"terms": Object {
"field": "span.type",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"date_histogram": Object {
"extended_bounds": Object {
"max": 50000,
"min": 0,
},
"field": "@timestamp",
"fixed_interval": "30s",
"min_doc_count": 0,
},
},
"sum_all_self_times": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
"types": Object {
"aggs": Object {
"subtypes": Object {
"aggs": Object {
"total_self_time_per_subtype": Object {
"sum": Object {
"field": "span.self_time.sum.us",
},
},
},
"terms": Object {
"field": "span.subtype",
"missing": "",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"terms": Object {
"field": "span.type",
"order": Object {
"_count": "desc",
},
"size": 20,
},
},
},
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"transaction.type": "bar",
},
},
Object {
"range": Object {
"@timestamp": Object {
"format": "epoch_millis",
"gte": 0,
"lte": 50000,
},
},
},
Object {
"exists": Object {
"field": "span.self_time.sum.us",
},
},
Object {
"term": Object {
"transaction.name": "baz",
},
},
],
},
},
"size": 0,
"track_total_hits": false,
},
}
`;
exports[`transaction queries fetches transaction trace samples 1`] = `
Object {
"_source": Array [
"transaction.id",
"trace.id",
"@timestamp",
],
"apm": Object {
"events": Array [
"transaction",
],
},
"body": Object {
"fields": Array [
"transaction.id",
"trace.id",
"@timestamp",
],
"query": Object {
"bool": Object {
"filter": Array [
Object {
"term": Object {
"service.name": "foo",
},
},
Object {
"term": Object {
"transaction.type": "baz",
},
},
Object {
"term": Object {
"transaction.name": "bar",
},
},
Object {
"range": Object {
"@timestamp": Object {
"format": "epoch_millis",
"gte": 0,
"lte": 50000,
},
},
},
Object {
"term": Object {
"transaction.sampled": true,
},
},
],
"should": Array [
Object {
"term": Object {
"trace.id": "qux",
},
},
Object {
"term": Object {
"transaction.id": "quz",
},
},
],
},
},
"size": 500,
"sort": Array [
Object {
"_score": Object {
"order": "desc",
},
},
Object {
"@timestamp": Object {
"order": "desc",
},
},
],
"track_total_hits": false,
},
}
`;

View file

@ -344,7 +344,7 @@ exports[`Error TIMESTAMP_US 1`] = `1337`;
exports[`Error TRACE_ID 1`] = `"trace id"`;
exports[`Error TRANSACTION_AGENT_MARKS 1`] = `undefined`;
exports[`Error TRANSACTION_MARKS_AGENT 1`] = `undefined`;
exports[`Error TRANSACTION_DURATION 1`] = `undefined`;
@ -719,7 +719,7 @@ exports[`Span TIMESTAMP_US 1`] = `1337`;
exports[`Span TRACE_ID 1`] = `"trace id"`;
exports[`Span TRANSACTION_AGENT_MARKS 1`] = `undefined`;
exports[`Span TRANSACTION_MARKS_AGENT 1`] = `undefined`;
exports[`Span TRANSACTION_DURATION 1`] = `undefined`;
@ -1112,7 +1112,7 @@ exports[`Transaction TIMESTAMP_US 1`] = `1337`;
exports[`Transaction TRACE_ID 1`] = `"trace id"`;
exports[`Transaction TRANSACTION_AGENT_MARKS 1`] = `undefined`;
exports[`Transaction TRANSACTION_MARKS_AGENT 1`] = `undefined`;
exports[`Transaction TRANSACTION_DURATION 1`] = `1337`;

View file

@ -4,7 +4,7 @@ exports[`transaction queries fetches a transaction 1`] = `
Object {
"_source": Array [
"span.links",
"transaction.agent.marks",
"transaction.marks.agent",
],
"apm": Object {
"sources": Array [

View file

@ -23,7 +23,7 @@ import {
AT_TIMESTAMP,
PROCESSOR_NAME,
SPAN_LINKS,
TRANSACTION_AGENT_MARKS,
TRANSACTION_MARKS_AGENT,
SERVICE_LANGUAGE_NAME,
URL_FULL,
HTTP_REQUEST_METHOD,
@ -105,7 +105,7 @@ export async function getTransaction({
},
},
fields: [...requiredFields, ...optionalFields],
_source: [SPAN_LINKS, TRANSACTION_AGENT_MARKS],
_source: [SPAN_LINKS, TRANSACTION_MARKS_AGENT],
});
const hit = maybe(resp.hits.hits[0]);
@ -117,9 +117,9 @@ export async function getTransaction({
const event = unflattenKnownApmEventFields(hit.fields, requiredFields);
const source =
'span' in hit._source && 'transaction' in hit._source
'span' in hit._source || 'transaction' in hit._source
? (hit._source as {
transaction: Pick<Required<Transaction>['transaction'], 'marks'>;
transaction?: Pick<Required<Transaction>['transaction'], 'marks'>;
span?: Pick<Required<Transaction>['span'], 'links'>;
})
: undefined;
@ -128,7 +128,7 @@ export async function getTransaction({
...event,
transaction: {
...event.transaction,
marks: source?.transaction.marks,
marks: source?.transaction?.marks,
},
processor: {
name: 'transaction',