Skip to content

Commit

Permalink
[APM] Mobile most launches (#168925)
Browse files Browse the repository at this point in the history
## Summary

Enabling the "Most launches" Mobile dashboard panel which shows an
aggregation of log events that contain the attribute
`labels.lifecycle_state` set to either `created` (for Android) or
`active` (for iOS).

![Screenshot 2023-10-27 at 09 37 08
copy](https://github.com/elastic/kibana/assets/56847527/911c769c-1456-4f38-bf07-5e71b6ce5ae5)


### Checklist

Delete any items that are not applicable to this PR.

- [x] [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

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Katerina <kate@kpatticha.com>
  • Loading branch information
3 people committed Nov 6, 2023
1 parent b562839 commit 820cfc0
Show file tree
Hide file tree
Showing 17 changed files with 330 additions and 24 deletions.
3 changes: 3 additions & 0 deletions packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export type ApmUserAgentFields = Partial<{
export interface ApmException {
message: string;
}

export interface Observer {
type: string;
version: string;
Expand Down Expand Up @@ -94,6 +95,7 @@ export type ApmFields = Fields<{
'error.type': string;
'event.ingested': number;
'event.name': string;
'event.action': string;
'event.outcome': string;
'event.outcome_numeric':
| number
Expand Down Expand Up @@ -121,6 +123,7 @@ export type ApmFields = Fields<{
'kubernetes.pod.uid': string;
'labels.name': string;
'labels.telemetry_auto_version': string;
'labels.lifecycle_state': string;
'metricset.name': string;
'network.carrier.icc': string;
'network.carrier.mcc': string;
Expand Down
30 changes: 30 additions & 0 deletions packages/kbn-apm-synthtrace-client/src/lib/apm/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { ApmFields } from './apm_fields';
import { Serializable } from '../serializable';

export class Event extends Serializable<ApmFields> {
constructor(fields: ApmFields) {
super({
...fields,
});
}

lifecycle(state: string): this {
this.fields['event.action'] = 'lifecycle';
this.fields['labels.lifecycle_state'] = state;
return this;
}

override timestamp(timestamp: number) {
const ret = super.timestamp(timestamp);
this.fields['timestamp.us'] = timestamp * 1000;
return ret;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import { Entity } from '../entity';
import { Span } from './span';
import { Transaction } from './transaction';
import { ApmFields, SpanParams, GeoLocation, ApmApplicationMetricFields } from './apm_fields';
import { Event } from './event';
import { ApmApplicationMetricFields, ApmFields, GeoLocation, SpanParams } from './apm_fields';
import { generateLongId } from '../utils/generate_id';
import { Metricset } from './metricset';
import { ApmError } from './apm_error';
Expand Down Expand Up @@ -143,6 +144,10 @@ export class MobileDevice extends Entity<ApmFields> {
return this;
}

event(): Event {
return new Event({ ...this.fields });
}

transaction(
...options:
| [{ transactionName: string; frameworkName?: string; frameworkVersion?: string }]
Expand Down
29 changes: 28 additions & 1 deletion packages/kbn-apm-synthtrace-client/src/lib/apm/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import { ApmError } from './apm_error';
import { Event } from './event';
import { BaseSpan } from './base_span';
import { generateShortId } from '../utils/generate_id';
import { ApmFields } from './apm_fields';
Expand All @@ -15,6 +16,7 @@ import { getBreakdownMetrics } from './processors/get_breakdown_metrics';
export class Transaction extends BaseSpan {
private _sampled: boolean = true;
private readonly _errors: ApmError[] = [];
private readonly _events: Event[] = [];

constructor(fields: ApmFields) {
super({
Expand All @@ -35,6 +37,27 @@ export class Transaction extends BaseSpan {
error.fields['transaction.sampled'] = this.fields['transaction.sampled'];
});

this._events.forEach((event) => {
event.fields['trace.id'] = this.fields['trace.id'];
event.fields['transaction.id'] = this.fields['transaction.id'];
event.fields['transaction.type'] = this.fields['transaction.type'];
event.fields['transaction.sampled'] = this.fields['transaction.sampled'];
});

return this;
}

events(...events: Event[]) {
events.forEach((event) => {
event.fields['trace.id'] = this.fields['trace.id'];
event.fields['transaction.id'] = this.fields['transaction.id'];
event.fields['transaction.name'] = this.fields['transaction.name'];
event.fields['transaction.type'] = this.fields['transaction.type'];
event.fields['transaction.sampled'] = this.fields['transaction.sampled'];
});

this._events.push(...events);

return this;
}

Expand Down Expand Up @@ -62,13 +85,17 @@ export class Transaction extends BaseSpan {
this._errors.forEach((error) => {
error.fields['transaction.sampled'] = sampled;
});
this._events.forEach((event) => {
event.fields['transaction.sampled'] = sampled;
});
return this;
}

serialize() {
const [transaction, ...spans] = super.serialize();

const errors = this._errors.flatMap((error) => error.serialize());
const logEvents = this._events.flatMap((event) => event.serialize());

const directChildren = this.getChildren().map((child) => child.fields);

Expand All @@ -80,6 +107,6 @@ export class Transaction extends BaseSpan {
events.push(...spans);
}

return events.concat(errors).concat(breakdownMetrics);
return events.concat(errors).concat(breakdownMetrics).concat(logEvents);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export function getRoutingTransform() {
index = `metrics-apm.internal-${namespace}`;
}
break;
default:
if (document['event.action'] != null) {
index = `logs-apm.app-${namespace}`;
}
break;
}

if (!index) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions x-pack/plugins/apm/common/es_fields/apm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export const LABEL_NAME = 'labels.name';
export const LABEL_GC = 'labels.gc';
export const LABEL_TYPE = 'labels.type';
export const LABEL_TELEMETRY_AUTO_VERSION = 'labels.telemetry_auto_version';
export const LABEL_LIFECYCLE_STATE = 'labels.lifecycle_state';

export const HOST = 'host';
export const HOST_HOSTNAME = 'host.hostname'; // Do not use. Please use `HOST_NAME` instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,17 +163,18 @@ export function MobileLocationStats({
trendShape: MetricTrendShape.Area,
},
{
color: euiTheme.eui.euiColorDisabled,
color: euiTheme.eui.euiColorLightestShade,
title: i18n.translate('xpack.apm.mobile.location.metrics.launches', {
defaultMessage: 'Most launches',
}),
subtitle: i18n.translate('xpack.apm.mobile.coming.soon', {
defaultMessage: 'Coming Soon',
extra: getComparisonValueFormatter({
currentPeriodValue: currentPeriod?.mostLaunches.value,
previousPeriodValue: previousPeriod?.mostLaunches.value,
}),
icon: getIcon('launch'),
value: NOT_AVAILABLE_LABEL,
value: currentPeriod?.mostLaunches.location ?? NOT_AVAILABLE_LABEL,
valueFormatter: (value) => `${value}`,
trend: [],
trend: currentPeriod?.mostLaunches.timeseries,
trendShape: MetricTrendShape.Area,
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import type { APMIndices } from '@kbn/apm-data-access-plugin/server';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import { uniq } from 'lodash';
import { ApmDataSource } from '../../../../../common/data_source';
import {} from '../../../../../common/document_type';
import { PROCESSOR_EVENT } from '../../../../../common/es_fields/apm';
import {
getConfigForDocumentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import type {
FieldCapsResponse,
MsearchMultisearchBody,
MsearchMultisearchHeader,
TermsEnumResponse,
TermsEnumRequest,
TermsEnumResponse,
} from '@elastic/elasticsearch/lib/api/types';
import { ElasticsearchClient, KibanaRequest } from '@kbn/core/server';
import type { ESSearchRequest, InferSearchResponseOf } from '@kbn/es-types';
Expand All @@ -26,6 +26,7 @@ import { APMError } from '../../../../../typings/es_schemas/ui/apm_error';
import { Metric } from '../../../../../typings/es_schemas/ui/metric';
import { Span } from '../../../../../typings/es_schemas/ui/span';
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
import { Event } from '../../../../../typings/es_schemas/ui/event';
import { withApmSpan } from '../../../../utils/with_apm_span';
import {
callAsyncWithDebug,
Expand All @@ -46,6 +47,13 @@ export type APMEventESSearchRequest = Omit<ESSearchRequest, 'index'> & {
};
};

export type APMLogEventESSearchRequest = Omit<ESSearchRequest, 'index'> & {
body: {
size: number;
track_total_hits: boolean | number;
};
};

type APMEventWrapper<T> = Omit<T, 'index'> & {
apm: { events: ProcessorEvent[] };
};
Expand All @@ -63,6 +71,9 @@ type TypeOfProcessorEvent<T extends ProcessorEvent> = {
metric: Metric;
}[T];

type TypedLogEventSearchResponse<TParams extends APMLogEventESSearchRequest> =
InferSearchResponseOf<Event, TParams>;

type TypedSearchResponse<TParams extends APMEventESSearchRequest> =
InferSearchResponseOf<
TypeOfProcessorEvent<
Expand Down Expand Up @@ -196,6 +207,41 @@ export class APMEventClient {
});
}

async logEventSearch<TParams extends APMLogEventESSearchRequest>(
operationName: string,
params: TParams
): Promise<TypedLogEventSearchResponse<TParams>> {
// Reusing indices configured for errors since both events and errors are stored as logs.
const index = processorEventsToIndex([ProcessorEvent.error], this.indices);

const searchParams = {
...omit(params, 'body'),
index,
body: {
...params.body,
query: {
bool: {
must: compact([params.body.query]),
},
},
},
...(this.includeFrozen ? { ignore_throttled: false } : {}),
ignore_unavailable: true,
preference: 'any',
expand_wildcards: ['open' as const, 'hidden' as const],
};

return this.callAsyncWithDebug({
cb: (opts) =>
this.esClient.search(searchParams, opts) as unknown as Promise<{
body: TypedLogEventSearchResponse<TParams>;
}>,
operationName,
params: searchParams,
requestType: 'search',
});
}

async msearch<TParams extends APMEventESSearchRequest>(
operationName: string,
...allParams: TParams[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
rangeQuery,
termQuery,
} from '@kbn/observability-plugin/server';
import { SERVICE_NAME, ERROR_TYPE } from '../../../common/es_fields/apm';
import { ERROR_TYPE, SERVICE_NAME } from '../../../common/es_fields/apm';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms';
import { getBucketSize } from '../../../common/utils/get_bucket_size';
Expand Down Expand Up @@ -101,9 +101,7 @@ export async function getCrashesByLocation({
timeseries:
response.aggregations?.timeseries?.buckets.map((bucket) => ({
x: bucket.key,
y:
response.aggregations?.crashes?.crashesByLocation?.buckets[0]
?.doc_count ?? 0,
y: bucket?.crashes?.crashesByLocation?.buckets[0]?.doc_count ?? 0,
})) ?? [],
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
*/

import {
termQuery,
kqlQuery,
rangeQuery,
termQuery,
} from '@kbn/observability-plugin/server';
import { ProcessorEvent } from '@kbn/observability-plugin/common';
import {
SERVICE_NAME,
SPAN_TYPE,
SPAN_SUBTYPE,
SPAN_TYPE,
} from '../../../common/es_fields/apm';
import { environmentQuery } from '../../../common/utils/environment_query';
import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client';
Expand Down Expand Up @@ -111,9 +111,7 @@ export async function getHttpRequestsByLocation({
timeseries:
response.aggregations?.timeseries?.buckets.map((bucket) => ({
x: bucket.key,
y:
response.aggregations?.requests?.requestsByLocation?.buckets[0]
?.doc_count ?? 0,
y: bucket?.requests?.requestsByLocation?.buckets[0]?.doc_count ?? 0,
})) ?? [],
};
}
Loading

0 comments on commit 820cfc0

Please sign in to comment.