Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import datemath from '@elastic/datemath';
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { getFlattenedKeyValuePairs } from '@kbn/key-value-metadata-table';
import { accessKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import type { KeyValuePair } from '@kbn/key-value-metadata-table';
import { maybe } from '../../../../common/utils/maybe';
import { asMutableArray } from '../../../../common/utils/as_mutable_array';
Expand All @@ -18,6 +17,7 @@ import { PROCESSOR_EVENT, TRACE_ID } from '../../../../common/es_fields/apm';
import { getTypedSearch } from '../../../utils/create_typed_es_client';
import { getDownstreamServiceResource } from '../get_observability_alert_details_context/get_downstream_dependency_name';
import { getShouldMatchOrNotExistFilter } from '../utils/get_should_match_or_not_exist_filter';
import { asKeyValuePairs } from '../utils/as_key_value_pair';

export interface LogCategory {
errorCategory: string;
Expand Down Expand Up @@ -126,10 +126,10 @@ export async function getLogCategories({
const promises = categorizedLogsRes.aggregations?.sampling.categories?.buckets.map(
async ({ doc_count: docCount, key, sample }) => {
const hit = sample.hits.hits[0];
const event = unflattenKnownApmEventFields(hit?.fields);
const event = accessKnownApmEventFields(hit.fields);

const sampleMessage = event.message as string;
const sampleTraceId = event.trace?.id;
const sampleTraceId = event[TRACE_ID];
const errorCategory = key as string;

if (!sampleTraceId) {
Expand All @@ -147,12 +147,12 @@ export async function getLogCategories({
}
);

const event = unflattenKnownApmEventFields(maybe(categorizedLogsRes.hits.hits[0])?.fields);
const hits = maybe(categorizedLogsRes.hits.hits[0])?.fields;

const sampleDoc = event as Record<string, string>;
const event = hits && accessKnownApmEventFields(hits);

return {
logCategories: await Promise.all(promises ?? []),
entities: getFlattenedKeyValuePairs(sampleDoc),
entities: asKeyValuePairs(event),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import moment from 'moment';
import type { alertDetailsContextRt } from '@kbn/observability-plugin/server/services';
import type { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types';
import { CONTAINER_ID } from '@kbn/apm-types';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { accessKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import type { FlattenedApmEvent } from '@kbn/apm-data-access-plugin/server/utils/utility_types';
import { maybe } from '../../../../common/utils/maybe';
import { asMutableArray } from '../../../../common/utils/as_mutable_array';
import { ApmDocumentType } from '../../../../common/document_type';
Expand Down Expand Up @@ -91,9 +92,13 @@ async function getContainerIdFromLogs({
fields: requiredFields,
});

const event = unflattenKnownApmEventFields(maybe(res.hits.hits[0])?.fields, requiredFields);
const fields = maybe(res.hits.hits[0])?.fields;

return event?.container.id;
const event =
fields &&
accessKnownApmEventFields(fields as Partial<FlattenedApmEvent>).requireFields(requiredFields);

return event?.[CONTAINER_ID];
}

async function getContainerIdFromTraces({
Expand All @@ -105,6 +110,7 @@ async function getContainerIdFromTraces({
}) {
const requiredFields = asMutableArray([CONTAINER_ID] as const);
const res = await apmEventClient.search('get_container_id_from_traces', {
...params,
apm: {
sources: [
{
Expand All @@ -113,11 +119,14 @@ async function getContainerIdFromTraces({
},
],
},
...params,
fields: requiredFields,
});

const event = unflattenKnownApmEventFields(maybe(res.hits.hits[0])?.fields, requiredFields);
const fields = maybe(res.hits.hits[0])?.fields;

const event =
fields &&
accessKnownApmEventFields(fields as Partial<FlattenedApmEvent>).requireFields(requiredFields);

return event?.container.id;
return event?.[CONTAINER_ID];
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { rangeQuery } from '@kbn/observability-plugin/server';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { accessKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { asMutableArray } from '../../../../common/utils/as_mutable_array';
import { maybe } from '../../../../common/utils/maybe';
import { ApmDocumentType } from '../../../../common/document_type';
Expand Down Expand Up @@ -56,7 +56,9 @@ export async function getDownstreamServiceResource({
fields: requiredFields,
});

const event = unflattenKnownApmEventFields(maybe(response.hits.hits[0])?.fields, requiredFields);
const fields = maybe(response.hits.hits[0])?.fields;

return event?.span.destination.service.resource;
const event = fields && accessKnownApmEventFields(fields).requireFields(requiredFields);

return event?.[SPAN_DESTINATION_SERVICE_RESOURCE];
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type * as t from 'io-ts';
import moment from 'moment';
import type { alertDetailsContextRt } from '@kbn/observability-plugin/server/services';
import type { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types';
import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { accessKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils';
import { SERVICE_NAME } from '@kbn/apm-types';
import { maybe } from '../../../../common/utils/maybe';
import { asMutableArray } from '../../../../common/utils/as_mutable_array';
Expand Down Expand Up @@ -120,7 +120,9 @@ async function getServiceNameFromTraces({
fields: requiredFields,
});

const event = unflattenKnownApmEventFields(maybe(res.hits.hits[0])?.fields, requiredFields);
const fields = maybe(res.hits.hits[0])?.fields;

return event?.service.name;
const event = fields && accessKnownApmEventFields(fields).requireFields(requiredFields);

return event?.[SERVICE_NAME];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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 type { KeyValuePair } from '@kbn/key-value-metadata-table';

/**
* Sorts and maps the record into an array of `key`/`value` objects. Flattens multi-values into single value items.
*/
export function asKeyValuePairs<T extends Record<string, any>>(record?: T | null): KeyValuePair[] {
return record
? Object.keys(record)
.sort()
.flatMap((key) => {
const value = record[key];

return Array.isArray(value)
? value.map((item, index) => ({
key: value.length > 1 ? `${key}.${index}` : key,
value: item,
}))
: { key, value };
})
: [];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { asKeyValuePairs } from './as_key_value_pair';

describe('asKeyValuePairs', () => {
it('maps a record into an array of `key`:`value` objects', () => {
const data = {
'foo.item1': 'value 1',
'foo.item2.itemA': 'value 2',
'bar.item3.itemA.itemAB': 'value AB',
'bar.item4': 'value 4',
'bar.item5': [1],
'bar.item6': [1, 2, 3],
};

const flatten = asKeyValuePairs(data);
expect(flatten).toEqual([
{ key: 'bar.item3.itemA.itemAB', value: 'value AB' },
{ key: 'bar.item4', value: 'value 4' },
{ key: 'bar.item5', value: 1 },
{ key: 'bar.item6.0', value: 1 },
{ key: 'bar.item6.1', value: 2 },
{ key: 'bar.item6.2', value: 3 },
{ key: 'foo.item1', value: 'value 1' },
{ key: 'foo.item2.itemA', value: 'value 2' },
]);
});
it('returns an empty array if no valid object is provided', () => {
expect(asKeyValuePairs({})).toEqual([]);
expect(asKeyValuePairs(null)).toEqual([]);
expect(asKeyValuePairs(undefined)).toEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ describe('accessKnownApmEventFields', () => {
it('can be spread into a new flattened object with the correct value formats', () => {
const event = accessKnownApmEventFields(smallInput);

expect({ ...event }).toEqual({
const eventObj = { ...event };

expect(eventObj).toEqual({
'@timestamp': '2024-10-10T10:10:10.000Z',
'service.name': 'node-svc',
'links.span_id': ['link1', 'link2'],
Expand Down Expand Up @@ -109,4 +111,15 @@ describe('accessKnownApmEventFields', () => {
event['agent.name'] = 'nodejs';
}).toThrowError("'set' on proxy: trap returned falsish for property 'agent.name'");
});

it('lists only the keys that exist on the original object', () => {
const event = accessKnownApmEventFields(smallInput);

expect(Object.keys(event)).toEqual(['@timestamp', 'service.name', 'links.span_id']);
expect(Object.entries(event)).toEqual([
['@timestamp', '2024-10-10T10:10:10.000Z'],
['service.name', 'node-svc'],
['links.span_id', ['link1', 'link2']],
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,8 @@ const accessHandler = {
set() {
return false;
},

ownKeys(target: any) {
return Object.keys(target);
},
};