Skip to content

Commit

Permalink
[APM] Show Universal Profiling data on transaction details page (elas…
Browse files Browse the repository at this point in the history
…tic#176922)

- For Java agent services we show the flamegraph/functions based on APM
data.
- Use APM kql bar for java/otel-java agents
- For non-java agent services we show the flamegraph/functions based on
Profling data.
- Use Profiling kql bar for non-java/otel-java agents
- On the transaction details page we show the flamegraph/functions based
on APM data filtering by transaction.name.
- Hide Transaction Universal Profiling tab for non-java/otel-java
agents.
- `apmEnableTransactionProfiling` Feature flag, enabled by default.


https://github.com/elastic/kibana/assets/55978943/0df83f11-6adf-4942-bf03-93c2779a1d97
  • Loading branch information
cauemarcondes authored Mar 12, 2024
1 parent 3c2956c commit a84b420
Show file tree
Hide file tree
Showing 19 changed files with 676 additions and 491 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,36 @@
*/

import {
EuiButton,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
EuiLoadingSpinner,
EuiSpacer,
EuiTabbedContent,
EuiTabbedContentProps,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import {
EmbeddableProfilingSearchBar,
ProfilingEmptyState,
} from '@kbn/observability-shared-plugin/public';
import React, { useMemo } from 'react';
import { useHistory } from 'react-router-dom';
import { isJavaAgentName as getIsJavaAgentName } from '../../../../common/agent_name';
import { ApmDocumentType } from '../../../../common/document_type';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { useApmParams } from '../../../hooks/use_apm_params';
import { useLocalStorage } from '../../../hooks/use_local_storage';
import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferred_data_source_and_bucket_size';
import { useProfilingPlugin } from '../../../hooks/use_profiling_plugin';
import { useTimeRange } from '../../../hooks/use_time_range';
import { ApmPluginStartDeps } from '../../../plugin';
import { push } from '../../shared/links/url_helpers';
import { ProfilingFlamegraph } from './profiling_flamegraph';
import { ProfilingTopNFunctions } from './profiling_top_functions';
import { ProfilingFlamegraph } from '../../shared/profiling/flamegraph';
import { ProfilingTopNFunctions } from '../../shared/profiling/top_functions';
import { SearchBar } from '../../shared/search_bar/search_bar';
import { ProfilingHostsCallout } from './profiling_hosts_callout';
import { ProfilingHostsFlamegraph } from './profiling_hosts_flamegraph';
import { ProfilingHostsTopNFunctions } from './profiling_hosts_top_functions';

export function ProfilingOverview() {
const history = useHistory();
const { services } = useKibana<ApmPluginStartDeps>();
const {
path: { serviceName },
query: { rangeFrom, rangeTo, environment, kuery },
Expand All @@ -53,13 +50,8 @@ export function ProfilingOverview() {
numBuckets: 20,
});

const [
apmUniversalProfilingShowCallout,
setAPMUniversalProfilingShowCallout,
] = useLocalStorage('apmUniversalProfilingShowCallout', true);

const baseUrl =
services.docLinks?.ELASTIC_WEBSITE_URL || 'https://www.elastic.co/';
const { agentName, transactionType } = useApmServiceContext();
const isJavaAgent = getIsJavaAgentName(agentName);

const tabs = useMemo((): EuiTabbedContentProps['tabs'] => {
return [
Expand All @@ -71,16 +63,27 @@ export function ProfilingOverview() {
content: (
<>
<EuiSpacer />
<ProfilingFlamegraph
serviceName={serviceName}
start={start}
end={end}
environment={environment}
dataSource={preferred?.source}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
/>
{isJavaAgent ? (
<ProfilingFlamegraph
serviceName={serviceName}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
environment={environment}
transactionType={transactionType}
/>
) : (
<ProfilingHostsFlamegraph
serviceName={serviceName}
start={start}
end={end}
environment={environment}
dataSource={preferred?.source}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
/>
)}
</>
),
},
Expand All @@ -92,31 +95,44 @@ export function ProfilingOverview() {
content: (
<>
<EuiSpacer />
<ProfilingTopNFunctions
serviceName={serviceName}
start={start}
end={end}
environment={environment}
startIndex={0}
endIndex={10}
dataSource={preferred?.source}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
/>
{isJavaAgent ? (
<ProfilingTopNFunctions
serviceName={serviceName}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
environment={environment}
transactionType={transactionType}
/>
) : (
<ProfilingHostsTopNFunctions
serviceName={serviceName}
start={start}
end={end}
environment={environment}
startIndex={0}
endIndex={10}
dataSource={preferred?.source}
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
/>
)}
</>
),
},
];
}, [
end,
environment,
isJavaAgent,
kuery,
preferred?.source,
rangeFrom,
rangeTo,
serviceName,
start,
transactionType,
]);

if (isLoading) {
Expand All @@ -138,68 +154,29 @@ export function ProfilingOverview() {

return (
<>
{apmUniversalProfilingShowCallout && (
{isJavaAgent ? (
<SearchBar showTransactionTypeSelector />
) : (
<>
<EuiCallOut
title={i18n.translate('xpack.apm.profiling.callout.title', {
defaultMessage:
'Displaying profiling insights from the host(s) running {serviceName} services',
values: { serviceName },
})}
color="primary"
iconType="iInCircle"
>
<p>
{i18n.translate('xpack.apm.profiling.callout.description', {
defaultMessage:
'Universal Profiling provides unprecedented code visibility into the runtime behaviour of all applications. It profiles every line of code on the host(s) running your services, including not only your application code but also the kernel and third-party libraries.',
})}
</p>
<EuiFlexGroup direction="row">
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
<EuiLink
href={`${baseUrl}observability/universal-profiling`}
target="_blank"
data-test-subj="apmProfilingOverviewLearnMoreLink"
>
{i18n.translate('xpack.apm.profiling.callout.learnMore', {
defaultMessage: 'Learn more',
})}
</EuiLink>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="apmProfilingOverviewLinkButtonButton"
color="primary"
onClick={() => {
setAPMUniversalProfilingShowCallout(false);
}}
>
{i18n.translate('xpack.apm.profiling.callout.dismiss', {
defaultMessage: 'Dismiss',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiCallOut>
<ProfilingHostsCallout serviceName={serviceName} />
<EuiSpacer />
<EmbeddableProfilingSearchBar
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
onQuerySubmit={(next) => {
push(history, {
query: {
kuery: next.query,
rangeFrom: next.dateRange.from,
rangeTo: next.dateRange.to,
},
});
}}
onRefresh={refreshTimeRange}
/>
</>
)}
<EmbeddableProfilingSearchBar
kuery={kuery}
rangeFrom={rangeFrom}
rangeTo={rangeTo}
onQuerySubmit={(next) => {
push(history, {
query: {
kuery: next.query,
rangeFrom: next.dateRange.from,
rangeTo: next.dateRange.to,
},
});
}}
onRefresh={refreshTimeRange}
/>
<EuiSpacer />
<EuiTabbedContent
tabs={tabs}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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 {
EuiButton,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiLink,
} from '@elastic/eui';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { ApmPluginStartDeps } from '../../../plugin';
import { useLocalStorage } from '../../../hooks/use_local_storage';

interface Props {
serviceName: string;
}

export function ProfilingHostsCallout({ serviceName }: Props) {
const { services } = useKibana<ApmPluginStartDeps>();

const baseUrl =
services.docLinks?.ELASTIC_WEBSITE_URL || 'https://www.elastic.co/';

const [
apmUniversalProfilingShowCallout,
setAPMUniversalProfilingShowCallout,
] = useLocalStorage('apmUniversalProfilingShowCallout', true);

if (apmUniversalProfilingShowCallout === false) {
return null;
}

return (
<EuiCallOut
title={i18n.translate('xpack.apm.profiling.callout.title', {
defaultMessage:
'Displaying profiling insights from the host(s) running {serviceName} services',
values: { serviceName },
})}
color="primary"
iconType="iInCircle"
>
<p>
{i18n.translate('xpack.apm.profiling.callout.description', {
defaultMessage:
'Universal Profiling provides unprecedented code visibility into the runtime behaviour of all applications. It profiles every line of code on the host(s) running your services, including not only your application code but also the kernel and third-party libraries.',
})}
</p>
<EuiFlexGroup direction="row">
<EuiFlexItem grow={false} style={{ justifyContent: 'center' }}>
<EuiLink
href={`${baseUrl}observability/universal-profiling`}
target="_blank"
data-test-subj="apmProfilingOverviewLearnMoreLink"
>
{i18n.translate('xpack.apm.profiling.callout.learnMore', {
defaultMessage: 'Learn more',
})}
</EuiLink>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="apmProfilingOverviewLinkButtonButton"
color="primary"
onClick={() => setAPMUniversalProfilingShowCallout(false)}
>
{i18n.translate('xpack.apm.profiling.callout.dismiss', {
defaultMessage: 'Dismiss',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiCallOut>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
toKueryFilterFormat,
} from '../../../../common/utils/kuery_utils';
import { useFetcher } from '../../../hooks/use_fetcher';
import { ProfilingFlamegraphChart } from '../../shared/profiling/flamegraph';
import { FlamegraphChart } from '../../shared/charts/flamegraph';
import { ProfilingFlamegraphLink } from '../../shared/profiling/flamegraph/flamegraph_link';
import { HostnamesFilterWarning } from './host_names_filter_warning';

Expand All @@ -32,7 +32,7 @@ interface Props {
rangeTo: string;
}

export function ProfilingFlamegraph({
export function ProfilingHostsFlamegraph({
start,
end,
serviceName,
Expand All @@ -46,7 +46,7 @@ export function ProfilingFlamegraph({
(callApmApi) => {
if (dataSource) {
return callApmApi(
'GET /internal/apm/services/{serviceName}/profiling/flamegraph',
'GET /internal/apm/services/{serviceName}/profiling/hosts/flamegraph',
{
params: {
path: { serviceName },
Expand Down Expand Up @@ -87,7 +87,7 @@ export function ProfilingFlamegraph({
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<ProfilingFlamegraphChart data={data?.flamegraph} status={status} />
<FlamegraphChart data={data?.flamegraph} status={status} />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ interface Props {
rangeTo: string;
}

export function ProfilingTopNFunctions({
export function ProfilingHostsTopNFunctions({
serviceName,
start,
end,
Expand All @@ -50,7 +50,7 @@ export function ProfilingTopNFunctions({
(callApmApi) => {
if (dataSource) {
return callApmApi(
'GET /internal/apm/services/{serviceName}/profiling/functions',
'GET /internal/apm/services/{serviceName}/profiling/hosts/functions',
{
params: {
path: { serviceName },
Expand Down
Loading

0 comments on commit a84b420

Please sign in to comment.