Skip to content

Add advanced setting for default log columns in trace samples#275365

Merged
MiriamAparicio merged 12 commits into
elastic:mainfrom
MiriamAparicio:274632-apm-setting-for-logs-columns
Jul 2, 2026
Merged

Add advanced setting for default log columns in trace samples#275365
MiriamAparicio merged 12 commits into
elastic:mainfrom
MiriamAparicio:274632-apm-setting-for-logs-columns

Conversation

@MiriamAparicio

Copy link
Copy Markdown
Contributor

Closes #274632

Summary

Adds an advanced setting (observability:apmTraceLogsDefaultColumns) so APM admins can configure default columns for the Trace samples → Logs tab.

Column resolution priority:

  1. URL logsColumns param — when present with custom (non-Summary) columns
  2. Advanced setting — when non-empty
  3. Discover default — @timestamp + Summary (_source)

Example: setting ["message"] shows @timestamp + message instead of Summary.

Changes

  • Register observability:apmTraceLogsDefaultColumns advanced setting (technical preview, array of field names, default [])
  • Surface in APM → Settings → General settings
  • Apply setting in trace samples Logs tab via getTraceLogsColumns() merge helper
  • Read setting via settings.client with live subscription (no page reload after save)
  • URL sync: do not persist Discover Summary/_source defaults to logsColumns
  • Add optional resolveColumnsOnChange to @kbn/saved-search-component (APM-only) to prevent a Summary flash when users remove all columns and a setting is configured

How it works (confirmed with product)

URL session state takes precedence over the admin setting when logsColumns contains custom columns. This is intentional:

  • URL wins over setting: If a user customizes columns in the table, those columns are written to the URL and override the admin setting for that session.
  • Clearing the setting does not clear URL overrides: An admin clearing the setting does not reset users who already have logsColumns in the URL. Users must reset the table or navigate without the param.
  • Setting changes are ignored while URL overrides exist: If a user has session columns in the URL, admin setting changes do not apply until the URL override is removed.
  • Summary not available when a setting is configured: Removing all columns falls back to the setting columns, not Summary. Users cannot deliberately switch to Summary while the admin setting has values.

Out of scope (unchanged)

  • Per-service / per-environment scoping
  • Changing the global Discover default for all users
  • Custom field-picker UI for the setting

Test plan

  • Set message in APM General settings → Logs tab shows @timestamp + message
  • Save setting → Logs tab updates without page refresh
  • Add a column via the table → URL updates, both columns visible
  • Remove all columns with setting configured → returns to setting columns
  • Empty setting, no URL override → Summary default (@timestamp + Summary)
  • URL with custom logsColumns → URL columns win over setting
  • Clear admin setting while URL has columns → URL columns still shown
  • Other LazySavedSearchComponent consumers (Fleet, Infra, logs-overview) unaffected — resolveColumnsOnChange is opt-in and only passed from APM

@MiriamAparicio
MiriamAparicio requested a review from a team as a code owner June 29, 2026 12:20
@MiriamAparicio MiriamAparicio added the backport:skip This PR does not require backporting label Jun 29, 2026
@MiriamAparicio
MiriamAparicio requested review from a team as code owners June 29, 2026 12:20
@MiriamAparicio MiriamAparicio added Team:obs-presentation Focus: APM UI, Infra UI, Hosts UI, Universal Profiling, Obs Overview and left Navigation v9.5.0 labels Jun 29, 2026
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

Pinging @elastic/obs-presentation-team (Team:obs-presentation)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One inline comment on a code/comment mismatch in the kbn-saved-search-component effect dependency array. The rest of the change (column resolution helper with tests, settings subscription, URL-sync dedup) looks coherent and well-covered.

Generated by Claude Reviewer for issue #275365 · 124.4 AIC · ⌖ 12.1 AIC · ⊞ 3.9K

@MiriamAparicio
MiriamAparicio force-pushed the 274632-apm-setting-for-logs-columns branch from c3457dd to 62bb3f9 Compare June 29, 2026 12:49
@MiriamAparicio
MiriamAparicio force-pushed the 274632-apm-setting-for-logs-columns branch from d4171c1 to e6add87 Compare June 29, 2026 15:14

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One inline finding: setting-derived default columns can be auto-persisted to the logsColumns URL param on tab view, which then suppresses live setting updates and prevents clearing the setting from resetting affected users. The prior dep-array/comment mismatch is resolved, and the rest of the change (column-resolution helper with tests, settings subscription, URL-sync dedup) looks coherent.

Generated by Claude Reviewer for issue #275365 · 120.6 AIC · ⌖ 10.5 AIC · ⊞ 3.9K

@sbelastic sbelastic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a long while because of testing, just found one bug/improvement.

After changing the settings, and going back, the fields don't have the new values, you need to leave the page completely and go back to see the new fields.

bug1.mp4

@MiriamAparicio
MiriamAparicio force-pushed the 274632-apm-setting-for-logs-columns branch from 1aa1e7b to 156bffb Compare June 30, 2026 14:55
@MiriamAparicio

Copy link
Copy Markdown
Contributor Author

Hi @sbelastic , thanks for the review, fixed here 156bffb

Screen.Recording.2026-06-30.at.16.53.41.mov

@MiriamAparicio MiriamAparicio added the ci:project-deploy-observability Create an Observability project label Jun 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 GitHub comments

Expand to view the GitHub comments

Just comment with:

  • /oblt-deploy : Deploy a Kibana instance using the Observability test environments.
  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

@elastic-vault-github-plugin-prod

elastic-vault-github-plugin-prod Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Kibana Exploratory Testing — execution report

Run Metadata

  • Triggered by: Issue #691
  • Elasticsearch image tag: 9.5.0-SNAPSHOT
  • Kibana image: docker.elastic.co/kibana-ci/kibana-serverless:pr-275365-6880662ea230
  • Date: 2026-07-02
  • PR: elastic/kibana#275365 — Add advanced setting for default log columns in trace samples
  • Mode: PR-targeted (journeys generated from PR diff)
  • Journeys executed: 3
  • Passed: 3
  • Errored: 0
  • Findings: 1 bugs, 3 warnings, 1 info

Findings

Step 1: Verify base stream data via Elasticsearch API

Severity: warning
Description: The logs.otel.errors child stream exists but contains 0 documents, despite the seed data description indicating it should have ~25 error documents forked from logs.otel where severity_text == "error". This means the routing/forking mechanism may not have been set up correctly, or no error documents exist in the parent stream. Additionally, the document structure uses nested fields (attributes.severity_text, resource.attributes.host.name) rather than the flat structure described in the seed data, which may affect subsequent query testing.
Screenshot: findings/screenshots/03_api_preview_column_state_01_step1_elasticsearch_api.png
API Results:

  • logs.otel: 100 documents (correct)
  • logs.otel.errors: 0 documents (expected ~25)
  • Sample document shows nested OTel structure with attributes.severity_text="info"
  • No error severity documents found in parent stream

Step 5: Test significant events preview API

Severity: bug
Description: The significant events preview API endpoint documented in the reference (POST /api/streams/{name}/significant_events/_preview) returns 404 Not Found. The read endpoint (GET /api/streams/{name}/significant_events) works correctly and returns empty arrays for streams with no significant events. This prevents testing preview functionality before saving queries.
API Tested:

  • POST /api/streams/logs.otel/significant_events/_preview → 404 Not Found
  • GET /api/streams/logs.otel/significant_events → 200 OK (returns empty arrays)
    Expected: Preview endpoint should accept query body and return occurrences/change_points
    Actual: Endpoint returns 404, may be unimplemented or behind a feature flag
    Feature flags: All Streams flags are enabled (significant events, content packs, attachments)

Step 6: Test edge case - Simulate processing with zero documents

Severity: warning
Description: When simulating processing with an empty documents array, the API returns failed_rate: 1 (100%) in documents_metrics, which is misleading. With zero documents, there are no failures. This appears to be a calculation issue where 0/0 defaults to 1 instead of 0 or NaN. Other metrics correctly show 0.
API Response: { "documents_metrics": { "failed_rate": 1, "parsed_rate": 0, ... } }
Expected: With zero documents, all rates should be 0 or NaN/null to indicate no data
Actual: failed_rate shows 1 (100%), which falsely indicates complete failure
Impact: Could confuse users testing processors with empty datasets or cause incorrect UI state

Step 9: Test failure store samples API

Severity: warning
Description: The failure store samples API does not validate time range parameters. When called with an invalid time range where start > end (e.g., start=2027-12-31, end=2024-01-01), the API returns an empty documents array instead of a validation error. This could mask configuration errors or incorrect API usage by clients.
API Tested: GET /internal/streams/logs.otel/processing/_failure_store_samples?start=2027-12-31&end=2024-01-01
Expected: 400 Bad Request with validation error indicating invalid time range
Actual: 200 OK with { "documents": [] }, indistinguishable from a valid empty result
Impact: Developers may not realize they've passed incorrect parameters; no way to distinguish validation failure from legitimate empty results

Step 10: Cross-verify API data with UI

Severity: info
Description: There is a minor discrepancy between the unmapped_fields API response and what is displayed in the Schema tab UI. The API returns ["attributes.error.type", "attributes.severity_text"] as unmapped fields, but the UI only shows attributes.error.type as unmapped. The UI shows severity_text (without the attributes. prefix) as a mapped keyword field. These may be different fields (one is severity_text at the root level, the other is attributes.severity_text nested), or the UI may be filtering/consolidating the display.
Screenshots:

  • findings/screenshots/03_api_preview_column_state_10a_schema_tab.png
  • findings/screenshots/03_api_preview_column_state_10b_processing_tab.png
  • findings/screenshots/03_api_preview_column_state_10c_significant_events_tab.png
    API vs UI:
  • API unmapped: ["attributes.error.type", "attributes.severity_text"]
  • UI unmapped (visible): attributes.error.type only
  • UI mapped: severity_text (keyword) shown as mapped field
    Impact: Minor inconsistency; does not affect functionality but may cause confusion when comparing API and UI data
Additional details

Screenshots

Screenshots are available in the workflow artifacts.

  • 01_streams_discover_integration_login_complete.png
  • 02_streams_discover_integration_step1_partitioning_preview.png
  • 02_streams_preview_edge_cases_00_login_complete.png
  • 02_streams_preview_edge_cases_01a_partitioning_page.png
  • 02_streams_preview_edge_cases_01b_create_partition_form.png
  • 02_streams_preview_edge_cases_01c_zero_match_condition.png
  • 02_streams_preview_edge_cases_01d_unmatched_documents.png
  • 02_streams_preview_edge_cases_02a_rapid_operator_changes.png
  • 02_streams_preview_edge_cases_02b_rapid_field_changes_complete.png
  • 02_streams_preview_edge_cases_03_long_field_values.png
  • 02_streams_preview_edge_cases_04a_processing_tab.png
  • 02_streams_preview_edge_cases_04b_processor_menu.png
  • 02_streams_preview_edge_cases_final_summary.png
  • 03_api_preview_column_state_00_login_success.png
  • 03_api_preview_column_state_10a_schema_tab.png
  • 03_api_preview_column_state_10b_processing_tab.png
  • 03_api_preview_column_state_10c_significant_events_tab.png
  • 03_streams_discover_integration_step1_expanded_row.png
  • 04_streams_discover_integration_step2_create_partition_form.png
  • 05_streams_discover_integration_step2_routing_condition_set.png
  • 06_streams_discover_integration_step2_unmatched_docs.png
  • 08_streams_discover_integration_step3_discover_loaded.png
  • 09_streams_discover_integration_step4_custom_columns.png
  • 10_streams_discover_integration_step4_back_to_streams.png
  • 11_streams_discover_integration_step5_discover_fresh_tab.png
  • 12_streams_discover_integration_step6_schema_editor.png
  • 13_streams_discover_integration_final_state.png

Workflow run: View workflow run

@MiriamAparicio
MiriamAparicio force-pushed the 274632-apm-setting-for-logs-columns branch from 1458756 to e1752fc Compare July 1, 2026 06:54
@MiriamAparicio
MiriamAparicio requested review from a team as code owners July 1, 2026 06:54
@MiriamAparicio

Copy link
Copy Markdown
Contributor Author

/oblt-deploy

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ Update Documentation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sbelastic sbelastic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM, awesome job :)

@weltenwort weltenwort left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works as specified.

I'm impressed by how you found a way to intercept the "no columns" state without rendering the summary column temporarily. At the same time I question the choice of disallowing that state (see below). 😇

As I'm not aware of the finer-grained product discussions that happened around this feature, I'll leave it to you to judge whether it's worth fine-tuning that aspect.

Comment on lines +60 to +65
/**
* Called synchronously when the embeddable column state changes.
* Return column names to substitute Discover default/Summary columns; return
* undefined to keep the emitted columns.
*/
resolveColumnsOnChange?: (columns: string[] | undefined) => string[] | undefined;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a reader this might be confusing at this point and lead to questions like:

  • What does the "default" have to do with it? Does it use the columns I return or not?
  • Why return undefined and not just columns if I want to keep all of them?

Do you think anything can be done about this? For example, could this just be "use whatever columns this returns"? Do we need the undefined case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point , I can see how this can be misleading here. undefined doesn't mean "use defaults", it means "don't substitute, keep whatever the embeddable just emitted". A returned array means "replace the emitted columns with these".

We only use this in APM to intercept Discover's internal "no columns to Summary (_source)" transition and swap in the admin configured default columns instead. The word "default" in the comment refers to Discover's Summary state, not the APM advanced setting.

Happy to improve this in a follow-up, i.e. rename to something like substituteColumnsOnChange and document the two return semantics more explicitly. Didn't want to bikeshed the shared component API in this PR, but agree it's worth clarifying.

Maybe something like

/**
 * Called when the embeddable column state changes.
 * Return `undefined` to keep the emitted columns unchanged.
 * Return a column list to replace Discover's emitted columns (e.g. substitute Summary for app-specific columns).
 */

WDYT?


const SOURCE_COLUMN = '_source';

