From 8b0a031cab3b37603a75445778c06b3c682b1296 Mon Sep 17 00:00:00 2001 From: ricoberger Date: Mon, 9 Aug 2021 19:42:17 +0200 Subject: [PATCH] Change Prometheus sparkline chart to allow labels It is now possible to use the labels of a Prometheus metric as value in the sparkline chart. This means when the user provides a label in a sparkline chart the label will be shown instead of the current value of the returned metric. We also improved the calculation of the average value of a metric by excluding all the null values. --- CHANGELOG.md | 3 ++ deploy/demo/bookinfo/details-application.yaml | 3 +- .../bookinfo/productpage-application.yaml | 3 +- deploy/demo/bookinfo/ratings-application.yaml | 3 +- deploy/demo/bookinfo/reviews-application.yaml | 3 +- .../demo/kobs/base/dashboards/istio-http.yaml | 6 ++-- .../kobs/base/dashboards/resource-usage.yaml | 9 ++---- docs/plugins/prometheus.md | 14 +++++----- docs/resources/applications.md | 3 +- docs/resources/dashboards.md | 9 ++---- plugins/prometheus/pkg/instance/instance.go | 6 ++-- .../src/components/panel/Sparkline.tsx | 28 +++++++++++-------- .../src/components/preview/Sparkline.tsx | 26 +++++++++-------- 13 files changed, 58 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 551aa8c35..1c2a2c924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan - [#103](https://github.com/kobsio/kobs/pull/103): Add option to get user information from a request. - [#104](https://github.com/kobsio/kobs/pull/104): Add actions for Opsgenie plugin to acknowledge, snooze and close alerts. +- [#105](https://github.com/kobsio/kobs/pull/105): Add Prometheus metrics for API requests. ### Fixed @@ -19,6 +20,8 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan ### Changed +- [#106](https://github.com/kobsio/kobs/pull/106): :warning: *Breaking change:* :warning: Change Prometheus sparkline chart to allow the usage of labels. + ## [v0.5.0](https://github.com/kobsio/kobs/releases/tag/v0.5.0) (2021-08-03) ### Added diff --git a/deploy/demo/bookinfo/details-application.yaml b/deploy/demo/bookinfo/details-application.yaml index c7cc879f0..793b9c71b 100644 --- a/deploy/demo/bookinfo/details-application.yaml +++ b/deploy/demo/bookinfo/details-application.yaml @@ -23,8 +23,7 @@ spec: options: unit: "%" queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"details-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"details-v1"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"details-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"details-v1"}[5m])) * 100 dashboards: - name: resources namespace: kobs diff --git a/deploy/demo/bookinfo/productpage-application.yaml b/deploy/demo/bookinfo/productpage-application.yaml index 37f8a30db..8d695cec5 100644 --- a/deploy/demo/bookinfo/productpage-application.yaml +++ b/deploy/demo/bookinfo/productpage-application.yaml @@ -32,8 +32,7 @@ spec: options: unit: "%" queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"productpage-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"productpage-v1"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"productpage-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"productpage-v1"}[5m])) * 100 dashboards: - name: resources namespace: kobs diff --git a/deploy/demo/bookinfo/ratings-application.yaml b/deploy/demo/bookinfo/ratings-application.yaml index 62d2ba6a9..50c449302 100644 --- a/deploy/demo/bookinfo/ratings-application.yaml +++ b/deploy/demo/bookinfo/ratings-application.yaml @@ -23,8 +23,7 @@ spec: options: unit: "%" queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"ratings-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"ratings-v1"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"ratings-v1",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"ratings-v1"}[5m])) * 100 dashboards: - name: resources namespace: kobs diff --git a/deploy/demo/bookinfo/reviews-application.yaml b/deploy/demo/bookinfo/reviews-application.yaml index 85d54cd40..1d5f0443b 100644 --- a/deploy/demo/bookinfo/reviews-application.yaml +++ b/deploy/demo/bookinfo/reviews-application.yaml @@ -26,8 +26,7 @@ spec: options: unit: "%" queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*"}[5m])) * 100 dashboards: - name: resources namespace: kobs diff --git a/deploy/demo/kobs/base/dashboards/istio-http.yaml b/deploy/demo/kobs/base/dashboards/istio-http.yaml index 6ed81a561..ab2cddd59 100644 --- a/deploy/demo/kobs/base/dashboards/istio-http.yaml +++ b/deploy/demo/kobs/base/dashboards/istio-http.yaml @@ -58,8 +58,7 @@ spec: unit: req/s type: sparkline queries: - - label: Incoming Request Volume - query: round(sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}"}[5m])), 0.001) + - query: round(sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}"}[5m])), 0.001) - title: Incoming Success Rate colSpan: 6 plugin: @@ -68,8 +67,7 @@ spec: unit: "%" type: sparkline queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="{% .var_reporter %}",destination_workload_namespace=~"{{ .namespace }}",destination_workload=~"{% .var_workload %}"}[5m])) * 100 - size: 2 panels: - title: Request Duration diff --git a/deploy/demo/kobs/base/dashboards/resource-usage.yaml b/deploy/demo/kobs/base/dashboards/resource-usage.yaml index e946e9757..2b39fa60c 100644 --- a/deploy/demo/kobs/base/dashboards/resource-usage.yaml +++ b/deploy/demo/kobs/base/dashboards/resource-usage.yaml @@ -32,8 +32,7 @@ spec: type: sparkline unit: Cores queries: - - label: CPU Usage - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) + - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) - title: Memory Usage colSpan: 4 plugin: @@ -42,8 +41,7 @@ spec: type: sparkline unit: MiB queries: - - label: Memory Usage - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 + - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 - title: Restarts colSpan: 4 plugin: @@ -51,8 +49,7 @@ spec: options: type: sparkline queries: - - label: Restarts - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} + - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} - size: 3 panels: - title: CPU Usage diff --git a/docs/plugins/prometheus.md b/docs/plugins/prometheus.md index f39a426cd..78f30ddc7 100644 --- a/docs/plugins/prometheus.md +++ b/docs/plugins/prometheus.md @@ -25,7 +25,10 @@ The following options can be used for a panel with the Prometheus plugin: | Field | Type | Description | Required | | ----- | ---- | ----------- | -------- | | query | string | The PromQL query. | Yes | -| label | string | The label the results. The label can use the value of a variable or a label of the returned time series, e.g. `{% . %}`. If you want to use a Prometheus label make sure that the label name doesn't conflict with a variable name | Yes | +| label | string | The label the results. The label can use the value of a variable or a label of the returned time series, e.g. `{% . %}`. If you want to use a Prometheus label make sure that the label name doesn't conflict with a variable name. | Yes | + +!!! note + In `sparkline` charts the label must not be provided. If the label is provided in a `sparkline` chart the label will be displayed instead of the current value. ### Column @@ -73,8 +76,7 @@ spec: type: sparkline unit: Cores queries: - - label: CPU Usage - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) + - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) - title: Memory Usage colSpan: 4 plugin: @@ -83,8 +85,7 @@ spec: type: sparkline unit: MiB queries: - - label: Memory Usage - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 + - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 - title: Restarts colSpan: 4 plugin: @@ -92,8 +93,7 @@ spec: options: type: sparkline queries: - - label: Restarts - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} + - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} - size: 3 panels: - title: CPU Usage diff --git a/docs/resources/applications.md b/docs/resources/applications.md index 96e393dbb..dd29ceffa 100644 --- a/docs/resources/applications.md +++ b/docs/resources/applications.md @@ -111,8 +111,7 @@ spec: options: unit: "%" queries: - - label: Incoming Success Rate - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*"}[5m])) * 100 + - query: sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="destination",destination_workload_namespace=~"bookinfo",destination_workload=~"reviews-.*"}[5m])) * 100 dashboards: - name: resources namespace: kobs diff --git a/docs/resources/dashboards.md b/docs/resources/dashboards.md index ebf898682..65e70d645 100644 --- a/docs/resources/dashboards.md +++ b/docs/resources/dashboards.md @@ -166,8 +166,7 @@ spec: type: sparkline unit: Cores queries: - - label: CPU Usage - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) + - query: sum(rate(container_cpu_usage_seconds_total{namespace="{{ .namespace }}", image!="", pod=~"{% .var_pod %}", container!="POD", container!=""}[2m])) - title: Memory Usage colSpan: 4 plugin: @@ -176,8 +175,7 @@ spec: type: sparkline unit: MiB queries: - - label: Memory Usage - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 + - query: sum(container_memory_working_set_bytes{namespace="{{ .namespace }}", pod=~"{% .var_pod %}", container!="POD", container!=""}) / 1024 / 1024 - title: Restarts colSpan: 4 plugin: @@ -185,8 +183,7 @@ spec: options: type: sparkline queries: - - label: Restarts - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} + - query: kube_pod_container_status_restarts_total{namespace="{{ .namespace }}", pod=~"{% .var_pod %}"} - size: 3 panels: - title: CPU Usage diff --git a/plugins/prometheus/pkg/instance/instance.go b/plugins/prometheus/pkg/instance/instance.go index 6334589f3..4432f5f1b 100644 --- a/plugins/prometheus/pkg/instance/instance.go +++ b/plugins/prometheus/pkg/instance/instance.go @@ -98,6 +98,7 @@ func (i *Instance) GetMetrics(ctx context.Context, queries []Query, resolution s var min float64 var max float64 var avg float64 + var count float64 var data []Datum for index, value := range stream.Values { @@ -109,6 +110,7 @@ func (i *Instance) GetMetrics(ctx context.Context, queries []Query, resolution s }) } else { avg = avg + val + count = count + 1 if index == 0 { min = val @@ -128,8 +130,8 @@ func (i *Instance) GetMetrics(ctx context.Context, queries []Query, resolution s } } - if avg != 0 { - avg = avg / float64(len(stream.Values)) + if avg != 0 && count != 0 { + avg = avg / count } var labels map[string]string diff --git a/plugins/prometheus/src/components/panel/Sparkline.tsx b/plugins/prometheus/src/components/panel/Sparkline.tsx index c8924816c..e19885c00 100644 --- a/plugins/prometheus/src/components/panel/Sparkline.tsx +++ b/plugins/prometheus/src/components/panel/Sparkline.tsx @@ -1,13 +1,13 @@ import { Alert, AlertActionLink, AlertVariant } from '@patternfly/react-core'; import { QueryObserverResult, useQuery } from 'react-query'; -import { ResponsiveLineCanvas, Serie } from '@nivo/line'; import React from 'react'; +import { ResponsiveLineCanvas } from '@nivo/line'; +import { IPanelOptions, ISeries } from '../../utils/interfaces'; import { IPluginTimes, PluginCard } from '@kobsio/plugin-core'; import { convertMetrics, getMappingValue } from '../../utils/helpers'; import Actions from './Actions'; import { COLOR_SCALE } from '../../utils/colors'; -import { IPanelOptions } from '../../utils/interfaces'; interface ISpakrlineProps { name: string; @@ -28,7 +28,7 @@ export const Spakrline: React.FunctionComponent = ({ times, options, }: ISpakrlineProps) => { - const { isError, isFetching, error, data, refetch } = useQuery( + const { isError, isFetching, error, data, refetch } = useQuery( ['prometheus/metrics', name, options.queries, times], async () => { try { @@ -49,9 +49,9 @@ export const Spakrline: React.FunctionComponent = ({ if (response.status >= 200 && response.status < 300) { if (json) { - return convertMetrics(json).series; + return convertMetrics(json); } else { - return []; + return { labels: {}, series: [] }; } } else { if (json.error) { @@ -70,14 +70,18 @@ export const Spakrline: React.FunctionComponent = ({ // Determine the label which should be shown above the chart. This is the last value in first metric of the returned // data or a value from the user specified mappings. let label = 'N/A'; - if (data && data.length > 0) { + if (options.queries && Array.isArray(options.queries) && options.queries.length > 0 && options.queries[0].label) { + if (data && data.labels && data.labels.hasOwnProperty('0-0')) { + label = data.labels['0-0']; + } + } else if (data && data.series && data.series.length > 0) { if (options.mappings && Object.keys(options.mappings).length > 0) { - label = getMappingValue(data[0].data[data[0].data.length - 1].y, options.mappings); + label = getMappingValue(data.series[0].data[data.series[0].data.length - 1].y, options.mappings); } else { label = - data[0].data[data[0].data.length - 1].y === null + data.series[0].data[data.series[0].data.length - 1].y === null ? 'N/A' - : `${data[0].data[data[0].data.length - 1].y} ${options.unit ? options.unit : ''}`; + : `${data.series[0].data[data.series[0].data.length - 1].y} ${options.unit ? options.unit : ''}`; } } @@ -94,7 +98,7 @@ export const Spakrline: React.FunctionComponent = ({ title="Could not get metrics" actionLinks={ - > => refetch()}> + > => refetch()}> Retry @@ -102,7 +106,7 @@ export const Spakrline: React.FunctionComponent = ({ >

{error?.message}

- ) : data ? ( + ) : data && data.series ? (
@@ -111,7 +115,7 @@ export const Spakrline: React.FunctionComponent = ({ = ({ title, options, }: ISpakrlineProps) => { - const { isError, isLoading, error, data } = useQuery( + const { isError, isLoading, error, data } = useQuery( ['prometheus/metrics', name, options.queries, times], async () => { try { @@ -42,9 +42,9 @@ export const Spakrline: React.FunctionComponent = ({ if (response.status >= 200 && response.status < 300) { if (json) { - return convertMetrics(json).series; + return convertMetrics(json); } else { - return []; + return { labels: {}, series: [] }; } } else { if (json.error) { @@ -63,14 +63,18 @@ export const Spakrline: React.FunctionComponent = ({ // Determine the label which should be shown above the chart. This is the last value in first metric of the returned // data or a value from the user specified mappings. let label = 'N/A'; - if (data && data.length > 0) { + if (options.queries && Array.isArray(options.queries) && options.queries.length > 0 && options.queries[0].label) { + if (data && data.labels && data.labels.hasOwnProperty('0-0')) { + label = data.labels['0-0']; + } + } else if (data && data.series && data.series.length > 0) { if (options.mappings && Object.keys(options.mappings).length > 0) { - label = getMappingValue(data[0].data[data[0].data.length - 1].y, options.mappings); + label = getMappingValue(data.series[0].data[data.series[0].data.length - 1].y, options.mappings); } else { label = - data[0].data[data[0].data.length - 1].y === null + data.series[0].data[data.series[0].data.length - 1].y === null ? 'N/A' - : `${data[0].data[data[0].data.length - 1].y} ${options.unit ? options.unit : ''}`; + : `${data.series[0].data[data.series[0].data.length - 1].y} ${options.unit ? options.unit : ''}`; } } @@ -82,7 +86,7 @@ export const Spakrline: React.FunctionComponent = ({
) : isError ? ( - ) : data ? ( + ) : data && data.series ? (
{label}
{title}
@@ -90,7 +94,7 @@ export const Spakrline: React.FunctionComponent = ({