mirror of
https://github.com/karl0ss/homepage.git
synced 2025-05-19 21:06:19 +01:00
Feature: Prometheus Metric service widget (#4269)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
794ec127cd
commit
e938c3ac1e
@ -98,6 +98,7 @@ You can also find a list of all available service widgets in the sidebar navigat
|
||||
- [Plex](plex.md)
|
||||
- [Portainer](portainer.md)
|
||||
- [Prometheus](prometheus.md)
|
||||
- [Prometheus Metric](prometheusmetric.md)
|
||||
- [Prowlarr](prowlarr.md)
|
||||
- [Proxmox](proxmox.md)
|
||||
- [Proxmox Backup Server](proxmoxbackupserver.md)
|
||||
|
67
docs/widgets/services/prometheusmetric.md
Normal file
67
docs/widgets/services/prometheusmetric.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Prometheus Metric
|
||||
description: Prometheus Metric Widget Configuration
|
||||
---
|
||||
|
||||
Learn more about [Querying Prometheus](https://prometheus.io/docs/prometheus/latest/querying/basics/).
|
||||
|
||||
This widget can show metrics for your service defined by PromQL queries which are requested from a running Prometheus instance.
|
||||
|
||||
Quries can be defined in the `metrics` array of the widget along with a label to be used to present the metric value. You can optionally specify a global `refreshInterval` in milliseconds and/or define the `refreshInterval` per metric. Inside the optional `format` object of a metric various formatting styles and transformations can be applied (see below).
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: prometheusmetric
|
||||
url: https://prometheus.host.or.ip
|
||||
refreshInterval: 10000 # optional - in milliseconds, defaults to 10s
|
||||
metrics:
|
||||
- label: Metric 1
|
||||
query: alertmanager_alerts{state="active"}
|
||||
- label: Metric 2
|
||||
query: apiserver_storage_size_bytes{node="mynode"}
|
||||
format:
|
||||
type: bytes
|
||||
- label: Metric 3
|
||||
query: avg(prometheus_notifications_latency_seconds)
|
||||
format:
|
||||
type: number
|
||||
suffix: s
|
||||
options:
|
||||
maximumFractionDigits: 4
|
||||
- label: Metric 4
|
||||
query: time()
|
||||
refreshInterval: 1000 # will override global refreshInterval
|
||||
format:
|
||||
type: date
|
||||
scale: 1000
|
||||
options:
|
||||
timeStyle: medium
|
||||
```
|
||||
|
||||
## Formatting
|
||||
|
||||
Supported values for `format.type` are `text`, `number`, `percent`, `bytes`, `bits`, `bbytes`, `bbits`, `byterate`, `bibyterate`, `bitrate`, `bibitrate`, `date`, `duration`, `relativeDate`, and `text` which is the default.
|
||||
|
||||
The `dateStyle` and `timeStyle` options of the `date` format are passed directly to [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) and the `style` and `numeric` options of `relativeDate` are passed to [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat). For the `number` format, options of [Intl.NumberFormat](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat) can be used, e.g. `maximumFractionDigits` or `minimumFractionDigits`.
|
||||
|
||||
### Data Transformation
|
||||
|
||||
You can manipulate your metric value with the following tools: `scale`, `prefix` and `suffix`, for example:
|
||||
|
||||
```yaml
|
||||
- query: my_custom_metric{}
|
||||
label: Metric 1
|
||||
format:
|
||||
type: number
|
||||
scale: 1000 # multiplies value by a number or fraction string e.g. 1/16
|
||||
- query: my_custom_metric{}
|
||||
label: Metric 2
|
||||
format:
|
||||
type: number
|
||||
prefix: "$" # prefixes value with given string
|
||||
- query: my_custom_metric{}
|
||||
label: Metric 3
|
||||
format:
|
||||
type: number
|
||||
suffix: "€" # suffixes value with given string
|
||||
```
|
@ -121,6 +121,7 @@ nav:
|
||||
- widgets/services/plex.md
|
||||
- widgets/services/portainer.md
|
||||
- widgets/services/prometheus.md
|
||||
- widgets/services/prometheusmetric.md
|
||||
- widgets/services/prowlarr.md
|
||||
- widgets/services/proxmox.md
|
||||
- widgets/services/proxmoxbackupserver.md
|
||||
|
@ -418,7 +418,7 @@ export function cleanServiceGroups(groups) {
|
||||
pointsLimit,
|
||||
diskUnits,
|
||||
|
||||
// glances, customapi, iframe
|
||||
// glances, customapi, iframe, prometheusmetric
|
||||
refreshInterval,
|
||||
|
||||
// hdhomerun
|
||||
@ -461,6 +461,9 @@ export function cleanServiceGroups(groups) {
|
||||
// opnsense, pfsense
|
||||
wan,
|
||||
|
||||
// prometheusmetric
|
||||
metrics,
|
||||
|
||||
// proxmox
|
||||
node,
|
||||
|
||||
@ -646,6 +649,10 @@ export function cleanServiceGroups(groups) {
|
||||
if (type === "vikunja") {
|
||||
if (enableTaskList !== undefined) cleanedService.widget.enableTaskList = !!enableTaskList;
|
||||
}
|
||||
if (type === "prometheusmetric") {
|
||||
if (metrics) cleanedService.widget.metrics = metrics;
|
||||
if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval;
|
||||
}
|
||||
}
|
||||
|
||||
return cleanedService;
|
||||
|
@ -95,6 +95,7 @@ const components = {
|
||||
plex: dynamic(() => import("./plex/component")),
|
||||
portainer: dynamic(() => import("./portainer/component")),
|
||||
prometheus: dynamic(() => import("./prometheus/component")),
|
||||
prometheusmetric: dynamic(() => import("./prometheusmetric/component")),
|
||||
prowlarr: dynamic(() => import("./prowlarr/component")),
|
||||
proxmox: dynamic(() => import("./proxmox/component")),
|
||||
pterodactyl: dynamic(() => import("./pterodactyl/component")),
|
||||
|
115
src/widgets/prometheusmetric/component.jsx
Normal file
115
src/widgets/prometheusmetric/component.jsx
Normal file
@ -0,0 +1,115 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
function formatValue(t, metric, rawValue) {
|
||||
if (!rawValue) return "-";
|
||||
|
||||
let value = rawValue;
|
||||
|
||||
// Scale the value. Accepts either a number to multiply by or a string
|
||||
// like "12/345".
|
||||
const scale = metric?.format?.scale;
|
||||
if (typeof scale === "number") {
|
||||
value *= scale;
|
||||
} else if (typeof scale === "string" && scale.includes("/")) {
|
||||
const parts = scale.split("/");
|
||||
const numerator = parts[0] ? parseFloat(parts[0]) : 1;
|
||||
const denominator = parts[1] ? parseFloat(parts[1]) : 1;
|
||||
value = (value * numerator) / denominator;
|
||||
} else {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
|
||||
// Format the value using a known type and optional options.
|
||||
switch (metric?.format?.type) {
|
||||
case "text":
|
||||
break;
|
||||
default:
|
||||
value = t(`common.${metric.format.type}`, { value, ...metric.format?.options });
|
||||
}
|
||||
|
||||
// Apply fixed prefix.
|
||||
const prefix = metric?.format?.prefix;
|
||||
if (prefix) {
|
||||
value = `${prefix}${value}`;
|
||||
}
|
||||
|
||||
// Apply fixed suffix.
|
||||
const suffix = metric?.format?.suffix;
|
||||
if (suffix) {
|
||||
value = `${value}${suffix}`;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { metrics = [], refreshInterval = 10000 } = widget;
|
||||
|
||||
let prometheusmetricError;
|
||||
|
||||
const prometheusmetricData = new Map(
|
||||
metrics.slice(0, 4).map((metric) => {
|
||||
// disable the rule that hooks should not be called from a callback,
|
||||
// because we don't need a strong guarantee of hook execution order here.
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { data: resultData, error: resultError } = useWidgetAPI(widget, "query", {
|
||||
query: metric.query,
|
||||
refreshInterval: Math.max(1000, metric.refreshInterval ?? refreshInterval),
|
||||
});
|
||||
if (resultError) {
|
||||
prometheusmetricError = resultError;
|
||||
}
|
||||
return [metric.key ?? metric.label, resultData];
|
||||
}),
|
||||
);
|
||||
|
||||
if (prometheusmetricError) {
|
||||
return <Container service={service} error={prometheusmetricError} />;
|
||||
}
|
||||
|
||||
if (!prometheusmetricData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
{metrics.slice(0, 4).map((item) => (
|
||||
<Block label={item.label} key={item.label} />
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function getResultValue(data) {
|
||||
// Fetches the first metric result from the Prometheus query result data.
|
||||
// The first element in the result value is the timestamp which is ignored here.
|
||||
const resultType = data?.data?.resultType;
|
||||
const result = data?.data?.result;
|
||||
|
||||
switch (resultType) {
|
||||
case "vector":
|
||||
return result?.[0]?.value?.[1];
|
||||
case "scalar":
|
||||
return result?.[1];
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
{metrics.map((metric) => (
|
||||
<Block
|
||||
label={metric.label}
|
||||
key={metric.key ?? metric.label}
|
||||
value={formatValue(t, metric, getResultValue(prometheusmetricData.get(metric.key ?? metric.label)))}
|
||||
/>
|
||||
))}
|
||||
</Container>
|
||||
);
|
||||
}
|
16
src/widgets/prometheusmetric/widget.js
Normal file
16
src/widgets/prometheusmetric/widget.js
Normal file
@ -0,0 +1,16 @@
|
||||
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/v1/{endpoint}",
|
||||
proxyHandler: genericProxyHandler,
|
||||
|
||||
mappings: {
|
||||
query: {
|
||||
method: "GET",
|
||||
endpoint: "query",
|
||||
params: ["query"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
@ -87,6 +87,7 @@ import plantit from "./plantit/widget";
|
||||
import plex from "./plex/widget";
|
||||
import portainer from "./portainer/widget";
|
||||
import prometheus from "./prometheus/widget";
|
||||
import prometheusmetric from "./prometheusmetric/widget";
|
||||
import prowlarr from "./prowlarr/widget";
|
||||
import proxmox from "./proxmox/widget";
|
||||
import pterodactyl from "./pterodactyl/widget";
|
||||
@ -218,6 +219,7 @@ const widgets = {
|
||||
plex,
|
||||
portainer,
|
||||
prometheus,
|
||||
prometheusmetric,
|
||||
prowlarr,
|
||||
proxmox,
|
||||
pterodactyl,
|
||||
|
Loading…
x
Reference in New Issue
Block a user