From 9f03b1d3088e3ee0474abd941cc01bb637e9ac7d Mon Sep 17 00:00:00 2001 From: mandryllo Date: Mon, 20 Apr 2026 11:45:26 +0200 Subject: [PATCH 1/4] feat: add trace id variable --- src/components/grafana/dashboards/logs-and-traces.ts | 2 ++ src/components/grafana/variables/helpers.ts | 2 ++ src/components/grafana/variables/trace-id.ts | 5 +++++ src/components/grafana/variables/types.ts | 1 + 4 files changed, 10 insertions(+) create mode 100644 src/components/grafana/variables/trace-id.ts diff --git a/src/components/grafana/dashboards/logs-and-traces.ts b/src/components/grafana/dashboards/logs-and-traces.ts index 59260209..78928134 100644 --- a/src/components/grafana/dashboards/logs-and-traces.ts +++ b/src/components/grafana/dashboards/logs-and-traces.ts @@ -5,6 +5,7 @@ import { createLimitVariable } from '../variables/limit'; import { createLogLevelVariable } from '../variables/log-level'; import { createLogsViewPanel } from '../panels/logs'; import { createSearchTextVariable } from '../variables/search-text'; +import { createTraceIdVariable } from '../variables/trace-id'; export namespace LogsAndTracesDashboard { export type Args = { @@ -37,6 +38,7 @@ export function createLogsAndTracesDashboard( .addVariable(createStatusCodeVariable()) .addVariable(createLogLevelVariable()) .addVariable(createLimitVariable()) + .addVariable(createTraceIdVariable()) .addPanel( createLogsViewPanel({ logGroupName, diff --git a/src/components/grafana/variables/helpers.ts b/src/components/grafana/variables/helpers.ts index 6ff6a58c..11719622 100644 --- a/src/components/grafana/variables/helpers.ts +++ b/src/components/grafana/variables/helpers.ts @@ -26,10 +26,12 @@ export function createCustomVariable( export function createTextBoxVariable( name: string, label: string, + hide?: string, ): TextBoxVariable { return { type: 'textbox', name, label, + hide, }; } diff --git a/src/components/grafana/variables/trace-id.ts b/src/components/grafana/variables/trace-id.ts new file mode 100644 index 00000000..4ff5eca2 --- /dev/null +++ b/src/components/grafana/variables/trace-id.ts @@ -0,0 +1,5 @@ +import { createTextBoxVariable } from './helpers'; + +export function createTraceIdVariable() { + return createTextBoxVariable('traceId', 'Trace Id', 'hideVariable'); +} diff --git a/src/components/grafana/variables/types.ts b/src/components/grafana/variables/types.ts index cf4c773b..9e3802c9 100644 --- a/src/components/grafana/variables/types.ts +++ b/src/components/grafana/variables/types.ts @@ -16,6 +16,7 @@ export type TextBoxVariable = { type: 'textbox'; name: string; label: string; + hide?: string; }; export type Variable = CustomVariable | TextBoxVariable; From 04eb15ebadf616e6586ad4a44fe72e0f5baa6e3f Mon Sep 17 00:00:00 2001 From: mandryllo Date: Mon, 20 Apr 2026 11:48:00 +0200 Subject: [PATCH 2/4] feat: add traces view panel --- .../grafana/dashboards/logs-and-traces.ts | 13 +++++- src/components/grafana/panels/helpers.ts | 15 ++----- .../panels/{logs.ts => logs-traces.ts} | 41 ++++++++++++++----- src/components/grafana/panels/types.ts | 18 ++++---- 4 files changed, 56 insertions(+), 31 deletions(-) rename src/components/grafana/panels/{logs.ts => logs-traces.ts} (54%) diff --git a/src/components/grafana/dashboards/logs-and-traces.ts b/src/components/grafana/dashboards/logs-and-traces.ts index 78928134..98bdcf4b 100644 --- a/src/components/grafana/dashboards/logs-and-traces.ts +++ b/src/components/grafana/dashboards/logs-and-traces.ts @@ -3,9 +3,12 @@ import { GrafanaDashboardBuilder } from './builder'; import { createStatusCodeVariable } from '../variables/status-code'; import { createLimitVariable } from '../variables/limit'; import { createLogLevelVariable } from '../variables/log-level'; -import { createLogsViewPanel } from '../panels/logs'; import { createSearchTextVariable } from '../variables/search-text'; import { createTraceIdVariable } from '../variables/trace-id'; +import { + createLogsViewPanel, + createTracesViewPanel, +} from '../panels/logs-traces'; export namespace LogsAndTracesDashboard { export type Args = { @@ -29,7 +32,8 @@ export function createLogsAndTracesDashboard( config: LogsAndTracesDashboard.Args, ): GrafanaDashboardBuilder.CreateDashboard { const argsWithDefaults = mergeWithDefaults(defaults, config); - const { title, logsDataSourceName, logGroupName } = argsWithDefaults; + const { title, logsDataSourceName, logGroupName, tracesDataSourceName } = + argsWithDefaults; return new GrafanaDashboardBuilder(config.name) .withConfig(argsWithDefaults.dashboardConfig) @@ -45,5 +49,10 @@ export function createLogsAndTracesDashboard( dataSourceName: logsDataSourceName, }), ) + .addPanel( + createTracesViewPanel({ + dataSourceName: tracesDataSourceName, + }), + ) .build(); } diff --git a/src/components/grafana/panels/helpers.ts b/src/components/grafana/panels/helpers.ts index aceb92dd..63115ee4 100644 --- a/src/components/grafana/panels/helpers.ts +++ b/src/components/grafana/panels/helpers.ts @@ -1,4 +1,4 @@ -import { Panel, Metric, Transformation } from './types'; +import { Panel, Metric, Target, Transformation } from './types'; const percentageFieldConfig = { unit: 'percent', @@ -141,22 +141,15 @@ export function createTablePanel( title: string, position: Panel.Position, dataSource: string, - logGroupName: string, - expression: string, - transformations: Transformation[], + targets: Target[], + transformations?: Transformation[], ): Panel { return { type: 'table', title, gridPos: position, datasource: dataSource, - targets: [ - { - expression, - logGroups: [{ name: logGroupName }], - queryMode: 'Logs', - }, - ], + targets, transformations, fieldConfig: { defaults: {}, diff --git a/src/components/grafana/panels/logs.ts b/src/components/grafana/panels/logs-traces.ts similarity index 54% rename from src/components/grafana/panels/logs.ts rename to src/components/grafana/panels/logs-traces.ts index 6bf66357..266c2bfc 100644 --- a/src/components/grafana/panels/logs.ts +++ b/src/components/grafana/panels/logs-traces.ts @@ -9,16 +9,21 @@ export function createLogsViewPanel(config: { 'Logs', { x: 0, y: 0, w: 24, h: 12 }, config.dataSourceName, - config.logGroupName, - `fields @Timestamp - | parse @message '"body":"*"' as body - | parse @message '"res":{"statusCode":*}' as statusCode - | parse @message '"severity_text":"*"' as logLevel - | filter body like /\${search_text}/ - | filter \${status_code} - | filter \${log_level} - | sort @timestamp desc - | limit \${limit}`, + [ + { + expression: `fields @Timestamp + | parse @message '"body":"*"' as body + | parse @message '"res":{"statusCode":*}' as statusCode + | parse @message '"severity_text":"*"' as logLevel + | filter body like /\${search_text}/ + | filter \${status_code} + | filter \${log_level} + | sort @timestamp desc + | limit \${limit}`, + logGroups: [{ name: config.logGroupName }], + queryMode: 'Logs', + }, + ], [ { id: 'organize', @@ -54,3 +59,19 @@ export function createLogsViewPanel(config: { ], ); } + +export function createTracesViewPanel(config: { + dataSourceName: string; +}): Panel { + return createTablePanel( + 'Traces', + { x: 0, y: 0, w: 24, h: 12 }, + config.dataSourceName, + [ + { + expression: '$traceId', + queryType: 'getTrace', + }, + ], + ); +} diff --git a/src/components/grafana/panels/types.ts b/src/components/grafana/panels/types.ts index 01c58d4a..ca6053b5 100644 --- a/src/components/grafana/panels/types.ts +++ b/src/components/grafana/panels/types.ts @@ -3,14 +3,7 @@ export type Panel = { gridPos: Panel.Position; type: string; datasource: string; - targets: { - expr?: string; - expression?: string; - legendFormat?: string; - logGroups?: { name: string }[]; - queryMode?: string; - queryType?: string; - }[]; + targets: Target[]; fieldConfig: { defaults: { unit?: string; @@ -52,6 +45,15 @@ export namespace Panel { }; } +export type Target = { + expr?: string; + expression?: string; + legendFormat?: string; + logGroups?: { name: string }[]; + queryMode?: string; + queryType?: string; +}; + export type Metric = { label: string; query: string; From 92645824877ca631e0a3e5f0391814ab5dab9135 Mon Sep 17 00:00:00 2001 From: mandryllo Date: Mon, 20 Apr 2026 13:48:27 +0200 Subject: [PATCH 3/4] feat: add data link to traces panel --- src/components/grafana/panels/helpers.ts | 2 ++ src/components/grafana/panels/logs-traces.ts | 34 ++++++++++++++++++-- src/components/grafana/panels/types.ts | 11 +++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/components/grafana/panels/helpers.ts b/src/components/grafana/panels/helpers.ts index 63115ee4..52a0f056 100644 --- a/src/components/grafana/panels/helpers.ts +++ b/src/components/grafana/panels/helpers.ts @@ -143,6 +143,7 @@ export function createTablePanel( dataSource: string, targets: Target[], transformations?: Transformation[], + overrides?: any, ): Panel { return { type: 'table', @@ -153,6 +154,7 @@ export function createTablePanel( transformations, fieldConfig: { defaults: {}, + overrides, }, }; } diff --git a/src/components/grafana/panels/logs-traces.ts b/src/components/grafana/panels/logs-traces.ts index 266c2bfc..2933383a 100644 --- a/src/components/grafana/panels/logs-traces.ts +++ b/src/components/grafana/panels/logs-traces.ts @@ -11,7 +11,7 @@ export function createLogsViewPanel(config: { config.dataSourceName, [ { - expression: `fields @Timestamp + expression: `fields @Timestamp, trace_id as traceId | parse @message '"body":"*"' as body | parse @message '"res":{"statusCode":*}' as statusCode | parse @message '"severity_text":"*"' as logLevel @@ -33,6 +33,7 @@ export function createLogsViewPanel(config: { logLevel: 'Log Level', body: 'Body', '@timestamp': 'Timestamp', + traceId: 'Trace Id', }, indexByName: { '@timestamp': 0, @@ -57,6 +58,35 @@ export function createLogsViewPanel(config: { }, }, ], + [ + { + matcher: { + id: 'byName', + options: 'traceId', + }, + properties: [ + { + id: 'displayName', + value: 'Traces', + }, + { + id: 'links', + value: [ + { + title: 'Open traces', + url: `/d/\${__dashboard.uid}/\${__dashboard}?var-traceId=\${__data.fields.traceId}`, + }, + ], + }, + { + id: 'custom.cellOptions', + value: { + type: 'data-links', + }, + }, + ], + }, + ], ); } @@ -69,7 +99,7 @@ export function createTracesViewPanel(config: { config.dataSourceName, [ { - expression: '$traceId', + query: '$traceId', queryType: 'getTrace', }, ], diff --git a/src/components/grafana/panels/types.ts b/src/components/grafana/panels/types.ts index ca6053b5..69bcda29 100644 --- a/src/components/grafana/panels/types.ts +++ b/src/components/grafana/panels/types.ts @@ -21,6 +21,16 @@ export type Panel = { spanNulls: boolean; }; }; + overrides?: { + matcher: { + id: string; + options: string; + }; + properties: { + id: string; + value: string | { title: string; url: string }[] | { type: string }; + }[]; + }; }; transformations?: Transformation[]; options?: { @@ -52,6 +62,7 @@ export type Target = { logGroups?: { name: string }[]; queryMode?: string; queryType?: string; + query?: string; }; export type Metric = { From 85a0715bf028dbe7aedda83b0525086e5834a211 Mon Sep 17 00:00:00 2001 From: mandryllo Date: Mon, 20 Apr 2026 14:10:56 +0200 Subject: [PATCH 4/4] WIP --- src/components/grafana/dash.json | 510 +++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 src/components/grafana/dash.json diff --git a/src/components/grafana/dash.json b/src/components/grafana/dash.json new file mode 100644 index 00000000..4a0633ad --- /dev/null +++ b/src/components/grafana/dash.json @@ -0,0 +1,510 @@ +{ + "apiVersion": "dashboard.grafana.app/v2", + "kind": "Dashboard", + "metadata": { + "name": "27f71330-d1af-4fde-b2f8-41285dddef6e", + "namespace": "stacks-1412373", + "uid": "e01ae8e8-c5e8-49ff-852c-eb197b68ba21", + "resourceVersion": "2045161115373011293", + "generation": 26, + "creationTimestamp": "2026-04-17T13:54:05Z", + "labels": { + "grafana.app/deprecatedInternalID": "504502390951936" + }, + "annotations": { + "grafana.app/createdBy": "service-account:afjd95p4uhqtcc", + "grafana.app/folder": "dfjd968owvfggf", + "grafana.app/saved-from-ui": "Grafana Cloud", + "grafana.app/updatedBy": "user:cf1q5zu9i0kxsc", + "grafana.app/updatedTimestamp": "2026-04-17T15:23:01Z", + "grafana.app/folderTitle": "icb-test-lat-ICB-GENERATED", + "grafana.app/folderUrl": "/dashboards/f/dfjd968owvfggf/icbtestlaticbgenerated" + } + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "DataQuery", + "group": "grafana", + "version": "v0", + "datasource": { + "name": "-- Grafana --" + }, + "spec": {} + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "builtIn": true + } + } + ], + "cursorSync": "Off", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Logs", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "DataQuery", + "group": "cloudwatch", + "version": "v0", + "datasource": { + "name": "efjd96fgdz4sgf" + }, + "spec": { + "expression": "fields @Timestamp, trace_id as exploreTraces, trace_id as panelTraces\n | parse @message '\"body\":\"*\"' as body\n | parse @message '\"res\":{\"statusCode\":*}' as statusCode\n | parse @message '\"severity_text\":\"*\"' as logLevel\n | filter body like /${search_text}/\n | filter ${status_code}\n | filter ${log_level}\n | sort @timestamp desc\n | limit ${limit}", + "logGroups": [ + { + "name": "icb-test-dev-lg" + } + ], + "queryMode": "Logs", + "region": "default", + "statsGroups": [] + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [ + { + "kind": "Transformation", + "group": "organize", + "spec": { + "options": { + "excludeByName": { + "Value": true + }, + "includeByName": {}, + "indexByName": { + "@timestamp": 0, + "body": 3, + "logLevel": 2, + "statusCode": 1 + }, + "renameByName": { + "@timestamp": "Timestamp", + "body": "Body", + "exploreTraces": "Explore Traces", + "logLevel": "Log Level", + "panelTraces": "Panel Traces", + "statusCode": "Status Code", + "traceId": "Trace Id" + } + } + } + }, + { + "kind": "Transformation", + "group": "sortBy", + "spec": { + "options": { + "sort": [ + { + "desc": true, + "field": "Time" + } + ] + } + } + } + ], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "VizConfig", + "group": "table", + "version": "13.1.0-24556818486", + "spec": { + "options": { + "cellHeight": "sm", + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Explore Traces" + } + ] + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "inspect": false + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "scope": "series", + "options": "Explore Traces" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": true, + "title": "Explore traces", + "url": "/explore?left={\"datasource\":\"dfjdcu10ysykgc\",\"queries\":[{\"queryType\":\"getTrace\",\"query\":\"${__data.fields.exploreTraces}\"}],\"range\":{\"from\":\"now-1h\",\"to\":\"now\"}}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "scope": "series", + "options": "Panel Traces" + }, + "properties": [ + { + "id": "links", + "value": [ + { + "targetBlank": false, + "title": "Panel traces", + "url": "/d/27f71330-d1af-4fde-b2f8-41285dddef6e/icb-grafana-test-logs-and-traces?var-traceId=${__data.fields.panelTraces}" + } + ] + } + ] + }, + { + "matcher": { + "id": "byName", + "scope": "series", + "options": "Panel Traces" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "type": "data-links" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "scope": "series", + "options": "Explore Traces" + }, + "properties": [ + { + "id": "custom.cellOptions", + "value": { + "type": "data-links" + } + } + ] + } + ] + } + } + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "id": 2, + "title": "Traces", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "DataQuery", + "group": "grafana-x-ray-datasource", + "version": "v0", + "datasource": { + "name": "dfjdcu10ysykgc" + }, + "spec": { + "group": { + "GroupARN": "arn:aws:xray:us-east-1:587728158746:group/Default", + "GroupName": "Default", + "InsightsConfiguration": { + "InsightsEnabled": false, + "NotificationsEnabled": false + } + }, + "query": "$traceId", + "queryMode": "X-Ray", + "queryType": "getTrace", + "region": "default" + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "VizConfig", + "group": "table", + "version": "13.1.0-24556818486", + "spec": { + "options": { + "cellHeight": "sm", + "showHeader": true + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "footer": { + "reducers": [] + }, + "inspect": false + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "traceID" + }, + "properties": [ + { + "id": "custom.width", + "value": 323 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "serviceTags" + }, + "properties": [ + { + "id": "custom.width", + "value": 152 + } + ] + } + ] + } + } + } + } + } + }, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 0, + "width": 24, + "height": 12, + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 12, + "width": 24, + "height": 10, + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "1m", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "ICB Grafana Test Logs & Traces", + "variables": [ + { + "kind": "TextVariable", + "spec": { + "name": "search_text", + "current": { + "text": "", + "value": "" + }, + "query": "", + "label": "Search Text", + "hide": "dontHide", + "skipUrlSync": false + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "status_code", + "query": "[{\"text\":\"N/A\",\"value\":\"!ispresent(statusCode)\"},{\"text\":\"1xx\",\"value\":\"statusCode >= 100 and statusCode < 200\"},{\"text\":\"2xx\",\"value\":\"statusCode >= 200 and statusCode < 300\"},{\"text\":\"3xx\",\"value\":\"statusCode >= 300 and statusCode < 400\"},{\"text\":\"4xx\",\"value\":\"statusCode >= 400 and statusCode < 500\"},{\"text\":\"5xx\",\"value\":\"statusCode >= 500 and statusCode < 600\"}]", + "current": { + "text": "2xx", + "value": "statusCode >= 200 and statusCode < 300" + }, + "options": [], + "multi": false, + "includeAll": false, + "label": "Status Code", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": true, + "valuesFormat": "json" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "log_level", + "query": "[{\"text\":\"trace\",\"value\":\"logLevel = 'trace'\"},{\"text\":\"debug\",\"value\":\"logLevel = 'debug'\"},{\"text\":\"info\",\"value\":\"logLevel = 'info'\"},{\"text\":\"warn\",\"value\":\"logLevel = 'warn'\"},{\"text\":\"error\",\"value\":\"logLevel = 'error'\"},{\"text\":\"fatal\",\"value\":\"logLevel = 'fatal'\"}]", + "current": { + "text": "info", + "value": "logLevel = 'info'" + }, + "options": [], + "multi": false, + "includeAll": false, + "label": "Log Level", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": true, + "valuesFormat": "json" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "limit", + "query": "[{\"text\":\"20\",\"value\":20},{\"text\":\"50\",\"value\":50},{\"text\":\"100\",\"value\":100},{\"text\":\"250\",\"value\":250},{\"text\":\"500\",\"value\":500},{\"text\":\"1000\",\"value\":1000}]", + "current": { + "text": "20", + "value": "20" + }, + "options": [], + "multi": false, + "includeAll": false, + "label": "Limit", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": true, + "valuesFormat": "json" + } + }, + { + "kind": "TextVariable", + "spec": { + "name": "traceId", + "current": { + "text": "", + "value": "" + }, + "query": "", + "label": "Trace Id", + "hide": "hideVariable", + "skipUrlSync": false + } + } + ] + } +}