From 5149fabdca6acc78b73e8828eb060789c408625a Mon Sep 17 00:00:00 2001 From: Drew Davis Date: Thu, 16 Apr 2026 12:00:07 -0400 Subject: [PATCH] feat: Improve runtime metric dashboard templates (#2092) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR improves the OTel Runtime Metric dashboard templates - Python template has been added - The filter condition on each template has been converted to Lucene so that it works with JSON schema in addition to the map schema - The top row of tiles have been increased to height: 4 ### Screenshots or video Screenshot 2026-04-16 at 7 06 04 AM Screenshot 2026-04-16 at 7 06 43 AM Screenshot 2026-04-16 at 7 06 33 AM Screenshot 2026-04-16 at 7 06 23 AM Screenshot 2026-04-16 at 7 07 28 AM ### How to test locally or on Vercel These templates can be tested in the demo / preview environment. ### References - Linear Issue: Closes HDX-3873 - Related PRs: --- .changeset/shaggy-tigers-tan.md | 5 + .../dashboardTemplates/dotnet-runtime.json | 30 +- .../src/dashboardTemplates/go-runtime.json | 28 +- packages/app/src/dashboardTemplates/index.ts | 2 + .../jvm-runtime-metrics.json | 30 +- .../dashboardTemplates/nodejs-runtime.json | 26 +- .../dashboardTemplates/python-runtime.json | 326 ++++++++++++++++++ 7 files changed, 390 insertions(+), 57 deletions(-) create mode 100644 .changeset/shaggy-tigers-tan.md create mode 100644 packages/app/src/dashboardTemplates/python-runtime.json diff --git a/.changeset/shaggy-tigers-tan.md b/.changeset/shaggy-tigers-tan.md new file mode 100644 index 00000000..6b117e3b --- /dev/null +++ b/.changeset/shaggy-tigers-tan.md @@ -0,0 +1,5 @@ +--- +"@hyperdx/app": patch +--- + +feat: Add Python Runtime Metrics dashboard template diff --git a/packages/app/src/dashboardTemplates/dotnet-runtime.json b/packages/app/src/dashboardTemplates/dotnet-runtime.json index 43cd4fa9..e1f85726 100644 --- a/packages/app/src/dashboardTemplates/dotnet-runtime.json +++ b/packages/app/src/dashboardTemplates/dotnet-runtime.json @@ -9,7 +9,7 @@ "x": 0, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "GC Heap Size", "source": "Metrics", @@ -37,7 +37,7 @@ "x": 6, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Exceptions", "source": "Metrics", @@ -66,7 +66,7 @@ "x": 12, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "GC Pause (s)", "source": "Metrics", @@ -95,7 +95,7 @@ "x": 18, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "ThreadPool Items", "source": "Metrics", @@ -122,7 +122,7 @@ { "id": "98e92c", "x": 0, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -147,7 +147,7 @@ { "id": "f6c44a", "x": 12, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -176,7 +176,7 @@ { "id": "e54085", "x": 0, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -205,7 +205,7 @@ { "id": "34979f", "x": 12, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -234,7 +234,7 @@ { "id": "6649b7", "x": 0, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -258,7 +258,7 @@ { "id": "3a8add", "x": 12, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -286,7 +286,7 @@ { "id": "fdfdc8", "x": 0, - "y": 24, + "y": 25, "w": 12, "h": 7, "config": { @@ -310,7 +310,7 @@ { "id": "ebe6dc", "x": 12, - "y": 24, + "y": 25, "w": 12, "h": 7, "config": { @@ -348,8 +348,8 @@ "expression": "ServiceName", "source": "Metrics", "sourceMetricType": "sum", - "where": "ResourceAttributes['telemetry.sdk.language'] = 'dotnet'", - "whereLanguage": "sql" + "where": "ResourceAttributes.telemetry.sdk.language:\"dotnet\"", + "whereLanguage": "lucene" } ] -} +} \ No newline at end of file diff --git a/packages/app/src/dashboardTemplates/go-runtime.json b/packages/app/src/dashboardTemplates/go-runtime.json index d979270f..10104026 100644 --- a/packages/app/src/dashboardTemplates/go-runtime.json +++ b/packages/app/src/dashboardTemplates/go-runtime.json @@ -9,7 +9,7 @@ "x": 0, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Memory Used", "source": "Metrics", @@ -37,7 +37,7 @@ "x": 6, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Alloc Rate", "source": "Metrics", @@ -65,7 +65,7 @@ "x": 12, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "GC Heap Target", "source": "Metrics", @@ -93,7 +93,7 @@ "x": 18, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "CPU Utilization", "source": "Metrics", @@ -119,7 +119,7 @@ { "id": "bbde66", "x": 0, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -162,7 +162,7 @@ { "id": "deb616", "x": 12, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -190,7 +190,7 @@ { "id": "432607", "x": 0, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -218,7 +218,7 @@ { "id": "08224d", "x": 12, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -242,7 +242,7 @@ { "id": "fae8d7", "x": 0, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -271,7 +271,7 @@ { "id": "545a68", "x": 12, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -300,7 +300,7 @@ { "id": "c0fc04", "x": 0, - "y": 24, + "y": 25, "w": 12, "h": 7, "config": { @@ -338,8 +338,8 @@ "expression": "ServiceName", "source": "Metrics", "sourceMetricType": "sum", - "where": "ResourceAttributes['telemetry.sdk.language'] = 'go'", - "whereLanguage": "sql" + "where": "ResourceAttributes.telemetry.sdk.language:\"go\"", + "whereLanguage": "lucene" } ] -} +} \ No newline at end of file diff --git a/packages/app/src/dashboardTemplates/index.ts b/packages/app/src/dashboardTemplates/index.ts index 4b233dc5..609affa2 100644 --- a/packages/app/src/dashboardTemplates/index.ts +++ b/packages/app/src/dashboardTemplates/index.ts @@ -7,6 +7,7 @@ import dotnetRuntime from './dotnet-runtime.json'; import goRuntime from './go-runtime.json'; import jvmRuntimeMetrics from './jvm-runtime-metrics.json'; import nodejsRuntime from './nodejs-runtime.json'; +import pythonRuntime from './python-runtime.json'; function parseTemplate( id: string, @@ -26,6 +27,7 @@ const templates: Record = { 'go-runtime': goRuntime, 'jvm-runtime-metrics': jvmRuntimeMetrics, 'nodejs-runtime': nodejsRuntime, + 'python-runtime': pythonRuntime, }; export const DASHBOARD_TEMPLATES = Object.entries(templates) diff --git a/packages/app/src/dashboardTemplates/jvm-runtime-metrics.json b/packages/app/src/dashboardTemplates/jvm-runtime-metrics.json index e3bf40ee..69246b65 100644 --- a/packages/app/src/dashboardTemplates/jvm-runtime-metrics.json +++ b/packages/app/src/dashboardTemplates/jvm-runtime-metrics.json @@ -9,7 +9,7 @@ "x": 0, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Heap Used", "source": "Metrics", @@ -39,7 +39,7 @@ "x": 6, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "CPU Utilization", "source": "Metrics", @@ -67,7 +67,7 @@ "x": 12, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Thread Count", "source": "Metrics", @@ -96,7 +96,7 @@ "x": 18, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "CPU Count", "source": "Metrics", @@ -123,7 +123,7 @@ { "id": "6363a5", "x": 0, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -172,7 +172,7 @@ { "id": "5abddd", "x": 12, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -203,7 +203,7 @@ { "id": "c4abeb", "x": 0, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -234,7 +234,7 @@ { "id": "26aa01", "x": 12, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -265,7 +265,7 @@ { "id": "ea6e2d", "x": 0, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -293,7 +293,7 @@ { "id": "bf01ef", "x": 12, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -317,7 +317,7 @@ { "id": "f90a9e", "x": 0, - "y": 24, + "y": 25, "w": 12, "h": 7, "config": { @@ -342,7 +342,7 @@ { "id": "17f41d", "x": 12, - "y": 24, + "y": 25, "w": 12, "h": 7, "config": { @@ -380,8 +380,8 @@ "expression": "ServiceName", "source": "Metrics", "sourceMetricType": "sum", - "where": "ResourceAttributes['telemetry.sdk.language'] = 'java'", - "whereLanguage": "sql" + "where": "ResourceAttributes.telemetry.sdk.language:\"java\"", + "whereLanguage": "lucene" } ] -} +} \ No newline at end of file diff --git a/packages/app/src/dashboardTemplates/nodejs-runtime.json b/packages/app/src/dashboardTemplates/nodejs-runtime.json index 2f9130e8..6e51c444 100644 --- a/packages/app/src/dashboardTemplates/nodejs-runtime.json +++ b/packages/app/src/dashboardTemplates/nodejs-runtime.json @@ -9,7 +9,7 @@ "x": 0, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "EL Delay p99 (ms)", "source": "Metrics", @@ -38,7 +38,7 @@ "x": 6, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Event Loop Util", "source": "Metrics", @@ -66,7 +66,7 @@ "x": 12, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "Heap Used", "source": "Metrics", @@ -94,7 +94,7 @@ "x": 18, "y": 0, "w": 6, - "h": 3, + "h": 4, "config": { "name": "CPU Utilization", "source": "Metrics", @@ -120,7 +120,7 @@ { "id": "f9dae7", "x": 0, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -164,7 +164,7 @@ { "id": "93c11e", "x": 12, - "y": 3, + "y": 4, "w": 12, "h": 7, "config": { @@ -192,7 +192,7 @@ { "id": "7155d4", "x": 0, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -228,7 +228,7 @@ { "id": "c2ea50", "x": 12, - "y": 10, + "y": 11, "w": 12, "h": 7, "config": { @@ -257,7 +257,7 @@ { "id": "ea6e2d", "x": 0, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -285,7 +285,7 @@ { "id": "a73b57", "x": 12, - "y": 17, + "y": 18, "w": 12, "h": 7, "config": { @@ -315,8 +315,8 @@ "expression": "ServiceName", "source": "Metrics", "sourceMetricType": "gauge", - "where": "ResourceAttributes['telemetry.sdk.language'] = 'nodejs'", - "whereLanguage": "sql" + "where": "ResourceAttributes.telemetry.sdk.language:\"nodejs\"", + "whereLanguage": "lucene" } ] -} +} \ No newline at end of file diff --git a/packages/app/src/dashboardTemplates/python-runtime.json b/packages/app/src/dashboardTemplates/python-runtime.json new file mode 100644 index 00000000..837131d0 --- /dev/null +++ b/packages/app/src/dashboardTemplates/python-runtime.json @@ -0,0 +1,326 @@ +{ + "version": "0.1.0", + "name": "Python Runtime Metrics", + "description": "CPU utilization, memory usage, GC collections, and thread count for Python applications with the OTel Runtime Metrics instrumentation", + "tags": [ + "OTel Runtime Metrics" + ], + "tiles": [ + { + "id": "b028c5", + "x": 0, + "y": 0, + "w": 6, + "h": 4, + "config": { + "name": "Memory RSS", + "source": "Metrics", + "displayType": "number", + "numberFormat": { + "output": "byte", + "mantissa": 1 + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "avg", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "metricName": "process.memory.usage" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "c847ba", + "x": 6, + "y": 0, + "w": 6, + "h": 4, + "config": { + "name": "CPU Utilization", + "source": "Metrics", + "displayType": "number", + "numberFormat": { + "output": "percent", + "mantissa": 1 + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "avg", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "gauge", + "metricName": "process.cpu.utilization" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "c02ef1", + "x": 12, + "y": 0, + "w": 6, + "h": 4, + "config": { + "name": "GC Collections", + "source": "Metrics", + "displayType": "number", + "numberFormat": { + "output": "number", + "mantissa": 0, + "thousandSeparated": true + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "metricName": "cpython.gc.collections" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "f0634b", + "x": 18, + "y": 0, + "w": 6, + "h": 4, + "config": { + "name": "GC Leaks", + "source": "Metrics", + "displayType": "number", + "numberFormat": { + "output": "number", + "mantissa": 0, + "thousandSeparated": true + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "metricName": "cpython.gc.uncollectable_objects" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "9adc01", + "x": 0, + "y": 4, + "w": 12, + "h": 7, + "config": { + "name": "Memory Usage", + "source": "Metrics", + "displayType": "line", + "numberFormat": { + "output": "byte", + "mantissa": 1 + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "avg", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "metricName": "process.memory.usage" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "3a5843", + "x": 12, + "y": 4, + "w": 12, + "h": 7, + "config": { + "name": "CPU Utilization", + "source": "Metrics", + "displayType": "line", + "numberFormat": { + "output": "percent", + "mantissa": 1 + }, + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "avg", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "gauge", + "metricName": "process.cpu.utilization" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "be51d5", + "x": 0, + "y": 11, + "w": 12, + "h": 7, + "config": { + "name": "GC Collections (rate)", + "source": "Metrics", + "displayType": "line", + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "alias": "gc_count", + "metricName": "cpython.gc.collections" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "421240", + "x": 12, + "y": 11, + "w": 12, + "h": 7, + "config": { + "name": "Collected vs Uncollectable Objects", + "source": "Metrics", + "displayType": "line", + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "alias": "collected", + "metricName": "cpython.gc.collected_objects" + }, + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "alias": "leaks (uncollectable)", + "metricName": "cpython.gc.uncollectable_objects" + } + ], + "where": "", + "whereLanguage": "sql" + } + }, + { + "id": "50a6fb", + "x": 0, + "y": 18, + "w": 12, + "h": 7, + "config": { + "name": "CPU Seconds Used", + "source": "Metrics", + "displayType": "line", + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "metricName": "process.cpu.time" + } + ], + "where": "", + "whereLanguage": "sql", + "groupBy": "" + } + }, + { + "id": "d72a48", + "x": 12, + "y": 18, + "w": 12, + "h": 7, + "config": { + "name": "Threads & Context Switches", + "source": "Metrics", + "displayType": "line", + "granularity": "auto", + "alignDateRangeToGranularity": true, + "select": [ + { + "aggFn": "avg", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "gauge", + "alias": "threads", + "metricName": "process.thread.count" + }, + { + "aggFn": "sum", + "aggConditionLanguage": "lucene", + "valueExpression": "Value", + "metricType": "sum", + "isDelta": true, + "alias": "ctx switches", + "metricName": "process.context_switches" + } + ], + "where": "", + "whereLanguage": "sql" + } + } + ], + "filters": [ + { + "id": "svc-filter-001", + "type": "QUERY_EXPRESSION", + "name": "ServiceName", + "expression": "ServiceName", + "source": "Metrics", + "sourceMetricType": "gauge", + "where": "ResourceAttributes.telemetry.sdk.language:\"python\"", + "whereLanguage": "lucene" + } + ], + "containers": [] +} \ No newline at end of file