From 773c87018bb5febb763c8aeac7a487b47dcf8f90 Mon Sep 17 00:00:00 2001 From: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:04:12 +0530 Subject: [PATCH] feat: add configuration option to show field as JSON in dashboard (#11091) (#11104) Design at: https://github.com/openobserve/designs/tree/main/dashboards/show-field-as-json-default-value-env --------- Co-authored-by: ktx-abhay Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com> --------- Co-authored-by: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Abhay Padamani --- src/config/src/config.rs | 6 ++++ src/handler/http/request/status/mod.rs | 2 ++ .../addPanel/DashboardQueryBuilder.vue | 6 +++- .../dashboards/panels/JsonFieldRenderer.vue | 28 +++++++++++++------ .../dashboard/useDashboardPanel.ts | 2 +- .../composables/dashboard/usePanelFields.ts | 12 ++++++-- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/config/src/config.rs b/src/config/src/config.rs index 6b4e4ce798..6e410c7b23 100644 --- a/src/config/src/config.rs +++ b/src/config/src/config.rs @@ -1334,6 +1334,12 @@ pub struct Common { help = "Enable to show symbol in dashboard" )] pub dashboard_show_symbol_enabled: bool, + #[env_config( + name = "ZO_DASHBOARD_SHOW_FIELD_AS_JSON_ENABLED", + default = false, + help = "Enable to show field as JSON in dashboard table" + )] + pub dashboard_show_field_as_json_enabled: bool, #[env_config(name = "ZO_INGEST_DEFAULT_HEC_STREAM", default = "")] pub default_hec_stream: String, #[env_config( diff --git a/src/handler/http/request/status/mod.rs b/src/handler/http/request/status/mod.rs index 680b62498b..83ff13f2a3 100644 --- a/src/handler/http/request/status/mod.rs +++ b/src/handler/http/request/status/mod.rs @@ -173,6 +173,7 @@ struct ConfigResponse<'a> { ai_enabled: bool, dashboard_placeholder: String, dashboard_show_symbol_enabled: bool, + dashboard_show_field_as_json_enabled: bool, ingest_flatten_level: u32, #[cfg(feature = "enterprise")] license_expiry: i64, @@ -410,6 +411,7 @@ pub async fn zo_config() -> impl IntoResponse { ai_enabled, dashboard_placeholder: cfg.common.dashboard_placeholder.to_string(), dashboard_show_symbol_enabled: cfg.common.dashboard_show_symbol_enabled, + dashboard_show_field_as_json_enabled: cfg.common.dashboard_show_field_as_json_enabled, ingest_flatten_level: cfg.limit.ingest_flatten_level, #[cfg(feature = "enterprise")] license_expiry: expiry_time, diff --git a/web/src/components/dashboards/addPanel/DashboardQueryBuilder.vue b/web/src/components/dashboards/addPanel/DashboardQueryBuilder.vue index b5af45ce33..ff44fc1672 100644 --- a/web/src/components/dashboards/addPanel/DashboardQueryBuilder.vue +++ b/web/src/components/dashboards/addPanel/DashboardQueryBuilder.vue @@ -821,7 +821,11 @@ export default defineComponent({ field.showFieldAsJson === undefined || field.showFieldAsJson === null ) { - field.showFieldAsJson = false; + field.showFieldAsJson = + dashboardPanelDataPageKey === "logs" + ? (store?.state?.zoConfig?.dashboard_show_field_as_json_enabled ?? + false) + : false; } }; diff --git a/web/src/components/dashboards/panels/JsonFieldRenderer.vue b/web/src/components/dashboards/panels/JsonFieldRenderer.vue index 744c2716b3..44401aeb6b 100644 --- a/web/src/components/dashboards/panels/JsonFieldRenderer.vue +++ b/web/src/components/dashboards/panels/JsonFieldRenderer.vue @@ -15,7 +15,7 @@ along with this program. If not, see . --> } - +
- {{ formatValue(parsedData) }} + {{ formatValue(parsedData) }}
@@ -85,17 +85,29 @@ const props = defineProps(); const store = useStore(); // Parse the value if it's a string, otherwise use as-is -const parsedData = computed(() => { +// Also tracks whether JSON parsing succeeded so we don't color plain strings as JSON +const parseResult = computed(() => { if (typeof props.value === "string") { try { - return JSON.parse(props.value); + const parsed = JSON.parse(props.value); + // Only treat as JSON if the parsed result is an object or array. + // Bare primitives ("42", "true", "null") are valid JSON but should + // not be colored -> they're just plain field values stored as strings. + const isStructuredJSON = parsed !== null && typeof parsed === "object"; + return { data: parsed, isJSON: isStructuredJSON }; } catch (e) { - return props.value; + return { data: props.value, isJSON: false }; } } - return props.value; + return { data: props.value, isJSON: false }; }); +const parsedData = computed(() => parseResult.value.data); + +// True when the original string was valid JSON (or value was not a string). +// Used to avoid coloring plain non-JSON strings (e.g. timestamps) like JSON values. +const isValidJSON = computed(() => parseResult.value.isJSON); + // Check if theme is dark - memoized const isDarkTheme = computed(() => store.state.theme === "dark"); @@ -202,7 +214,7 @@ const isLastDefinedEntry = (obj: any, currentKey: string): boolean => {