mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[One Discover] Display stacktrace in the logs overview tab (#204521)](https://github.com/elastic/kibana/pull/204521) <!--- Backport version: 9.6.4 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Giorgos Bamparopoulos","email":"georgios.bamparopoulos@elastic.co"},"sourceCommit":{"committedDate":"2025-01-22T16:06:14Z","message":"[One Discover] Display stacktrace in the logs overview tab (#204521)\n\n## 📓 Summary\r\nAdds a new section to the overview tab in the log details flyout in\r\nDiscover to display stacktrace information for logs and exceptions.\r\n\r\nIn a follow-up, the stacktrace could be moved to a new tab in the log\r\ndetails flyout and actions can be added to the stacktrace (and quality)\r\nicons in the document table to open the relevant sections in the flyout.\r\n\r\nCloses https://github.com/elastic/kibana/issues/190460\r\n\r\n### APM - Log stacktrace (library frames)\r\n<img width=\"1470\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8991f882-d329-4bc5-aa37-424576bcee72\"\r\n/>\r\n\r\n### APM - Exception (with cause)\r\n<img width=\"1476\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/cfbf24a7-6f82-48f1-b275-5aac977411ac\"\r\n/>\r\n\r\n### APM - Exception (simple stacktrace)\r\n<img width=\"1474\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/fc0306c4-5fcd-4b74-bb0d-c1784a48d677\"\r\n/>\r\n\r\n### Apache Tomcat Integration (Catalina) - Stacktrace\r\n<img width=\"1472\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/281f1822-faea-4e2d-9515-c11a9ee12f50\"\r\n/>\r\n\r\n## 📝 Notes for reviewers\r\n- The `@kbn/apm-types` package was marked as platform / shared as it's\r\nbeing used by the\r\n[unified_doc_viewer](https://github.com/elastic/kibana/blob/main/src/plugins/unified_doc_viewer/kibana.jsonc)\r\n- The code used to render stacktraces in APM was moved into a new\r\n`@kbn/event-stacktrace` package as it is reused in the\r\n`unified_doc_viewer`\r\n- The code used to render metadata table in APM was moved into a new\r\n`@kbn/key-value-metadata-table` package\r\n\r\n## 🧪 Testing instructions\r\nThe deployed environments have sample logs that can be used (time range:\r\nJan 1, 2025 - now). For a local setup, please follow the instructions\r\nbelow:\r\n\r\n1. Ingest sample logs with stacktraces\r\n([gist](https://gist.github.com/gbamparop/0da21ca7f65b24c4a9c071ce9e9b97b0)).\r\nPlease note that these are test data and some fields that are not used\r\nby stacktraces might not be consistent\r\n2. View relevant logs in Discover (Query: `service.name: \"synth-node-0\"\r\nOR apache_tomcat :*`, Time range: Jan 1, 2025 - now)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"368475e8e55845e17fd4621c1ae60ba1e983bb8f","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","v9.0.0","ci:project-deploy-observability","Team:obs-ux-infra_services","backport:version","v8.18.0"],"title":"[One Discover] Display stacktrace in the logs overview tab","number":204521,"url":"https://github.com/elastic/kibana/pull/204521","mergeCommit":{"message":"[One Discover] Display stacktrace in the logs overview tab (#204521)\n\n## 📓 Summary\r\nAdds a new section to the overview tab in the log details flyout in\r\nDiscover to display stacktrace information for logs and exceptions.\r\n\r\nIn a follow-up, the stacktrace could be moved to a new tab in the log\r\ndetails flyout and actions can be added to the stacktrace (and quality)\r\nicons in the document table to open the relevant sections in the flyout.\r\n\r\nCloses https://github.com/elastic/kibana/issues/190460\r\n\r\n### APM - Log stacktrace (library frames)\r\n<img width=\"1470\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8991f882-d329-4bc5-aa37-424576bcee72\"\r\n/>\r\n\r\n### APM - Exception (with cause)\r\n<img width=\"1476\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/cfbf24a7-6f82-48f1-b275-5aac977411ac\"\r\n/>\r\n\r\n### APM - Exception (simple stacktrace)\r\n<img width=\"1474\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/fc0306c4-5fcd-4b74-bb0d-c1784a48d677\"\r\n/>\r\n\r\n### Apache Tomcat Integration (Catalina) - Stacktrace\r\n<img width=\"1472\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/281f1822-faea-4e2d-9515-c11a9ee12f50\"\r\n/>\r\n\r\n## 📝 Notes for reviewers\r\n- The `@kbn/apm-types` package was marked as platform / shared as it's\r\nbeing used by the\r\n[unified_doc_viewer](https://github.com/elastic/kibana/blob/main/src/plugins/unified_doc_viewer/kibana.jsonc)\r\n- The code used to render stacktraces in APM was moved into a new\r\n`@kbn/event-stacktrace` package as it is reused in the\r\n`unified_doc_viewer`\r\n- The code used to render metadata table in APM was moved into a new\r\n`@kbn/key-value-metadata-table` package\r\n\r\n## 🧪 Testing instructions\r\nThe deployed environments have sample logs that can be used (time range:\r\nJan 1, 2025 - now). For a local setup, please follow the instructions\r\nbelow:\r\n\r\n1. Ingest sample logs with stacktraces\r\n([gist](https://gist.github.com/gbamparop/0da21ca7f65b24c4a9c071ce9e9b97b0)).\r\nPlease note that these are test data and some fields that are not used\r\nby stacktraces might not be consistent\r\n2. View relevant logs in Discover (Query: `service.name: \"synth-node-0\"\r\nOR apache_tomcat :*`, Time range: Jan 1, 2025 - now)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"368475e8e55845e17fd4621c1ae60ba1e983bb8f"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/204521","number":204521,"mergeCommit":{"message":"[One Discover] Display stacktrace in the logs overview tab (#204521)\n\n## 📓 Summary\r\nAdds a new section to the overview tab in the log details flyout in\r\nDiscover to display stacktrace information for logs and exceptions.\r\n\r\nIn a follow-up, the stacktrace could be moved to a new tab in the log\r\ndetails flyout and actions can be added to the stacktrace (and quality)\r\nicons in the document table to open the relevant sections in the flyout.\r\n\r\nCloses https://github.com/elastic/kibana/issues/190460\r\n\r\n### APM - Log stacktrace (library frames)\r\n<img width=\"1470\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8991f882-d329-4bc5-aa37-424576bcee72\"\r\n/>\r\n\r\n### APM - Exception (with cause)\r\n<img width=\"1476\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/cfbf24a7-6f82-48f1-b275-5aac977411ac\"\r\n/>\r\n\r\n### APM - Exception (simple stacktrace)\r\n<img width=\"1474\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/fc0306c4-5fcd-4b74-bb0d-c1784a48d677\"\r\n/>\r\n\r\n### Apache Tomcat Integration (Catalina) - Stacktrace\r\n<img width=\"1472\" alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/281f1822-faea-4e2d-9515-c11a9ee12f50\"\r\n/>\r\n\r\n## 📝 Notes for reviewers\r\n- The `@kbn/apm-types` package was marked as platform / shared as it's\r\nbeing used by the\r\n[unified_doc_viewer](https://github.com/elastic/kibana/blob/main/src/plugins/unified_doc_viewer/kibana.jsonc)\r\n- The code used to render stacktraces in APM was moved into a new\r\n`@kbn/event-stacktrace` package as it is reused in the\r\n`unified_doc_viewer`\r\n- The code used to render metadata table in APM was moved into a new\r\n`@kbn/key-value-metadata-table` package\r\n\r\n## 🧪 Testing instructions\r\nThe deployed environments have sample logs that can be used (time range:\r\nJan 1, 2025 - now). For a local setup, please follow the instructions\r\nbelow:\r\n\r\n1. Ingest sample logs with stacktraces\r\n([gist](https://gist.github.com/gbamparop/0da21ca7f65b24c4a9c071ce9e9b97b0)).\r\nPlease note that these are test data and some fields that are not used\r\nby stacktraces might not be consistent\r\n2. View relevant logs in Discover (Query: `service.name: \"synth-node-0\"\r\nOR apache_tomcat :*`, Time range: Jan 1, 2025 - now)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"368475e8e55845e17fd4621c1ae60ba1e983bb8f"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
1178850485
commit
7ebef44cf5
118 changed files with 835 additions and 127 deletions
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
|
@ -48,7 +48,7 @@ x-pack/solutions/observability/plugins/apm/ftr_e2e @elastic/obs-ux-infra_service
|
|||
x-pack/solutions/observability/plugins/apm @elastic/obs-ux-infra_services-team
|
||||
packages/kbn-apm-synthtrace @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team
|
||||
packages/kbn-apm-synthtrace-client @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team
|
||||
x-pack/solutions/observability/packages/kbn-apm-types @elastic/obs-ux-infra_services-team
|
||||
x-pack/platform/packages/shared/kbn-apm-types @elastic/obs-ux-infra_services-team
|
||||
src/platform/packages/shared/kbn-apm-utils @elastic/obs-ux-infra_services-team
|
||||
test/plugin_functional/plugins/app_link_test @elastic/kibana-core
|
||||
x-pack/test/usage_collection/plugins/application_usage_test @elastic/kibana-core
|
||||
|
@ -452,6 +452,7 @@ src/platform/plugins/private/event_annotation_listing @elastic/kibana-visualizat
|
|||
src/platform/plugins/private/event_annotation @elastic/kibana-visualizations
|
||||
x-pack/test/plugin_api_integration/plugins/event_log @elastic/response-ops
|
||||
x-pack/platform/plugins/shared/event_log @elastic/response-ops
|
||||
x-pack/platform/packages/shared/kbn-event-stacktrace @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team
|
||||
x-pack/solutions/security/packages/expandable-flyout @elastic/security-threat-hunting-investigations
|
||||
packages/kbn-expect @elastic/kibana-operations @elastic/appex-qa
|
||||
x-pack/examples/exploratory_view_example @elastic/obs-ux-infra_services-team
|
||||
|
@ -565,6 +566,7 @@ test/plugin_functional/plugins/kbn_sample_panel_action @elastic/appex-sharedux
|
|||
test/plugin_functional/plugins/kbn_top_nav @elastic/kibana-core
|
||||
test/plugin_functional/plugins/kbn_tp_custom_visualizations @elastic/kibana-visualizations
|
||||
test/interpreter_functional/plugins/kbn_tp_run_pipeline @elastic/kibana-core
|
||||
x-pack/platform/packages/shared/kbn-key-value-metadata-table @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team
|
||||
x-pack/test/functional_cors/plugins/kibana_cors_test @elastic/kibana-security
|
||||
packages/kbn-kibana-manifest-schema @elastic/kibana-operations
|
||||
src/platform/plugins/private/kibana_overview @elastic/appex-sharedux
|
||||
|
|
|
@ -187,7 +187,7 @@
|
|||
"@kbn/apm-data-access-plugin": "link:x-pack/solutions/observability/plugins/apm_data_access",
|
||||
"@kbn/apm-data-view": "link:src/platform/packages/shared/kbn-apm-data-view",
|
||||
"@kbn/apm-plugin": "link:x-pack/solutions/observability/plugins/apm",
|
||||
"@kbn/apm-types": "link:x-pack/solutions/observability/packages/kbn-apm-types",
|
||||
"@kbn/apm-types": "link:x-pack/platform/packages/shared/kbn-apm-types",
|
||||
"@kbn/apm-utils": "link:src/platform/packages/shared/kbn-apm-utils",
|
||||
"@kbn/app-link-test-plugin": "link:test/plugin_functional/plugins/app_link_test",
|
||||
"@kbn/application-usage-test-plugin": "link:x-pack/test/usage_collection/plugins/application_usage_test",
|
||||
|
@ -503,6 +503,7 @@
|
|||
"@kbn/event-annotation-plugin": "link:src/platform/plugins/private/event_annotation",
|
||||
"@kbn/event-log-fixture-plugin": "link:x-pack/test/plugin_api_integration/plugins/event_log",
|
||||
"@kbn/event-log-plugin": "link:x-pack/platform/plugins/shared/event_log",
|
||||
"@kbn/event-stacktrace": "link:x-pack/platform/packages/shared/kbn-event-stacktrace",
|
||||
"@kbn/expandable-flyout": "link:x-pack/solutions/security/packages/expandable-flyout",
|
||||
"@kbn/exploratory-view-example-plugin": "link:x-pack/examples/exploratory_view_example",
|
||||
"@kbn/exploratory-view-plugin": "link:x-pack/solutions/observability/plugins/exploratory_view",
|
||||
|
@ -600,6 +601,7 @@
|
|||
"@kbn/kbn-top-nav-plugin": "link:test/plugin_functional/plugins/kbn_top_nav",
|
||||
"@kbn/kbn-tp-custom-visualizations-plugin": "link:test/plugin_functional/plugins/kbn_tp_custom_visualizations",
|
||||
"@kbn/kbn-tp-run-pipeline-plugin": "link:test/interpreter_functional/plugins/kbn_tp_run_pipeline",
|
||||
"@kbn/key-value-metadata-table": "link:x-pack/platform/packages/shared/kbn-key-value-metadata-table",
|
||||
"@kbn/kibana-cors-test-plugin": "link:x-pack/test/functional_cors/plugins/kibana_cors_test",
|
||||
"@kbn/kibana-overview-plugin": "link:src/platform/plugins/private/kibana_overview",
|
||||
"@kbn/kibana-react-plugin": "link:src/platform/plugins/shared/kibana_react",
|
||||
|
|
|
@ -58,8 +58,8 @@ export type LogDocument = Fields &
|
|||
'cloud.project.id'?: string;
|
||||
'cloud.instance.id'?: string;
|
||||
'error.stack_trace'?: string;
|
||||
'error.exception.stacktrace'?: string;
|
||||
'error.log.stacktrace'?: string;
|
||||
'error.exception'?: unknown;
|
||||
'error.log'?: unknown;
|
||||
'log.custom': Record<string, unknown>;
|
||||
'host.geo.location': number[];
|
||||
'host.ip': string;
|
||||
|
|
|
@ -144,7 +144,7 @@ const scenario: Scenario<LogDocument> = async (runOptions) => {
|
|||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'error.message': message,
|
||||
'error.exception.stacktrace': 'Error message in error.exception.stacktrace',
|
||||
'error.stack_trace': 'Stacktrace',
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
});
|
||||
|
@ -174,7 +174,7 @@ const scenario: Scenario<LogDocument> = async (runOptions) => {
|
|||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'event.original': message,
|
||||
'error.log.stacktrace': 'Error message in error.log.stacktrace',
|
||||
'error.stack_trace': 'Stacktrace',
|
||||
'event.start': eventDate,
|
||||
'event.end': moment(eventDate).add(1, 'm').toDate(),
|
||||
})
|
||||
|
@ -203,7 +203,7 @@ const scenario: Scenario<LogDocument> = async (runOptions) => {
|
|||
.setHostIp(getIpAddress())
|
||||
.defaults({
|
||||
...commonLongEntryFields,
|
||||
'error.stack_trace': 'Error message in error.stack_trace',
|
||||
'error.stack_trace': 'Stacktrace',
|
||||
})
|
||||
.timestamp(timestamp);
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
generateLongId,
|
||||
generateShortId,
|
||||
Instance,
|
||||
LogDocument,
|
||||
} from '@kbn/apm-synthtrace-client';
|
||||
import { Scenario } from '../cli/scenario';
|
||||
import { getSynthtraceEnvironment } from '../lib/utils/get_synthtrace_environment';
|
||||
|
@ -26,7 +27,7 @@ const ENVIRONMENT = getSynthtraceEnvironment(__filename);
|
|||
const alwaysSpikeTransactionName = 'GET /always-spike';
|
||||
const sometimesSpikeTransactionName = 'GET /sometimes-spike';
|
||||
|
||||
const scenario: Scenario<ApmFields> = async ({ logger, ...runOptions }) => {
|
||||
const scenario: Scenario<LogDocument | ApmFields> = async ({ logger, ...runOptions }) => {
|
||||
const { isLogsDb } = parseLogsScenarioOpts(runOptions.scenarioOpts);
|
||||
|
||||
return {
|
||||
|
|
|
@ -77,4 +77,5 @@ export const storybookAliases = {
|
|||
ui_actions_enhanced: 'src/platform/plugins/shared/ui_actions_enhanced/.storybook',
|
||||
unified_search: 'src/platform/plugins/shared/unified_search/.storybook',
|
||||
profiling: 'x-pack/solutions/observability/plugins/profiling/.storybook',
|
||||
event_stacktrace: 'x-pack/platform/packages/shared/kbn-event-stacktrace/.storybook',
|
||||
};
|
||||
|
|
|
@ -37,8 +37,8 @@ export interface LogDocument extends DataTableRecord {
|
|||
'data_stream.dataset': string;
|
||||
|
||||
'error.stack_trace'?: string;
|
||||
'error.exception.stacktrace'?: string;
|
||||
'error.log.stacktrace'?: string;
|
||||
'error.exception.stacktrace.abs_path'?: string;
|
||||
'error.log.stacktrace.abs_path'?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -83,8 +83,8 @@ export interface ResourceFields {
|
|||
|
||||
export interface StackTraceFields {
|
||||
'error.stack_trace'?: string;
|
||||
'error.exception.stacktrace'?: string;
|
||||
'error.log.stacktrace'?: string;
|
||||
'error.exception.stacktrace.abs_path'?: string;
|
||||
'error.log.stacktrace.abs_path'?: string;
|
||||
}
|
||||
|
||||
export interface SmartFieldGridColumnOptions {
|
||||
|
|
|
@ -19,6 +19,7 @@ export const TRACE_ID_FIELD = 'trace.id';
|
|||
export const LOG_FILE_PATH_FIELD = 'log.file.path';
|
||||
export const DATASTREAM_NAMESPACE_FIELD = 'data_stream.namespace';
|
||||
export const DATASTREAM_DATASET_FIELD = 'data_stream.dataset';
|
||||
export const DATASTREAM_TYPE_FIELD = 'data_stream.type';
|
||||
|
||||
// Resource Fields
|
||||
export const AGENT_NAME_FIELD = 'agent.name';
|
||||
|
@ -41,5 +42,5 @@ export const DEGRADED_DOCS_FIELDS = [IGNORED_FIELD, IGNORED_FIELD_VALUES_FIELD]
|
|||
|
||||
// Error Stacktrace
|
||||
export const ERROR_STACK_TRACE = 'error.stack_trace';
|
||||
export const ERROR_EXCEPTION_STACKTRACE = 'error.exception.stacktrace';
|
||||
export const ERROR_LOG_STACKTRACE = 'error.log.stacktrace';
|
||||
export const ERROR_EXCEPTION_STACKTRACE_ABS_PATH = 'error.exception.stacktrace.abs_path';
|
||||
export const ERROR_LOG_STACKTRACE_ABS_PATH = 'error.log.stacktrace.abs_path';
|
||||
|
|
|
@ -9,19 +9,19 @@
|
|||
|
||||
import { getFieldValue, LogDocument, StackTraceFields } from '..';
|
||||
import {
|
||||
ERROR_EXCEPTION_STACKTRACE,
|
||||
ERROR_LOG_STACKTRACE,
|
||||
ERROR_EXCEPTION_STACKTRACE_ABS_PATH,
|
||||
ERROR_LOG_STACKTRACE_ABS_PATH,
|
||||
ERROR_STACK_TRACE,
|
||||
} from '../field_constants';
|
||||
|
||||
export const getStacktraceFields = (doc: LogDocument): StackTraceFields => {
|
||||
const errorStackTrace = getFieldValue(doc, ERROR_STACK_TRACE);
|
||||
const errorExceptionStackTrace = getFieldValue(doc, ERROR_EXCEPTION_STACKTRACE);
|
||||
const errorLogStackTrace = getFieldValue(doc, ERROR_LOG_STACKTRACE);
|
||||
const errorExceptionStackTrace = getFieldValue(doc, ERROR_EXCEPTION_STACKTRACE_ABS_PATH);
|
||||
const errorLogStackTrace = getFieldValue(doc, ERROR_LOG_STACKTRACE_ABS_PATH);
|
||||
|
||||
return {
|
||||
[ERROR_STACK_TRACE]: errorStackTrace,
|
||||
[ERROR_EXCEPTION_STACKTRACE]: errorExceptionStackTrace,
|
||||
[ERROR_LOG_STACKTRACE]: errorLogStackTrace,
|
||||
[ERROR_EXCEPTION_STACKTRACE_ABS_PATH]: errorExceptionStackTrace,
|
||||
[ERROR_LOG_STACKTRACE_ABS_PATH]: errorLogStackTrace,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
"fieldsMetadata"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"kibanaUtils"
|
||||
"kibanaUtils",
|
||||
"kibanaReact"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,13 @@ import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
|
|||
import { getLogDocumentOverview } from '@kbn/discover-utils';
|
||||
import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui';
|
||||
import { ObservabilityLogsAIAssistantFeatureRenderDeps } from '@kbn/discover-shared-plugin/public';
|
||||
import { getStacktraceFields, LogDocument } from '@kbn/discover-utils/src';
|
||||
import { LogsOverviewHeader } from './logs_overview_header';
|
||||
import { LogsOverviewHighlights } from './logs_overview_highlights';
|
||||
import { FieldActionsProvider } from '../../hooks/use_field_actions';
|
||||
import { getUnifiedDocViewerServices } from '../../plugin';
|
||||
import { LogsOverviewDegradedFields } from './logs_overview_degraded_fields';
|
||||
import { LogsOverviewStacktraceSection } from './logs_overview_stacktrace_section';
|
||||
|
||||
export type LogsOverviewProps = DocViewRenderProps & {
|
||||
renderAIAssistant?: (deps: ObservabilityLogsAIAssistantFeatureRenderDeps) => JSX.Element;
|
||||
|
@ -34,6 +36,8 @@ export function LogsOverview({
|
|||
const { fieldFormats } = getUnifiedDocViewerServices();
|
||||
const parsedDoc = getLogDocumentOverview(hit, { dataView, fieldFormats });
|
||||
const LogsOverviewAIAssistant = renderAIAssistant;
|
||||
const stacktraceFields = getStacktraceFields(hit as LogDocument);
|
||||
const isStacktraceAvailable = Object.values(stacktraceFields).some(Boolean);
|
||||
|
||||
return (
|
||||
<FieldActionsProvider
|
||||
|
@ -47,6 +51,7 @@ export function LogsOverview({
|
|||
<EuiHorizontalRule margin="xs" />
|
||||
<LogsOverviewHighlights formattedDoc={parsedDoc} flattenedDoc={hit.flattened} />
|
||||
<LogsOverviewDegradedFields rawDoc={hit.raw} />
|
||||
{isStacktraceAvailable && <LogsOverviewStacktraceSection hit={hit} dataView={dataView} />}
|
||||
{LogsOverviewAIAssistant && <LogsOverviewAIAssistant doc={hit} />}
|
||||
</FieldActionsProvider>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
import { EuiAccordion, EuiHorizontalRule, EuiTitle, useGeneratedHtmlId } from '@elastic/eui';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { DataTableRecord } from '@kbn/discover-utils';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { StacktraceContent } from './sub_components/stacktrace/stacktrace_content';
|
||||
|
||||
const stacktraceAccordionTitle = i18n.translate(
|
||||
'unifiedDocViewer.docView.logsOverview.accordion.title.stacktrace',
|
||||
{
|
||||
defaultMessage: 'Stacktrace',
|
||||
}
|
||||
);
|
||||
|
||||
export function LogsOverviewStacktraceSection({
|
||||
hit,
|
||||
dataView,
|
||||
}: {
|
||||
hit: DataTableRecord;
|
||||
dataView: DataView;
|
||||
}) {
|
||||
const accordionId = useGeneratedHtmlId({
|
||||
prefix: stacktraceAccordionTitle,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiAccordion
|
||||
id={accordionId}
|
||||
buttonContent={
|
||||
<EuiTitle size="xs">
|
||||
<p>{stacktraceAccordionTitle}</p>
|
||||
</EuiTitle>
|
||||
}
|
||||
paddingSize="m"
|
||||
initialIsOpen={false}
|
||||
data-test-subj="unifiedDocViewLogsOverviewStacktraceAccordion"
|
||||
>
|
||||
<EuiThemeProvider>
|
||||
<StacktraceContent hit={hit} dataView={dataView} />
|
||||
</EuiThemeProvider>
|
||||
</EuiAccordion>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import '@kbn/code-editor-mock/jest_helper';
|
||||
import * as hooks from '../../../../hooks/use_es_doc_search';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { buildDataTableRecord } from '@kbn/discover-utils';
|
||||
import { ApmStacktrace } from './apm_stacktrace';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { ExceptionStacktrace, PlaintextStacktrace, Stacktrace } from '@kbn/event-stacktrace';
|
||||
|
||||
const mockDataView = {
|
||||
getComputedFields: () => [],
|
||||
} as never;
|
||||
|
||||
describe('APM Stacktrace component', () => {
|
||||
test('renders loading state', () => {
|
||||
jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [0, null, () => {}]);
|
||||
|
||||
const comp = mountWithIntl(
|
||||
<ApmStacktrace
|
||||
hit={{
|
||||
raw: { _id: '1', _index: 'index1' },
|
||||
flattened: {},
|
||||
id: '',
|
||||
}}
|
||||
dataView={mockDataView}
|
||||
/>
|
||||
);
|
||||
|
||||
const loadingSpinner = comp.find(EuiLoadingSpinner);
|
||||
expect(loadingSpinner).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('renders error state', () => {
|
||||
jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [3, null, () => {}]);
|
||||
|
||||
const comp = mountWithIntl(
|
||||
<ApmStacktrace
|
||||
hit={{
|
||||
raw: { _id: '1', _index: 'index1' },
|
||||
flattened: {},
|
||||
id: '',
|
||||
}}
|
||||
dataView={mockDataView}
|
||||
/>
|
||||
);
|
||||
const errorComponent = comp.find('[data-test-subj="unifiedDocViewerApmStacktraceErrorMsg"]');
|
||||
expect(errorComponent).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('renders log stacktrace', () => {
|
||||
const mockHit = getMockHit({
|
||||
id: '1',
|
||||
grouping_key: '1',
|
||||
log: {
|
||||
message: 'Log message',
|
||||
stacktrace: [
|
||||
{
|
||||
exclude_from_grouping: false,
|
||||
abs_path: 'test.js',
|
||||
filename: 'test.js',
|
||||
line: {
|
||||
number: 1,
|
||||
context: 'console.log(err)',
|
||||
},
|
||||
function: '<anonymous>',
|
||||
context: {
|
||||
pre: ['console.log(err)'],
|
||||
post: ['console.log(err)'],
|
||||
},
|
||||
vars: {},
|
||||
},
|
||||
{
|
||||
exclude_from_grouping: false,
|
||||
library_frame: true,
|
||||
abs_path: 'test.js',
|
||||
filename: 'test.js',
|
||||
line: {
|
||||
number: 1,
|
||||
},
|
||||
function: 'test',
|
||||
vars: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [2, mockHit, () => {}]);
|
||||
|
||||
const comp = mountWithIntl(
|
||||
<EuiThemeProvider>
|
||||
<ApmStacktrace
|
||||
hit={{
|
||||
raw: { _id: '1', _index: 'index1' },
|
||||
flattened: {},
|
||||
id: '',
|
||||
}}
|
||||
dataView={mockDataView}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
const stacktraceComponent = comp.find(Stacktrace);
|
||||
expect(stacktraceComponent).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('renders exception stacktrace', () => {
|
||||
const mockHit = getMockHit({
|
||||
id: '1',
|
||||
grouping_key: '1',
|
||||
exception: [
|
||||
{
|
||||
message: 'Exception stacktrace',
|
||||
stacktrace: [
|
||||
{
|
||||
exclude_from_grouping: false,
|
||||
abs_path: 'test.js',
|
||||
filename: 'test.js',
|
||||
line: {
|
||||
number: 1,
|
||||
context: 'console.log(err)',
|
||||
},
|
||||
function: '<anonymous>',
|
||||
context: {
|
||||
pre: ['console.log(err)'],
|
||||
post: ['console.log(err);'],
|
||||
},
|
||||
vars: {},
|
||||
},
|
||||
{
|
||||
exclude_from_grouping: false,
|
||||
library_frame: true,
|
||||
abs_path: 'test.js',
|
||||
filename: 'test.js',
|
||||
line: {
|
||||
number: 1,
|
||||
},
|
||||
function: 'test',
|
||||
vars: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
handled: true,
|
||||
module: 'module',
|
||||
attributes: {
|
||||
test: 'test',
|
||||
},
|
||||
message: 'message',
|
||||
type: 'type',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [2, mockHit, () => {}]);
|
||||
|
||||
const comp = mountWithIntl(
|
||||
<EuiThemeProvider>
|
||||
<ApmStacktrace
|
||||
hit={{
|
||||
raw: { _id: '1', _index: 'index1' },
|
||||
flattened: {},
|
||||
id: '',
|
||||
}}
|
||||
dataView={mockDataView}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
const stacktraceComponent = comp.find(ExceptionStacktrace);
|
||||
expect(stacktraceComponent).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('renders plain text stacktrace', () => {
|
||||
const mockHit = getMockHit({
|
||||
id: '1',
|
||||
grouping_key: '1',
|
||||
exception: [
|
||||
{
|
||||
handled: true,
|
||||
message: 'message',
|
||||
type: 'type',
|
||||
},
|
||||
],
|
||||
stack_trace: 'test',
|
||||
});
|
||||
|
||||
jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [2, mockHit, () => {}]);
|
||||
|
||||
const comp = mountWithIntl(
|
||||
<EuiThemeProvider>
|
||||
<ApmStacktrace
|
||||
hit={{
|
||||
raw: { _id: '1', _index: 'index1' },
|
||||
flattened: {},
|
||||
id: '',
|
||||
}}
|
||||
dataView={mockDataView}
|
||||
/>
|
||||
</EuiThemeProvider>
|
||||
);
|
||||
|
||||
const stacktraceComponent = comp.find(PlaintextStacktrace);
|
||||
expect(stacktraceComponent).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
function getMockHit(error: Record<string, unknown>) {
|
||||
return buildDataTableRecord({
|
||||
_index: '.ds-logs-apm.error-default-2024.12.31-000001',
|
||||
_id: 'id123',
|
||||
_score: 1,
|
||||
_source: {
|
||||
data_stream: {
|
||||
type: 'logs',
|
||||
dataset: 'apm.error',
|
||||
namespace: 'default',
|
||||
},
|
||||
'@timestamp': '2024-12-31T00:00:00.000Z',
|
||||
message: 'Log stacktrace',
|
||||
error,
|
||||
},
|
||||
});
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { DataTableRecord } from '@kbn/discover-utils';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ExceptionStacktrace, PlaintextStacktrace, Stacktrace } from '@kbn/event-stacktrace';
|
||||
import type { APMError } from '@kbn/apm-types';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { ElasticRequestState } from '@kbn/unified-doc-viewer';
|
||||
import { useEsDocSearch } from '../../../../hooks';
|
||||
|
||||
export const APM_ERROR_DATASTREAM_FIELDS = {
|
||||
dataStreamType: 'logs',
|
||||
dataStreamDataset: 'apm.error',
|
||||
};
|
||||
|
||||
export function ApmStacktrace({ hit, dataView }: { hit: DataTableRecord; dataView: DataView }) {
|
||||
const [apmErrorDoc, setApmErrorDoc] = useState<APMError>();
|
||||
|
||||
const [requestState, esHit] = useEsDocSearch({
|
||||
id: hit.raw._id || '',
|
||||
index: hit.raw._index,
|
||||
dataView,
|
||||
requestSource: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (requestState === ElasticRequestState.Found && esHit) {
|
||||
setApmErrorDoc(esHit?.raw._source as unknown as APMError);
|
||||
}
|
||||
}, [requestState, esHit]);
|
||||
|
||||
if (requestState === ElasticRequestState.Loading) {
|
||||
return <EuiLoadingSpinner size="m" />;
|
||||
}
|
||||
|
||||
if (requestState === ElasticRequestState.Error || requestState === ElasticRequestState.NotFound) {
|
||||
return (
|
||||
<p data-test-subj="unifiedDocViewerApmStacktraceErrorMsg">
|
||||
{i18n.translate('unifiedDocViewer.apmStacktrace.errorMessage', {
|
||||
defaultMessage: 'Failed to load stacktrace',
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const codeLanguage = apmErrorDoc?.service?.language?.name;
|
||||
const exceptions = apmErrorDoc?.error?.exception || [];
|
||||
const logStackframes = apmErrorDoc?.error?.log?.stacktrace;
|
||||
const isPlaintextException =
|
||||
!!apmErrorDoc?.error?.stack_trace && exceptions.length === 1 && !exceptions[0].stacktrace;
|
||||
|
||||
if (apmErrorDoc?.error?.log?.message) {
|
||||
return <Stacktrace stackframes={logStackframes} codeLanguage={codeLanguage} />;
|
||||
}
|
||||
|
||||
if (apmErrorDoc?.error?.exception?.length) {
|
||||
return isPlaintextException ? (
|
||||
<PlaintextStacktrace
|
||||
message={exceptions[0].message}
|
||||
type={exceptions[0]?.type}
|
||||
stacktrace={apmErrorDoc?.error.stack_trace}
|
||||
codeLanguage={codeLanguage}
|
||||
/>
|
||||
) : (
|
||||
<ExceptionStacktrace codeLanguage={codeLanguage} exceptions={exceptions} />
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiCodeBlock } from '@elastic/eui';
|
||||
import { DataTableRecord, fieldConstants, getFieldValue } from '@kbn/discover-utils';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { APM_ERROR_DATASTREAM_FIELDS, ApmStacktrace } from './apm_stacktrace';
|
||||
|
||||
export function StacktraceContent({ hit, dataView }: { hit: DataTableRecord; dataView: DataView }) {
|
||||
const errorStackTrace = getFieldValue(hit, fieldConstants.ERROR_STACK_TRACE) as string;
|
||||
const dataStreamTypeField = getFieldValue(hit, fieldConstants.DATASTREAM_TYPE_FIELD) as string;
|
||||
const dataStreamDatasetField = getFieldValue(
|
||||
hit,
|
||||
fieldConstants.DATASTREAM_DATASET_FIELD
|
||||
) as string;
|
||||
|
||||
if (
|
||||
dataStreamTypeField === APM_ERROR_DATASTREAM_FIELDS.dataStreamType &&
|
||||
dataStreamDatasetField === APM_ERROR_DATASTREAM_FIELDS.dataStreamDataset
|
||||
) {
|
||||
return <ApmStacktrace hit={hit} dataView={dataView} />;
|
||||
}
|
||||
|
||||
if (errorStackTrace) {
|
||||
return <EuiCodeBlock isCopyable={true}>{errorStackTrace}</EuiCodeBlock>;
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
{i18n.translate('unifiedDocViewer.stacktraceSection.errorMessage', {
|
||||
defaultMessage: 'Failed to load stacktrace',
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
|
@ -37,7 +37,10 @@
|
|||
"@kbn/router-utils",
|
||||
"@kbn/unified-field-list",
|
||||
"@kbn/core-lifecycle-browser",
|
||||
"@kbn/management-settings-ids"
|
||||
"@kbn/management-settings-ids",
|
||||
"@kbn/apm-types",
|
||||
"@kbn/event-stacktrace"
|
||||
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -90,8 +90,8 @@
|
|||
"@kbn/apm-synthtrace/*": ["packages/kbn-apm-synthtrace/*"],
|
||||
"@kbn/apm-synthtrace-client": ["packages/kbn-apm-synthtrace-client"],
|
||||
"@kbn/apm-synthtrace-client/*": ["packages/kbn-apm-synthtrace-client/*"],
|
||||
"@kbn/apm-types": ["x-pack/solutions/observability/packages/kbn-apm-types"],
|
||||
"@kbn/apm-types/*": ["x-pack/solutions/observability/packages/kbn-apm-types/*"],
|
||||
"@kbn/apm-types": ["x-pack/platform/packages/shared/kbn-apm-types"],
|
||||
"@kbn/apm-types/*": ["x-pack/platform/packages/shared/kbn-apm-types/*"],
|
||||
"@kbn/apm-utils": ["src/platform/packages/shared/kbn-apm-utils"],
|
||||
"@kbn/apm-utils/*": ["src/platform/packages/shared/kbn-apm-utils/*"],
|
||||
"@kbn/app-link-test-plugin": ["test/plugin_functional/plugins/app_link_test"],
|
||||
|
@ -898,6 +898,8 @@
|
|||
"@kbn/event-log-fixture-plugin/*": ["x-pack/test/plugin_api_integration/plugins/event_log/*"],
|
||||
"@kbn/event-log-plugin": ["x-pack/platform/plugins/shared/event_log"],
|
||||
"@kbn/event-log-plugin/*": ["x-pack/platform/plugins/shared/event_log/*"],
|
||||
"@kbn/event-stacktrace": ["x-pack/platform/packages/shared/kbn-event-stacktrace"],
|
||||
"@kbn/event-stacktrace/*": ["x-pack/platform/packages/shared/kbn-event-stacktrace/*"],
|
||||
"@kbn/expandable-flyout": ["x-pack/solutions/security/packages/expandable-flyout"],
|
||||
"@kbn/expandable-flyout/*": ["x-pack/solutions/security/packages/expandable-flyout/*"],
|
||||
"@kbn/expect": ["packages/kbn-expect"],
|
||||
|
@ -1124,6 +1126,8 @@
|
|||
"@kbn/kbn-tp-custom-visualizations-plugin/*": ["test/plugin_functional/plugins/kbn_tp_custom_visualizations/*"],
|
||||
"@kbn/kbn-tp-run-pipeline-plugin": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline"],
|
||||
"@kbn/kbn-tp-run-pipeline-plugin/*": ["test/interpreter_functional/plugins/kbn_tp_run_pipeline/*"],
|
||||
"@kbn/key-value-metadata-table": ["x-pack/platform/packages/shared/kbn-key-value-metadata-table"],
|
||||
"@kbn/key-value-metadata-table/*": ["x-pack/platform/packages/shared/kbn-key-value-metadata-table/*"],
|
||||
"@kbn/kibana-cors-test-plugin": ["x-pack/test/functional_cors/plugins/kibana_cors_test"],
|
||||
"@kbn/kibana-cors-test-plugin/*": ["x-pack/test/functional_cors/plugins/kibana_cors_test/*"],
|
||||
"@kbn/kibana-manifest-schema": ["packages/kbn-kibana-manifest-schema"],
|
||||
|
|
|
@ -163,7 +163,8 @@
|
|||
"xpack.synthetics": ["solutions/observability/plugins/synthetics"],
|
||||
"xpack.ux": ["solutions/observability/plugins/ux"],
|
||||
"xpack.urlDrilldown": "platform/plugins/private/drilldowns/url_drilldown",
|
||||
"xpack.watcher": "platform/plugins/private/watcher"
|
||||
"xpack.watcher": "platform/plugins/private/watcher",
|
||||
"xpack.eventStacktrace": "platform/packages/shared/kbn-event-stacktrace"
|
||||
},
|
||||
"exclude": ["examples"],
|
||||
"translations": [
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"owner": [
|
||||
"@elastic/obs-ux-infra_services-team"
|
||||
],
|
||||
"group": "observability",
|
||||
"visibility": "private"
|
||||
}
|
||||
"group": "platform",
|
||||
"visibility": "shared"
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 { setGlobalConfig } from '@storybook/testing-react';
|
||||
import * as globalStorybookConfig from './preview';
|
||||
|
||||
setGlobalConfig(globalStorybookConfig);
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = require('@kbn/storybook').defaultConfig;
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 { EuiThemeProviderDecorator } from '@kbn/kibana-react-plugin/common';
|
||||
export const decorators = [EuiThemeProviderDecorator];
|
|
@ -0,0 +1,19 @@
|
|||
# @kbn/event-stacktrace
|
||||
|
||||
This package contains components that render event (error, log, span) stack traces.
|
||||
|
||||
## Unit Tests (Jest)
|
||||
|
||||
```
|
||||
node scripts/jest --config x-pack/platform/packages/shared/kbn-event-stacktrace/README.md [--watch]
|
||||
|
||||
```
|
||||
|
||||
## Storybook
|
||||
|
||||
### Start
|
||||
```
|
||||
yarn storybook event_stacktrace
|
||||
```
|
||||
|
||||
All files with a .stories.tsx extension will be loaded. You can access the development environment at http://localhost:9001.
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './src/components/stacktrace/cause_stacktrace';
|
||||
export * from './src/components/stacktrace/frame_heading';
|
||||
export * from './src/components/stacktrace';
|
||||
export * from './src/components/stacktrace/library_stacktrace';
|
||||
export * from './src/components/stacktrace/stackframe';
|
||||
export * from './src/components/stacktrace/variables';
|
||||
export * from './src/components/stacktrace/plain/plaintext_stacktrace';
|
||||
export * from './src/components/stacktrace/exception/exception_stacktrace';
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../../..',
|
||||
roots: ['<rootDir>/x-pack/platform/packages/shared/kbn-event-stacktrace'],
|
||||
setupFiles: [
|
||||
'<rootDir>/x-pack/platform/packages/shared/kbn-event-stacktrace/.storybook/jest_setup.js',
|
||||
],
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/event-stacktrace",
|
||||
"owner": [
|
||||
"@elastic/obs-ux-infra_services-team",
|
||||
"@elastic/obs-ux-logs-team"
|
||||
],
|
||||
"group": "platform",
|
||||
"visibility": "shared"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/event-stacktrace",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithTheme } from '../../../utils/test_helpers';
|
||||
import { mountWithTheme } from '../../utils/test_helpers';
|
||||
import { CauseStacktrace } from './cause_stacktrace';
|
||||
|
||||
describe('CauseStacktrace', () => {
|
|
@ -9,8 +9,8 @@ import { EuiAccordion, EuiTitle } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import { Stacktrace } from '.';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
|
||||
const Accordion = euiStyled(EuiAccordion)`
|
||||
border-top: ${({ theme }) => theme.eui.euiBorderThin};
|
||||
|
@ -37,7 +37,7 @@ function CausedBy({ message }: { message: string }) {
|
|||
return (
|
||||
<CausedByContainer>
|
||||
<CausedByHeading>
|
||||
{i18n.translate('xpack.apm.stacktraceTab.causedByFramesToogleButtonLabel', {
|
||||
{i18n.translate('xpack.eventStacktrace.stacktraceTab.causedByFramesToogleButtonLabel', {
|
||||
defaultMessage: 'Caused By',
|
||||
})}
|
||||
</CausedByHeading>
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiCodeBlock } from '@elastic/eui';
|
||||
import type { StackframeWithLineContext } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import type { StackframeWithLineContext } from '@kbn/apm-types';
|
||||
|
||||
function getStackframeLines(stackframe: StackframeWithLineContext) {
|
||||
const line = stackframe.line.context;
|
|
@ -6,10 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Exception } from '../../../../../typings/es_schemas/raw/error_raw';
|
||||
import { Stacktrace } from '../../../shared/stacktrace';
|
||||
import { CauseStacktrace } from '../../../shared/stacktrace/cause_stacktrace';
|
||||
import type { Exception } from '@kbn/apm-types/es_schemas_raw';
|
||||
import { ExceptionStacktraceTitle } from './exception_stacktrace_title';
|
||||
import { CauseStacktrace } from '../cause_stacktrace';
|
||||
import { Stacktrace } from '..';
|
||||
|
||||
interface ExceptionStacktraceProps {
|
||||
codeLanguage?: string;
|
|
@ -40,7 +40,7 @@ export function ExceptionStacktraceTitle({
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiTitle size="xs">
|
||||
<EuiTitle size="xxs">
|
||||
<h4>{title}</h4>
|
||||
</EuiTitle>
|
||||
);
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { renderWithTheme } from '../../../utils/test_helpers';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import { renderWithTheme } from '../../utils/test_helpers';
|
||||
import { FrameHeading } from './frame_heading';
|
||||
|
||||
function getRenderedStackframeText(stackframe: Stackframe, codeLanguage: string, idx: string) {
|
|
@ -8,8 +8,7 @@
|
|||
import type { ComponentType } from 'react';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import type { FrameHeadingRendererProps } from './frame_heading_renderers';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import {
|
||||
CSharpFrameHeadingRenderer,
|
||||
DefaultFrameHeadingRenderer,
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
JavaScriptFrameHeadingRenderer,
|
||||
RubyFrameHeadingRenderer,
|
||||
PhpFrameHeadingRenderer,
|
||||
FrameHeadingRendererProps,
|
||||
} from './frame_heading_renderers';
|
||||
|
||||
const FileDetails = euiStyled.div`
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ComponentType } from 'react';
|
||||
import type { Stackframe } from '../../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { ComponentType } from 'react';
|
||||
import type { Stackframe } from '@kbn/apm-types';
|
||||
|
||||
export interface FrameHeadingRendererProps {
|
||||
fileDetailComponent: ComponentType<React.PropsWithChildren<{}>>;
|
|
@ -8,9 +8,8 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty, last } from 'lodash';
|
||||
import React, { Fragment } from 'react';
|
||||
import { EuiCodeBlock } from '@elastic/eui';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { EmptyMessage } from '../empty_message';
|
||||
import { EuiCodeBlock, EuiEmptyPrompt } from '@elastic/eui';
|
||||
import type { Stackframe } from '@kbn/apm-types';
|
||||
import { LibraryStacktrace } from './library_stacktrace';
|
||||
import { Stackframe as StackframeComponent } from './stackframe';
|
||||
|
||||
|
@ -23,11 +22,15 @@ interface Props {
|
|||
export function Stacktrace({ stackframes = [], codeLanguage }: Props) {
|
||||
if (isEmpty(stackframes)) {
|
||||
return (
|
||||
<EmptyMessage
|
||||
heading={i18n.translate('xpack.apm.stacktraceTab.noStacktraceAvailableLabel', {
|
||||
defaultMessage: 'No stack trace available.',
|
||||
})}
|
||||
hideSubheading
|
||||
<EuiEmptyPrompt
|
||||
titleSize="s"
|
||||
title={
|
||||
<div>
|
||||
{i18n.translate('xpack.eventStacktrace.stacktraceTab.noStacktraceAvailableLabel', {
|
||||
defaultMessage: 'No stack trace available.',
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderWithTheme } from '../../../utils/test_helpers';
|
||||
import { renderWithTheme } from '../../utils/test_helpers';
|
||||
import { LibraryStacktrace } from './library_stacktrace';
|
||||
|
||||
describe('LibraryStacktrace', () => {
|
|
@ -9,7 +9,7 @@ import { EuiAccordion } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import { Stackframe as StackframeComponent } from './stackframe';
|
||||
|
||||
const LibraryStacktraceAccordion = euiStyled(EuiAccordion)`
|
||||
|
@ -29,10 +29,13 @@ export function LibraryStacktrace({ codeLanguage, id, stackframes }: Props) {
|
|||
|
||||
return (
|
||||
<LibraryStacktraceAccordion
|
||||
buttonContent={i18n.translate('xpack.apm.stacktraceTab.libraryFramesToogleButtonLabel', {
|
||||
defaultMessage: '{count, plural, one {# library frame} other {# library frames}}',
|
||||
values: { count: stackframes.length },
|
||||
})}
|
||||
buttonContent={i18n.translate(
|
||||
'xpack.eventStacktrace.stacktraceTab.libraryFramesToogleButtonLabel',
|
||||
{
|
||||
defaultMessage: '{count, plural, one {# library frame} other {# library frames}}',
|
||||
values: { count: stackframes.length },
|
||||
}
|
||||
)}
|
||||
data-test-subj="LibraryStacktraceAccordion"
|
||||
id={id}
|
||||
>
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiCodeBlock } from '@elastic/eui';
|
||||
import { ExceptionStacktraceTitle } from './exception_stacktrace_title';
|
||||
import { ExceptionStacktraceTitle } from '../exception/exception_stacktrace_title';
|
||||
|
||||
interface PlaintextStacktraceProps {
|
||||
codeLanguage?: string;
|
|
@ -6,10 +6,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { ReactWrapper } from 'enzyme';
|
||||
import { shallow } from 'enzyme';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { mountWithTheme } from '../../../utils/test_helpers';
|
||||
import { ReactWrapper, shallow } from 'enzyme';
|
||||
import type { Stackframe } from '@kbn/apm-types';
|
||||
import { mountWithTheme } from '../../utils/test_helpers';
|
||||
import { Stackframe as StackframeComponent } from './stackframe';
|
||||
import stacktracesMock from './__fixtures__/stacktraces.json';
|
||||
|
|
@ -8,10 +8,7 @@
|
|||
import { EuiAccordion } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import type {
|
||||
Stackframe as StackframeType,
|
||||
StackframeWithLineContext,
|
||||
} from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import type { Stackframe as StackframeType, StackframeWithLineContext } from '@kbn/apm-types';
|
||||
import { Context } from './context';
|
||||
import { FrameHeading } from './frame_heading';
|
||||
import { Variables } from './variables';
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import { getGroupedStackframes } from '.';
|
||||
import stacktracesMock from './__fixtures__/stacktraces.json';
|
||||
|
|
@ -8,10 +8,9 @@
|
|||
import { EuiAccordion } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { KeyValueTable, getFlattenedKeyValuePairs } from '@kbn/key-value-metadata-table';
|
||||
import { Stackframe } from '@kbn/apm-types';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import type { Stackframe } from '../../../../typings/es_schemas/raw/fields/stackframe';
|
||||
import { KeyValueTable } from '../key_value_table';
|
||||
import { flattenObject } from '../../../../common/utils/flatten_object';
|
||||
|
||||
const VariablesContainer = euiStyled.div`
|
||||
background: ${({ theme }) => theme.eui.euiColorEmptyShade};
|
||||
|
@ -28,16 +27,19 @@ export function Variables({ vars }: Props) {
|
|||
if (!vars) {
|
||||
return null;
|
||||
}
|
||||
const flattenedVariables = flattenObject(vars);
|
||||
const flattenedVariables = getFlattenedKeyValuePairs(vars);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<VariablesContainer>
|
||||
<EuiAccordion
|
||||
id="local-variables"
|
||||
className="euiAccordion"
|
||||
buttonContent={i18n.translate('xpack.apm.stacktraceTab.localVariablesToogleButtonLabel', {
|
||||
defaultMessage: 'Local variables',
|
||||
})}
|
||||
buttonContent={i18n.translate(
|
||||
'xpack.eventStacktrace.stacktraceTab.localVariablesToogleButtonLabel',
|
||||
{
|
||||
defaultMessage: 'Local variables',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<React.Fragment>
|
||||
<KeyValueTable keyValuePairs={flattenedVariables} />
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { render } from '@testing-library/react';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { mount, MountRendererProps } from 'enzyme';
|
||||
|
||||
export function renderWithTheme(component: React.ReactNode, params?: any) {
|
||||
return render(<EuiThemeProvider>{component}</EuiThemeProvider>, params);
|
||||
}
|
||||
|
||||
export function mountWithTheme(tree: React.ReactElement<any>) {
|
||||
function WrappingThemeProvider(props: any) {
|
||||
return <EuiThemeProvider>{props.children}</EuiThemeProvider>;
|
||||
}
|
||||
|
||||
return mount(tree, {
|
||||
wrappingComponent: WrappingThemeProvider,
|
||||
} as MountRendererProps);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@testing-library/jest-dom"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"src/**/*.json"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/i18n",
|
||||
"@kbn/apm-types",
|
||||
"@kbn/key-value-metadata-table",
|
||||
"@kbn/kibana-react-plugin"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# @kbn/key-value-metadata-table
|
||||
|
||||
Key-value metadata table
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { KeyValueTable } from './src';
|
||||
export { getFlattenedKeyValuePairs } from './src/utils/get_flattened_key_value_pairs';
|
||||
export type { KeyValuePair } from './src/utils/get_flattened_key_value_pairs';
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../../..',
|
||||
roots: ['<rootDir>/x-pack/platform/packages/shared/kbn-key-value-metadata-table'],
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/key-value-metadata-table",
|
||||
"owner": [
|
||||
"@elastic/obs-ux-infra_services-team",
|
||||
"@elastic/obs-ux-logs-team"
|
||||
],
|
||||
"group": "platform",
|
||||
"visibility": "shared"
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/key-value-metadata-table",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import { isBoolean, isNumber, isObject } from 'lodash';
|
||||
import React from 'react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const EmptyValue = euiStyled.span`
|
||||
color: ${({ theme }) => theme.eui.euiColorMediumShade};
|
||||
|
@ -29,7 +29,13 @@ export function FormattedValue({ value }: { value: any }): JSX.Element {
|
|||
} else if (isBoolean(value) || isNumber(value)) {
|
||||
return <React.Fragment>{String(value)}</React.Fragment>;
|
||||
} else if (!value) {
|
||||
return <EmptyValue>{NOT_AVAILABLE_LABEL}</EmptyValue>;
|
||||
return (
|
||||
<EmptyValue>
|
||||
{i18n.translate('keyValueMetadataTable.notAvailableLabel', {
|
||||
defaultMessage: 'N/A',
|
||||
})}
|
||||
</EmptyValue>
|
||||
);
|
||||
}
|
||||
|
||||
return <React.Fragment>{value}</React.Fragment>;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue