Skip to content

[Discover][Logs profile] Fix missing search highlights#260056

Merged
tfcmarques merged 6 commits intoelastic:mainfrom
tfcmarques:259407-search-highlights-missing-logs-profile
Mar 31, 2026
Merged

[Discover][Logs profile] Fix missing search highlights#260056
tfcmarques merged 6 commits intoelastic:mainfrom
tfcmarques:259407-search-highlights-missing-logs-profile

Conversation

@tfcmarques
Copy link
Copy Markdown
Contributor

@tfcmarques tfcmarques commented Mar 27, 2026

Summary

Fixes search result highlighting not working in the Logs profile summary column and content breakdown.

Important

This should be seen as a temporary fix, while the team works on React-based field formatters

Closes #259407

Root cause

The message value was read from row.flattened, which doesn't contain ES highlights. The highlight snippets can be found in row.raw.highlight[field] with @kibana-highlighted-field@ tags, but were never passed to the rendering pipeline, after recent changes to the implementation.

Approaches considered

We explored different ways to get the highlighted value into the message rendering.

First approach was to reuse getLogDocumentOverview, which had been removed to solve an issue with OTel documents, but it called formatFieldValue internally. That receives the full ES hit, the field formatter would convert the ES tags to <mark> HTML tags automatically, so that we could then keep our current logic working. The idea was to get the field name from getMessageFieldWithFallbacks(row.flattened) (which handles OTel fallback) and the already highlighted value from getMessageFieldWithFallbacks(documentOverview).

However, this approach didn't work for two reasons:

  • There's a field resolution priority mismatch, where getMessageFieldWithFallbacks checks OTel fields first (body.text before message), while getFieldValueWithFallback inside getLogDocumentOverview checks ECS first (message before body.text). If a doc has both, the field name and value could come from different sources.

  • formatFieldValue passes the ECS field name to dataView.fields.getByName(), which can return undefined for OTel docs without aliases. When that happens, the field formatter can't look up hit.highlight[field.name] and silently skips highlighting entirely. In testing, highlights didn't appear.