export function isDiscoverDefaultLogColumns(columns?: string[]): boolean {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me uneasy that this contains so many implicit assumptions and duplicate knowledge about how the saved search embeddable handles its column state. I don't see an obvious way to improve that, though, short of refactoring the unified data table.

Maybe the root of the problem is that we're conflating the "show the summary" state with the "show the default APM columns" state. Why are we disallowing the user to choose to remove all columns to see the summary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we're blocking "remove all columns" in the sense of resetting the table, users can still remove columns they added. The tricky part is that Discover treats "zero columns" as Summary (_source) internally (getDisplayedColumns), so "remove everything" and "show Summary" are the same state inside the embeddable.

When an admin setting is configured, we intentionally substitute that Summary transition with the setting columns instead, that was discussed with product for this. If the setting is empty, Summary is still reachable.

Agree the helper duplicates some Discover column semantics and it would be cleaner if unified data table exposed a hook for "empty columns fallback" , I didn't find a less invasive way without a larger refactor. Open to revisiting whether users should be able to explicitly choose Summary while a setting exists, but that would be a separate product decision from this PR.

@kibanamachine

kibanamachine commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

  • Buildkite Build
  • Commit: 6880662
  • Build duration: 65 mins
  • Kibana Serverless Image: docker.elastic.co/kibana-ci/kibana-serverless:pr-275365-6880662ea230

Failed CI Steps

Test Failures

  • [job] [logs] Scout Lane #53 - serverless-observability_complete / default / local-serverless-observability_complete - Alerting Rule - when rule is deleted, apiKey and uiamApiKey are queued for invalidation
  • [job] [logs] Scout Lane #53 - serverless-observability_complete / default / local-serverless-observability_complete - Alerting Rule - when rule is updated, apiKey and uiamApiKey are queued for invalidation
  • [job] [logs] Scout Lane #54 - serverless-observability_complete / default / local-serverless-observability_complete - Hosts Page - Empty State - should show onboarding page when no data is present
  • [job] [logs] Scout Lane #10 - stateful-classic / default / local-stateful-classic - UptimeIntegrationDeprecation - returns true when non-managed synthetics policies exist

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
apm 2395 2396 +1

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
apm 3.1MB 3.1MB +1.7KB
fleet 2.8MB 2.8MB +386.0B
infra 1.5MB 1.5MB +386.0B
logsShared 103.1KB 103.5KB +386.0B
total +2.8KB

Page load bundle

Size of the bundles that are downloaded on every page load. Target size is below 100kb

id before after diff
observability 102.7KB 102.8KB +90.0B

History

@MiriamAparicio
MiriamAparicio merged commit 702c398 into elastic:main Jul 2, 2026
39 checks passed
MykhailoKondrat pushed a commit to MykhailoKondrat/kibana that referenced this pull request Jul 2, 2026
…c#275365)

Closes elastic#274632  

## Summary

Adds an advanced setting (`observability:apmTraceLogsDefaultColumns`) so
APM admins can configure default columns for the **Trace samples →
Logs** tab.

**Column resolution priority:**
1. URL `logsColumns` param — when present with custom (non-Summary)
columns
2. Advanced setting — when non-empty
3. Discover default — `@timestamp` + Summary (`_source`)

**Example:** setting `["message"]` shows `@timestamp` + `message`
instead of Summary.

### Changes

- Register `observability:apmTraceLogsDefaultColumns` advanced setting
(technical preview, array of field names, default `[]`)
- Surface in **APM → Settings → General settings**
- Apply setting in trace samples Logs tab via `getTraceLogsColumns()`
merge helper
- Read setting via `settings.client` with live subscription (no page
reload after save)
- URL sync: do not persist Discover Summary/`_source` defaults to
`logsColumns`
- Add optional `resolveColumnsOnChange` to `@kbn/saved-search-component`
(APM-only) to prevent a Summary flash when users remove all columns and
a setting is configured

### How it works (confirmed with product)

URL session state takes precedence over the admin setting when
`logsColumns` contains custom columns. This is intentional:

- **URL wins over setting:** If a user customizes columns in the table,
those columns are written to the URL and override the admin setting for
that session.
- **Clearing the setting does not clear URL overrides:** An admin
clearing the setting does not reset users who already have `logsColumns`
in the URL. Users must reset the table or navigate without the param.
- **Setting changes are ignored while URL overrides exist:** If a user
has session columns in the URL, admin setting changes do not apply until
the URL override is removed.
- **Summary not available when a setting is configured:** Removing all
columns falls back to the setting columns, not Summary. Users cannot
deliberately switch to Summary while the admin setting has values.

### Out of scope (unchanged)

- Per-service / per-environment scoping
- Changing the global Discover default for all users
- Custom field-picker UI for the setting

## Test plan

- [ ] Set `message` in APM General settings → Logs tab shows
`@timestamp` + `message`
- [ ] Save setting → Logs tab updates without page refresh
- [ ] Add a column via the table → URL updates, both columns visible
- [ ] Remove all columns with setting configured → returns to setting
columns
- [ ] Empty setting, no URL override → Summary default (`@timestamp` +
Summary)
- [ ] URL with custom `logsColumns` → URL columns win over setting
- [ ] Clear admin setting while URL has columns → URL columns still
shown
- [ ] Other `LazySavedSearchComponent` consumers (Fleet, Infra,
logs-overview) unaffected — `resolveColumnsOnChange` is opt-in and only
passed from APM

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting ci:project-deploy-observability Create an Observability project release_note:enhancement Team:obs-presentation Focus: APM UI, Infra UI, Hosts UI, Universal Profiling, Obs Overview and left Navigation v9.5.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[APM] Add advanced setting for default log columns in trace samples

6 participants