The final approach reads row.raw.highlight[field] directly using the correctly-resolved field name from getMessageFieldWithFallbacks(row.flattened), bypassing the field formatter altogether. A new getHighlightedFieldValue utility replicates the logic of getHighlightHtml from @kbn/field-formats-plugin (which packages can't import directly), merging all ES highlight snippets into the escaped field value — handling multi-valued fields and multiple highlight regions within a single value. escapeAndPreserveHighlightTags was simplified back to only handle HTML <mark> tags (used by resource badges that receive pre-formatted values from formatFieldValue). Also, we fixed a pre-existing undetected bug where multiple highlight regions were silently dropped due to a markTags.length === 2 condition that only allowed single keyword highlighting.

Changes

  • content.tsx / summary_column.tsx / content_breakdown.tsx

    • Read the full ES highlight array from row.raw.highlight[field] (not just [0]) and pass it with the plain field value to getHighlightedFieldValue, which merges all snippets and produces XSS-safe HTML.
  • highlight_utils.ts (renamed from escape_preserve_highlight_tags.ts)

    • Added getHighlightedFieldValue(fieldValue, highlights), directly replicating getHighlightHtml from @kbn/field-formats-plugin, iterating over all ES highlight snippets, stripping ES tags to find matching text, and replacing occurrences in the escaped field value with <mark> elements. Handles multi-valued fields and multiple highlight regions.
    • Simplified escapeAndPreserveHighlightTags to only handle HTML <mark> tags (its sole remaining use case in cell_actions_popover.tsx for resource badges).

Demo

Screen.Recording.2026-03-27.at.17.16.26.mov

@tfcmarques tfcmarques self-assigned this Mar 27, 2026
@tfcmarques tfcmarques added Feature:Discover Discover Application release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Team:obs-exploration Observability Exploration team labels Mar 27, 2026
@tfcmarques tfcmarques marked this pull request as ready for review March 27, 2026 17:17
@tfcmarques tfcmarques requested review from a team as code owners March 27, 2026 17:17
@elasticmachine
Copy link
Copy Markdown
Contributor

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

@elasticmachine
Copy link
Copy Markdown
Contributor

💛 Build succeeded, but was flaky

Failed CI Steps

Test Failures

  • [job] [logs] Scout: [ security / entity_store ] plugin / local-serverless-security_complete - Entity Store History Snapshot - history snapshot: copies latest to history index and resets behaviors on latest

Metrics [docs]

Public APIs missing comments

Total count of every public API that lacks a comment. Target amount is 0. Run node scripts/build_api_docs --plugin [yourplugin] --stats comments for more detailed information.

id before after diff
@kbn/discover-utils 372 373 +1

Async chunks

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

id before after diff
discover 1.6MB 1.6MB +582.0B
streamsApp 1.8MB 1.8MB +575.0B
unifiedDocViewer 421.4KB 421.6KB +226.0B
total +1.4KB
Unknown metric groups

API count

id before after diff
@kbn/discover-utils 444 447 +3

History

cc @tfcmarques

Copy link
Copy Markdown
Contributor

@davismcphee davismcphee left a comment

Choose a reason for hiding this comment

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

Thanks for looking at this! I don't remember the full context for some of these changes originally, so just looking to better understand.

Nothing stood out to me specifically in the code changes, but it's always a bit risky working with HTML directly. I think it's worth getting AI review on this one to see if it catches anything.

* Escapes HTML in a string while preserving field-format highlight <mark> tags.
* Used for values already processed by formatFieldValue / getHighlightHtml (e.g. resource badges).
*/
export function escapeAndPreserveHighlightTags(value: string): string {
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.

I'm a bit confused about the purpose of this function. Values run through an HTML formatter with formatFieldValue should already be HTML escaped since that's it's purpose. Can you help me understand why it's needed?

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.

escapeAndPreserveHighlightTags was introduced in
#253210 as a defense measure since the values are rendered via dangerouslySetInnerHTML. My PR didn't introduce it, only updated it to handle multiple highlights (it was previously limited to two mark tags). I don't think any logic changed since then, so I kept that fix wherever it was needed still.

We can work on removing these temporary fixes once #259286 is completed.

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.

Thanks for explaining. I'm still not sure this function actually does anything tbh, but it doesn't seem to introduce new risk, so I'll leave it up to you folks to decide.

/**
* Merges ES highlight snippets into a field value, producing safe HTML with <mark> tags.
* Replicates the logic of getHighlightHtml from @kbn/field-formats-plugin, which packages
* cannot import directly.
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.

Do we need to duplicate the field formats logic here? The components which use this should already have access to the field formats plugin. Can we not just use formatFieldValue directly?

Copy link
Copy Markdown
Contributor Author

@tfcmarques tfcmarques Mar 30, 2026

Choose a reason for hiding this comment

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

We tried using formatFieldValue directly (I left a quick summary about this in the PR description). The issue is that formatFieldValue needs a DataViewField from dataView.fields.getByName(field) to look up highlights via field.name. For OTel documents, getMessageFieldWithFallbacks can resolve to body.text, which may not exist in the data view. When getByName returns undefined, the string formatter's htmlConvert can't look up hit.highlight[field.name] and silently skips highlighting.

Additionally, this component applies custom log-level coloring via getHighlightedMessage (wrapping ERROR, WARN, etc. in colored spans), which needs to run on the value after highlight processing. formatFieldValue would give the same output format, but the undefined field issue blocks it.

The duplication is minimal (just the core split/join logic from getHighlightHtml) and explicitly documented as a workaround for the package/plugin boundary. Happy to add a TODO to remove these duplicates, linking to the React-based field formatters work.

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.

Thanks for clarifying. I think the fact we need to do this points to an issue around the OTel docs logic, but it doesn't seem to add new risk, so I won't block on it. But for clarity, you'll still need to provide a field when React field formatters land, so you'll probably need to fix the OTel docs logic soon regardless.

@davismcphee davismcphee added reviewer:coderabbit PR review with CodeRabbit reviewer:macroscope PR review with Macroscope labels Mar 30, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 2a3d3505-e67f-4634-a7d5-c1470f11abba

📥 Commits

Reviewing files that changed from the base of the PR and between 3e21cc2 and c26f565.

📒 Files selected for processing (9)
  • src/platform/packages/shared/kbn-discover-contextual-components/src/data_types/logs/components/summary_column/content.tsx
  • src/platform/packages/shared/kbn-discover-contextual-components/src/data_types/logs/components/summary_column/summary_column.tsx
  • src/platform/packages/shared/kbn-discover-utils/index.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/escape_preserve_highlight_tags.test.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/escape_preserve_highlight_tags.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/highlight_utils.test.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/highlight_utils.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/index.ts
  • src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_logs_overview/sub_components/content_breakdown/content_breakdown.tsx
💤 Files with no reviewable changes (2)
  • src/platform/packages/shared/kbn-discover-utils/src/utils/escape_preserve_highlight_tags.test.ts
  • src/platform/packages/shared/kbn-discover-utils/src/utils/escape_preserve_highlight_tags.ts

📝 Walkthrough

Walkthrough

The pull request refactors highlight tag handling across the Discover package. The original escapeAndPreserveHighlightTags function in escape_preserve_highlight_tags.ts was removed and replaced with a new highlight_utils.ts module containing two functions: a renamed escapeAndPreserveHighlightTags and a new getHighlightedFieldValue that merges Elasticsearch highlight snippets into field values. Multiple components in kbn-discover-contextual-components and unified_doc_viewer now use getHighlightedFieldValue, passing both field values and highlight arrays to generate properly escaped HTML with highlight markers. The public API surface was updated accordingly.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • 🛠️ Update Documentation: Commit on current branch
  • 🛠️ Update Documentation: Create PR

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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

Copy link
Copy Markdown
Contributor

@iblancof iblancof left a comment

Choose a reason for hiding this comment

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

I’m not a big fan of the duplicated logics we seem to be adding, but as I understand it, this is meant to be a temporary solution until the issues in [Discover][FieldFormatters] Migrate html field formatters to react gets done (which seem to be already in progress) are addressed.

@tfcmarques
Copy link
Copy Markdown
Contributor Author

Added a TODO comment on highlight_utils.ts file so that it is clear this is a temporary workaround.

Also gave the PR link to a fresh context Cursor Agent, no risks raised regarding these changes 🙇🏼

@tfcmarques tfcmarques requested a review from davismcphee March 30, 2026 11:20
@iblancof
Copy link
Copy Markdown
Contributor

@tfcmarques I see this is not meant to be backported (backport:skip) but we should, right?

The PR that introduced this new logic was backported:

@tfcmarques tfcmarques added backport:version Backport to applied version labels v9.3.0 v9.4.0 v9.3.1 v9.3.2 and removed backport:skip This PR does not require backporting labels Mar 30, 2026
Copy link
Copy Markdown
Contributor

@davismcphee davismcphee left a comment

Choose a reason for hiding this comment

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

I'm iffy on the changes overall since I'm not sure they get to the root of the issue even leaving aside React field formatters, but they don't seem to introduce new risk afaict, so no need to block on my end 👍

* Escapes HTML in a string while preserving field-format highlight <mark> tags.
* Used for values already processed by formatFieldValue / getHighlightHtml (e.g. resource badges).
*/
export function escapeAndPreserveHighlightTags(value: string): string {
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.

Thanks for explaining. I'm still not sure this function actually does anything tbh, but it doesn't seem to introduce new risk, so I'll leave it up to you folks to decide.

/**
* Merges ES highlight snippets into a field value, producing safe HTML with <mark> tags.
* Replicates the logic of getHighlightHtml from @kbn/field-formats-plugin, which packages
* cannot import directly.
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.

Thanks for clarifying. I think the fact we need to do this points to an issue around the OTel docs logic, but it doesn't seem to add new risk, so I won't block on it. But for clarity, you'll still need to provide a field when React field formatters land, so you'll probably need to fix the OTel docs logic soon regardless.

@tfcmarques tfcmarques merged commit 7346059 into elastic:main Mar 31, 2026
31 checks passed
@kibanamachine
Copy link
Copy Markdown
Contributor

Starting backport for target branches: 9.3

https://github.com/elastic/kibana/actions/runs/23788311731

kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Mar 31, 2026
## Summary

Fixes search result highlighting not working in the Logs profile summary
column and content breakdown.

> [!IMPORTANT]
> This should be seen as a temporary fix, while the team works on
React-based field formatters

Closes elastic#259407

### Root cause

The message value was read from `row.flattened`, which doesn't contain
ES highlights. The highlight snippets can be found in
`row.raw.highlight[field]` with `@kibana-highlighted-field@` tags, but
were never passed to the rendering pipeline, after recent changes to the
implementation.

### Approaches considered

We explored different ways to get the highlighted value into the message
rendering.

First approach was to reuse `getLogDocumentOverview`, which had been
removed to solve an issue with OTel documents, but it called
`formatFieldValue` internally. That receives the full ES hit, the field
formatter would convert the ES tags to `<mark>` HTML tags automatically,
so that we could then keep our current logic working. The idea was to
get the field name from `getMessageFieldWithFallbacks(row.flattened)`
(which handles OTel fallback) and the already highlighted value from
`getMessageFieldWithFallbacks(documentOverview)`.

However, this approach didn't work for two reasons:
- There's a field resolution priority mismatch, where
`getMessageFieldWithFallbacks` checks OTel fields first (`body.text`
before `message`), while `getFieldValueWithFallback` inside
`getLogDocumentOverview` checks ECS first (`message` before
`body.text`). If a doc has both, the field name and value could come
from different sources.

- `formatFieldValue` passes the ECS field name to
`dataView.fields.getByName()`, which can return `undefined` for OTel
docs without aliases. When that happens, the field formatter can't look
up `hit.highlight[field.name]` and silently skips highlighting entirely.
In testing, highlights didn't appear.

The final approach reads `row.raw.highlight[field]` directly using the
correctly-resolved field name from
`getMessageFieldWithFallbacks(row.flattened)`, bypassing the field
formatter altogether. A new `getHighlightedFieldValue` utility
replicates the logic of `getHighlightHtml` from
`@kbn/field-formats-plugin` (which packages can't import directly),
merging all ES highlight snippets into the escaped field value —
handling multi-valued fields and multiple highlight regions within a
single value. `escapeAndPreserveHighlightTags` was simplified back to
only handle HTML `<mark>` tags (used by resource badges that receive
pre-formatted values from `formatFieldValue`). Also, we fixed a
pre-existing undetected bug where multiple highlight regions were
silently dropped due to a `markTags.length === 2` condition that only
allowed single keyword highlighting.

### Changes

- **`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**
- Read the full ES highlight array from `row.raw.highlight[field]` (not
just `[0]`) and pass it with the plain field value to
`getHighlightedFieldValue`, which merges all snippets and produces
XSS-safe HTML.

- **`highlight_utils.ts`** (renamed from
`escape_preserve_highlight_tags.ts`)
- Added `getHighlightedFieldValue(fieldValue, highlights)`, directly
replicating `getHighlightHtml` from `@kbn/field-formats-plugin`,
iterating over all ES highlight snippets, stripping ES tags to find
matching text, and replacing occurrences in the escaped field value with
`<mark>` elements. Handles multi-valued fields and multiple highlight
regions.
- Simplified `escapeAndPreserveHighlightTags` to only handle HTML
`<mark>` tags (its sole remaining use case in `cell_actions_popover.tsx`
for resource badges).

### Demo

https://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00
(cherry picked from commit 7346059)
@kibanamachine
Copy link
Copy Markdown
Contributor

💚 All backports created successfully

Status Branch Result
9.3

Note: Successful backport PRs will be merged automatically after passing CI.

Questions ?

Please refer to the Backport tool documentation

kibanamachine added a commit that referenced this pull request Mar 31, 2026
#260397)

# Backport

This will backport the following commits from `main` to `9.3`:
- [[Discover][Logs profile] Fix missing search highlights
(#260056)](#260056)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Tiago
Marques","email":"tiago.marques@elastic.co"},"sourceCommit":{"committedDate":"2026-03-31T08:38:04Z","message":"[Discover][Logs
profile] Fix missing search highlights (#260056)\n\n## Summary\n\nFixes
search result highlighting not working in the Logs profile
summary\ncolumn and content breakdown.\n\n> [!IMPORTANT] \n> This should
be seen as a temporary fix, while the team works on\nReact-based field
formatters\n\nCloses #259407\n\n### Root cause\n\nThe message value was
read from `row.flattened`, which doesn't contain\nES highlights. The
highlight snippets can be found in\n`row.raw.highlight[field]` with
`@kibana-highlighted-field@` tags, but\nwere never passed to the
rendering pipeline, after recent changes to the\nimplementation.\n\n###
Approaches considered\n\nWe explored different ways to get the
highlighted value into the message\nrendering.\n\nFirst approach was to
reuse `getLogDocumentOverview`, which had been\nremoved to solve an
issue with OTel documents, but it called\n`formatFieldValue` internally.
That receives the full ES hit, the field\nformatter would convert the ES
tags to `<mark>` HTML tags automatically,\nso that we could then keep
our current logic working. The idea was to\nget the field name from
`getMessageFieldWithFallbacks(row.flattened)`\n(which handles OTel
fallback) and the already highlighted value
from\n`getMessageFieldWithFallbacks(documentOverview)`.\n\nHowever, this
approach didn't work for two reasons:\n- There's a field resolution
priority mismatch, where\n`getMessageFieldWithFallbacks` checks OTel
fields first (`body.text`\nbefore `message`), while
`getFieldValueWithFallback` inside\n`getLogDocumentOverview` checks ECS
first (`message` before\n`body.text`). If a doc has both, the field name
and value could come\nfrom different sources.\n\n- `formatFieldValue`
passes the ECS field name to\n`dataView.fields.getByName()`, which can
return `undefined` for OTel\ndocs without aliases. When that happens,
the field formatter can't look\nup `hit.highlight[field.name]` and
silently skips highlighting entirely.\nIn testing, highlights didn't
appear.\n\nThe final approach reads `row.raw.highlight[field]` directly
using the\ncorrectly-resolved field name
from\n`getMessageFieldWithFallbacks(row.flattened)`, bypassing the
field\nformatter altogether. A new `getHighlightedFieldValue`
utility\nreplicates the logic of `getHighlightHtml`
from\n`@kbn/field-formats-plugin` (which packages can't import
directly),\nmerging all ES highlight snippets into the escaped field
value —\nhandling multi-valued fields and multiple highlight regions
within a\nsingle value. `escapeAndPreserveHighlightTags` was simplified
back to\nonly handle HTML `<mark>` tags (used by resource badges that
receive\npre-formatted values from `formatFieldValue`). Also, we fixed
a\npre-existing undetected bug where multiple highlight regions
were\nsilently dropped due to a `markTags.length === 2` condition that
only\nallowed single keyword highlighting.\n\n### Changes\n\n-
**`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**\n-
Read the full ES highlight array from `row.raw.highlight[field]`
(not\njust `[0]`) and pass it with the plain field value
to\n`getHighlightedFieldValue`, which merges all snippets and
produces\nXSS-safe HTML.\n\n- **`highlight_utils.ts`** (renamed
from\n`escape_preserve_highlight_tags.ts`)\n- Added
`getHighlightedFieldValue(fieldValue, highlights)`,
directly\nreplicating `getHighlightHtml` from
`@kbn/field-formats-plugin`,\niterating over all ES highlight snippets,
stripping ES tags to find\nmatching text, and replacing occurrences in
the escaped field value with\n`<mark>` elements. Handles multi-valued
fields and multiple highlight\nregions.\n- Simplified
`escapeAndPreserveHighlightTags` to only handle HTML\n`<mark>` tags (its
sole remaining use case in `cell_actions_popover.tsx`\nfor resource
badges).\n\n###
Demo\n\n\nhttps://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00","sha":"7346059bf4f80300165ad809f27ebecc6142f7f1","branchLabelMapping":{"^v9.4.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:Discover","release_note:skip","backport:version","v9.3.0","Team:obs-exploration","v9.4.0","v9.3.1","v9.3.2","reviewer:coderabbit","v9.3.3","reviewer:macroscope"],"title":"[Discover][Logs
profile] Fix missing search
highlights","number":260056,"url":"https://github.com/elastic/kibana/pull/260056","mergeCommit":{"message":"[Discover][Logs
profile] Fix missing search highlights (#260056)\n\n## Summary\n\nFixes
search result highlighting not working in the Logs profile
summary\ncolumn and content breakdown.\n\n> [!IMPORTANT] \n> This should
be seen as a temporary fix, while the team works on\nReact-based field
formatters\n\nCloses #259407\n\n### Root cause\n\nThe message value was
read from `row.flattened`, which doesn't contain\nES highlights. The
highlight snippets can be found in\n`row.raw.highlight[field]` with
`@kibana-highlighted-field@` tags, but\nwere never passed to the
rendering pipeline, after recent changes to the\nimplementation.\n\n###
Approaches considered\n\nWe explored different ways to get the
highlighted value into the message\nrendering.\n\nFirst approach was to
reuse `getLogDocumentOverview`, which had been\nremoved to solve an
issue with OTel documents, but it called\n`formatFieldValue` internally.
That receives the full ES hit, the field\nformatter would convert the ES
tags to `<mark>` HTML tags automatically,\nso that we could then keep
our current logic working. The idea was to\nget the field name from
`getMessageFieldWithFallbacks(row.flattened)`\n(which handles OTel
fallback) and the already highlighted value
from\n`getMessageFieldWithFallbacks(documentOverview)`.\n\nHowever, this
approach didn't work for two reasons:\n- There's a field resolution
priority mismatch, where\n`getMessageFieldWithFallbacks` checks OTel
fields first (`body.text`\nbefore `message`), while
`getFieldValueWithFallback` inside\n`getLogDocumentOverview` checks ECS
first (`message` before\n`body.text`). If a doc has both, the field name
and value could come\nfrom different sources.\n\n- `formatFieldValue`
passes the ECS field name to\n`dataView.fields.getByName()`, which can
return `undefined` for OTel\ndocs without aliases. When that happens,
the field formatter can't look\nup `hit.highlight[field.name]` and
silently skips highlighting entirely.\nIn testing, highlights didn't
appear.\n\nThe final approach reads `row.raw.highlight[field]` directly
using the\ncorrectly-resolved field name
from\n`getMessageFieldWithFallbacks(row.flattened)`, bypassing the
field\nformatter altogether. A new `getHighlightedFieldValue`
utility\nreplicates the logic of `getHighlightHtml`
from\n`@kbn/field-formats-plugin` (which packages can't import
directly),\nmerging all ES highlight snippets into the escaped field
value —\nhandling multi-valued fields and multiple highlight regions
within a\nsingle value. `escapeAndPreserveHighlightTags` was simplified
back to\nonly handle HTML `<mark>` tags (used by resource badges that
receive\npre-formatted values from `formatFieldValue`). Also, we fixed
a\npre-existing undetected bug where multiple highlight regions
were\nsilently dropped due to a `markTags.length === 2` condition that
only\nallowed single keyword highlighting.\n\n### Changes\n\n-
**`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**\n-
Read the full ES highlight array from `row.raw.highlight[field]`
(not\njust `[0]`) and pass it with the plain field value
to\n`getHighlightedFieldValue`, which merges all snippets and
produces\nXSS-safe HTML.\n\n- **`highlight_utils.ts`** (renamed
from\n`escape_preserve_highlight_tags.ts`)\n- Added
`getHighlightedFieldValue(fieldValue, highlights)`,
directly\nreplicating `getHighlightHtml` from
`@kbn/field-formats-plugin`,\niterating over all ES highlight snippets,
stripping ES tags to find\nmatching text, and replacing occurrences in
the escaped field value with\n`<mark>` elements. Handles multi-valued
fields and multiple highlight\nregions.\n- Simplified
`escapeAndPreserveHighlightTags` to only handle HTML\n`<mark>` tags (its
sole remaining use case in `cell_actions_popover.tsx`\nfor resource
badges).\n\n###
Demo\n\n\nhttps://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00","sha":"7346059bf4f80300165ad809f27ebecc6142f7f1"}},"sourceBranch":"main","suggestedTargetBranches":["9.3"],"targetPullRequestStates":[{"branch":"9.3","label":"v9.3.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.4.0","branchLabelMappingKey":"^v9.4.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/260056","number":260056,"mergeCommit":{"message":"[Discover][Logs
profile] Fix missing search highlights (#260056)\n\n## Summary\n\nFixes
search result highlighting not working in the Logs profile
summary\ncolumn and content breakdown.\n\n> [!IMPORTANT] \n> This should
be seen as a temporary fix, while the team works on\nReact-based field
formatters\n\nCloses #259407\n\n### Root cause\n\nThe message value was
read from `row.flattened`, which doesn't contain\nES highlights. The
highlight snippets can be found in\n`row.raw.highlight[field]` with
`@kibana-highlighted-field@` tags, but\nwere never passed to the
rendering pipeline, after recent changes to the\nimplementation.\n\n###
Approaches considered\n\nWe explored different ways to get the
highlighted value into the message\nrendering.\n\nFirst approach was to
reuse `getLogDocumentOverview`, which had been\nremoved to solve an
issue with OTel documents, but it called\n`formatFieldValue` internally.
That receives the full ES hit, the field\nformatter would convert the ES
tags to `<mark>` HTML tags automatically,\nso that we could then keep
our current logic working. The idea was to\nget the field name from
`getMessageFieldWithFallbacks(row.flattened)`\n(which handles OTel
fallback) and the already highlighted value
from\n`getMessageFieldWithFallbacks(documentOverview)`.\n\nHowever, this
approach didn't work for two reasons:\n- There's a field resolution
priority mismatch, where\n`getMessageFieldWithFallbacks` checks OTel
fields first (`body.text`\nbefore `message`), while
`getFieldValueWithFallback` inside\n`getLogDocumentOverview` checks ECS
first (`message` before\n`body.text`). If a doc has both, the field name
and value could come\nfrom different sources.\n\n- `formatFieldValue`
passes the ECS field name to\n`dataView.fields.getByName()`, which can
return `undefined` for OTel\ndocs without aliases. When that happens,
the field formatter can't look\nup `hit.highlight[field.name]` and
silently skips highlighting entirely.\nIn testing, highlights didn't
appear.\n\nThe final approach reads `row.raw.highlight[field]` directly
using the\ncorrectly-resolved field name
from\n`getMessageFieldWithFallbacks(row.flattened)`, bypassing the
field\nformatter altogether. A new `getHighlightedFieldValue`
utility\nreplicates the logic of `getHighlightHtml`
from\n`@kbn/field-formats-plugin` (which packages can't import
directly),\nmerging all ES highlight snippets into the escaped field
value —\nhandling multi-valued fields and multiple highlight regions
within a\nsingle value. `escapeAndPreserveHighlightTags` was simplified
back to\nonly handle HTML `<mark>` tags (used by resource badges that
receive\npre-formatted values from `formatFieldValue`). Also, we fixed
a\npre-existing undetected bug where multiple highlight regions
were\nsilently dropped due to a `markTags.length === 2` condition that
only\nallowed single keyword highlighting.\n\n### Changes\n\n-
**`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**\n-
Read the full ES highlight array from `row.raw.highlight[field]`
(not\njust `[0]`) and pass it with the plain field value
to\n`getHighlightedFieldValue`, which merges all snippets and
produces\nXSS-safe HTML.\n\n- **`highlight_utils.ts`** (renamed
from\n`escape_preserve_highlight_tags.ts`)\n- Added
`getHighlightedFieldValue(fieldValue, highlights)`,
directly\nreplicating `getHighlightHtml` from
`@kbn/field-formats-plugin`,\niterating over all ES highlight snippets,
stripping ES tags to find\nmatching text, and replacing occurrences in
the escaped field value with\n`<mark>` elements. Handles multi-valued
fields and multiple highlight\nregions.\n- Simplified
`escapeAndPreserveHighlightTags` to only handle HTML\n`<mark>` tags (its
sole remaining use case in `cell_actions_popover.tsx`\nfor resource
badges).\n\n###
Demo\n\n\nhttps://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00","sha":"7346059bf4f80300165ad809f27ebecc6142f7f1"}}]}]
BACKPORT-->

Co-authored-by: Tiago Marques <tiago.marques@elastic.co>
mbondyra added a commit to mbondyra/kibana that referenced this pull request Mar 31, 2026
…e_for_children6

* commit '3402744f63ca1196e97b11ffac4e7f7efab240df': (80 commits)
  [PerUserAuth] Add EARS auth type for Connectors V2 (elastic#253695)
  Fix `@elastic/eui/require-aria-label-for-modals` lint violations across `@elastic/kibana-core` files (elastic#259757)
  [Entity Analytics][Leads generation][4] Add API routes, LeadDataClient, and async generation (elastic#257046)
  [Agent Builder] Agent-centric UX redesign (elastic#258005)
  fix query streams failing test (elastic#260277)
  [Lens as code] Add list layout to the new API (elastic#259967)
  [FTR] Add warning comments to deployment-agnostic FTR base configs (elastic#260018)
  [Discover][Logs profile] Fix missing search highlights (elastic#260056)
  Plugin system: safe deletion (elastic#259038)
  [Infra] Fix Hosts filter options to match selected schema (elastic#259825)
  Manual Entity Resolution and flyout representation (elastic#260162)
  [Cascade] Handle grouping on fields with unset values (elastic#260033)
  [Fleet] generate OTel config for integration packages with otelcol inputs (elastic#259968)
  [Search] Switch over to V2 index management details (elastic#259866)
  [inference] increase timeout for ES inference calls (elastic#260382)
  [ES|QL] Enable subqueries (elastic#257455)
  [ES|QL] Change Point order free options (elastic#260282)
  [Auth] Added authentication strategy for UIAM OAuth (elastic#256182)
  [Security Solution] Add "alerts_candidate_count" rule execution metric (elastic#259917)
  [api-docs] 2026-03-31 Daily api_docs build (elastic#260380)
  ...
jeramysoucy pushed a commit to jeramysoucy/kibana that referenced this pull request Apr 1, 2026
## Summary

Fixes search result highlighting not working in the Logs profile summary
column and content breakdown.

> [!IMPORTANT]  
> This should be seen as a temporary fix, while the team works on
React-based field formatters

Closes elastic#259407

### Root cause

The message value was read from `row.flattened`, which doesn't contain
ES highlights. The highlight snippets can be found in
`row.raw.highlight[field]` with `@kibana-highlighted-field@` tags, but
were never passed to the rendering pipeline, after recent changes to the
implementation.

### Approaches considered

We explored different ways to get the highlighted value into the message
rendering.

First approach was to reuse `getLogDocumentOverview`, which had been
removed to solve an issue with OTel documents, but it called
`formatFieldValue` internally. That receives the full ES hit, the field
formatter would convert the ES tags to `<mark>` HTML tags automatically,
so that we could then keep our current logic working. The idea was to
get the field name from `getMessageFieldWithFallbacks(row.flattened)`
(which handles OTel fallback) and the already highlighted value from
`getMessageFieldWithFallbacks(documentOverview)`.

However, this approach didn't work for two reasons:
- There's a field resolution priority mismatch, where
`getMessageFieldWithFallbacks` checks OTel fields first (`body.text`
before `message`), while `getFieldValueWithFallback` inside
`getLogDocumentOverview` checks ECS first (`message` before
`body.text`). If a doc has both, the field name and value could come
from different sources.

- `formatFieldValue` passes the ECS field name to
`dataView.fields.getByName()`, which can return `undefined` for OTel
docs without aliases. When that happens, the field formatter can't look
up `hit.highlight[field.name]` and silently skips highlighting entirely.
In testing, highlights didn't appear.

The final approach reads `row.raw.highlight[field]` directly using the
correctly-resolved field name from
`getMessageFieldWithFallbacks(row.flattened)`, bypassing the field
formatter altogether. A new `getHighlightedFieldValue` utility
replicates the logic of `getHighlightHtml` from
`@kbn/field-formats-plugin` (which packages can't import directly),
merging all ES highlight snippets into the escaped field value —
handling multi-valued fields and multiple highlight regions within a
single value. `escapeAndPreserveHighlightTags` was simplified back to
only handle HTML `<mark>` tags (used by resource badges that receive
pre-formatted values from `formatFieldValue`). Also, we fixed a
pre-existing undetected bug where multiple highlight regions were
silently dropped due to a `markTags.length === 2` condition that only
allowed single keyword highlighting.

### Changes

- **`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**
- Read the full ES highlight array from `row.raw.highlight[field]` (not
just `[0]`) and pass it with the plain field value to
`getHighlightedFieldValue`, which merges all snippets and produces
XSS-safe HTML.

- **`highlight_utils.ts`** (renamed from
`escape_preserve_highlight_tags.ts`)
- Added `getHighlightedFieldValue(fieldValue, highlights)`, directly
replicating `getHighlightHtml` from `@kbn/field-formats-plugin`,
iterating over all ES highlight snippets, stripping ES tags to find
matching text, and replacing occurrences in the escaped field value with
`<mark>` elements. Handles multi-valued fields and multiple highlight
regions.
- Simplified `escapeAndPreserveHighlightTags` to only handle HTML
`<mark>` tags (its sole remaining use case in `cell_actions_popover.tsx`
for resource badges).

### Demo


https://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00
paulinashakirova pushed a commit to paulinashakirova/kibana that referenced this pull request Apr 2, 2026
## Summary

Fixes search result highlighting not working in the Logs profile summary
column and content breakdown.

> [!IMPORTANT]  
> This should be seen as a temporary fix, while the team works on
React-based field formatters

Closes elastic#259407

### Root cause

The message value was read from `row.flattened`, which doesn't contain
ES highlights. The highlight snippets can be found in
`row.raw.highlight[field]` with `@kibana-highlighted-field@` tags, but
were never passed to the rendering pipeline, after recent changes to the
implementation.

### Approaches considered

We explored different ways to get the highlighted value into the message
rendering.

First approach was to reuse `getLogDocumentOverview`, which had been
removed to solve an issue with OTel documents, but it called
`formatFieldValue` internally. That receives the full ES hit, the field
formatter would convert the ES tags to `<mark>` HTML tags automatically,
so that we could then keep our current logic working. The idea was to
get the field name from `getMessageFieldWithFallbacks(row.flattened)`
(which handles OTel fallback) and the already highlighted value from
`getMessageFieldWithFallbacks(documentOverview)`.

However, this approach didn't work for two reasons:
- There's a field resolution priority mismatch, where
`getMessageFieldWithFallbacks` checks OTel fields first (`body.text`
before `message`), while `getFieldValueWithFallback` inside
`getLogDocumentOverview` checks ECS first (`message` before
`body.text`). If a doc has both, the field name and value could come
from different sources.

- `formatFieldValue` passes the ECS field name to
`dataView.fields.getByName()`, which can return `undefined` for OTel
docs without aliases. When that happens, the field formatter can't look
up `hit.highlight[field.name]` and silently skips highlighting entirely.
In testing, highlights didn't appear.

The final approach reads `row.raw.highlight[field]` directly using the
correctly-resolved field name from
`getMessageFieldWithFallbacks(row.flattened)`, bypassing the field
formatter altogether. A new `getHighlightedFieldValue` utility
replicates the logic of `getHighlightHtml` from
`@kbn/field-formats-plugin` (which packages can't import directly),
merging all ES highlight snippets into the escaped field value —
handling multi-valued fields and multiple highlight regions within a
single value. `escapeAndPreserveHighlightTags` was simplified back to
only handle HTML `<mark>` tags (used by resource badges that receive
pre-formatted values from `formatFieldValue`). Also, we fixed a
pre-existing undetected bug where multiple highlight regions were
silently dropped due to a `markTags.length === 2` condition that only
allowed single keyword highlighting.

### Changes

- **`content.tsx` / `summary_column.tsx` / `content_breakdown.tsx`**
- Read the full ES highlight array from `row.raw.highlight[field]` (not
just `[0]`) and pass it with the plain field value to
`getHighlightedFieldValue`, which merges all snippets and produces
XSS-safe HTML.

- **`highlight_utils.ts`** (renamed from
`escape_preserve_highlight_tags.ts`)
- Added `getHighlightedFieldValue(fieldValue, highlights)`, directly
replicating `getHighlightHtml` from `@kbn/field-formats-plugin`,
iterating over all ES highlight snippets, stripping ES tags to find
matching text, and replacing occurrences in the escaped field value with
`<mark>` elements. Handles multi-valued fields and multiple highlight
regions.
- Simplified `escapeAndPreserveHighlightTags` to only handle HTML
`<mark>` tags (its sole remaining use case in `cell_actions_popover.tsx`
for resource badges).

### Demo


https://github.com/user-attachments/assets/7aa67eb4-f2ff-4874-bd3b-f8734f2c2e00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:version Backport to applied version labels Feature:Discover Discover Application release_note:skip Skip the PR/issue when compiling release notes reviewer:coderabbit PR review with CodeRabbit reviewer:macroscope PR review with Macroscope Team:obs-exploration Observability Exploration team v9.3.0 v9.3.1 v9.3.2 v9.3.3 v9.4.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Discover][Logs profile] Search highlights are missing

5 participants