Browse Source

Add Prometheus metrics

[Feat]: Simple Stats API #1285
pull/1299/head
Vadim Babadzhanyan 8 months ago
parent
commit
b97b66462b
  1. 4
      README.md
  2. 1
      docker-compose.yml
  3. 1
      src/config.js
  4. 15
      src/lib/Server.js
  5. 73
      src/lib/WireGuard.js

4
README.md

@ -26,6 +26,7 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
* UI_TRAFFIC_STATS (default off) * UI_TRAFFIC_STATS (default off)
* UI_SHOW_LINKS (default off) * UI_SHOW_LINKS (default off)
* WG_ENABLE_EXPIRES_TIME (default off) * WG_ENABLE_EXPIRES_TIME (default off)
* Prometheus metrics support
## Requirements ## Requirements
@ -87,6 +88,8 @@ To automatically install & run wg-easy, simply run:
The Web UI will now be available on `http://0.0.0.0:51821`. The Web UI will now be available on `http://0.0.0.0:51821`.
The Prometheus metrics will now be available on `http://0.0.0.0:51821/metrics`. Grafana dashboard [21733](https://grafana.com/grafana/dashboards/21733-wireguard/)
> 💡 Your configuration files will be saved in `~/.wg-easy` > 💡 Your configuration files will be saved in `~/.wg-easy`
WireGuard Easy can be launched with Docker Compose as well - just download WireGuard Easy can be launched with Docker Compose as well - just download
@ -126,6 +129,7 @@ These options can be configured by setting environment variables using `-e KEY="
| `UI_SHOW_LINKS` | `false` | `true` | Enable display of a short download link in Web UI | | `UI_SHOW_LINKS` | `false` | `true` | Enable display of a short download link in Web UI |
| `MAX_AGE` | `0` | `1440` | The maximum age of Web UI sessions in minutes. `0` means that the session will exist until the browser is closed. | | `MAX_AGE` | `0` | `1440` | The maximum age of Web UI sessions in minutes. `0` means that the session will exist until the browser is closed. |
| `UI_ENABLE_SORT_CLIENTS` | `false` | `true` | Enable UI sort clients by name | | `UI_ENABLE_SORT_CLIENTS` | `false` | `true` | Enable UI sort clients by name |
| `ENABLE_PROMETHEUS_METRICS` | `true` | `true` | Enable Prometheus metrics `http://0.0.0.0:51821/metrics` and `http://0.0.0.0:51821/metrics/json`|
> If you change `WG_PORT`, make sure to also change the exposed port. > If you change `WG_PORT`, make sure to also change the exposed port.

1
docker-compose.yml

@ -30,6 +30,7 @@ services:
# - UI_SHOW_LINKS=true # - UI_SHOW_LINKS=true
# - UI_ENABLE_SORT_CLIENTS=true # - UI_ENABLE_SORT_CLIENTS=true
# - WG_ENABLE_EXPIRES_TIME=true # - WG_ENABLE_EXPIRES_TIME=true
# - ENABLE_PROMETHEUS_METRICS=false
image: ghcr.io/wg-easy/wg-easy image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy container_name: wg-easy

1
src/config.js

@ -41,3 +41,4 @@ module.exports.UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0;
module.exports.UI_SHOW_LINKS = process.env.UI_SHOW_LINKS || 'false'; module.exports.UI_SHOW_LINKS = process.env.UI_SHOW_LINKS || 'false';
module.exports.UI_ENABLE_SORT_CLIENTS = process.env.UI_ENABLE_SORT_CLIENTS || 'false'; module.exports.UI_ENABLE_SORT_CLIENTS = process.env.UI_ENABLE_SORT_CLIENTS || 'false';
module.exports.WG_ENABLE_EXPIRES_TIME = process.env.WG_ENABLE_EXPIRES_TIME || 'false'; module.exports.WG_ENABLE_EXPIRES_TIME = process.env.WG_ENABLE_EXPIRES_TIME || 'false';
module.exports.ENABLE_PROMETHEUS_METRICS = process.env.ENABLE_PROMETHEUS_METRICS || 'true';

15
src/lib/Server.js

@ -36,6 +36,7 @@ const {
UI_SHOW_LINKS, UI_SHOW_LINKS,
UI_ENABLE_SORT_CLIENTS, UI_ENABLE_SORT_CLIENTS,
WG_ENABLE_EXPIRES_TIME, WG_ENABLE_EXPIRES_TIME,
ENABLE_PROMETHEUS_METRICS,
} = require('../config'); } = require('../config');
const requiresPassword = !!PASSWORD_HASH; const requiresPassword = !!PASSWORD_HASH;
@ -286,6 +287,20 @@ module.exports = class Server {
const { expireDate } = await readBody(event); const { expireDate } = await readBody(event);
await WireGuard.updateClientExpireDate({ clientId, expireDate }); await WireGuard.updateClientExpireDate({ clientId, expireDate });
return { success: true }; return { success: true };
}))
.get('/metrics', defineEventHandler(async (event) => {
setHeader(event, 'Content-Type', 'text/plain');
if (ENABLE_PROMETHEUS_METRICS === 'true') {
return WireGuard.getMetrics();
}
return '';
}))
.get('/metrics/json', defineEventHandler(async (event) => {
setHeader(event, 'Content-Type', 'application/json');
if (ENABLE_PROMETHEUS_METRICS === 'true') {
return WireGuard.getMetricsJSON();
}
return '';
})); }));
const safePathJoin = (base, target) => { const safePathJoin = (base, target) => {

73
src/lib/WireGuard.js

@ -158,6 +158,7 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
latestHandshakeAt: null, latestHandshakeAt: null,
transferRx: null, transferRx: null,
transferTx: null, transferTx: null,
endpoint: null,
})); }));
// Loop WireGuard status // Loop WireGuard status
@ -186,6 +187,7 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
client.latestHandshakeAt = latestHandshakeAt === '0' client.latestHandshakeAt = latestHandshakeAt === '0'
? null ? null
: new Date(Number(`${latestHandshakeAt}000`)); : new Date(Number(`${latestHandshakeAt}000`));
client.endpoint = endpoint === '(none)' ? null : endpoint;
client.transferRx = Number(transferRx); client.transferRx = Number(transferRx);
client.transferTx = Number(transferTx); client.transferTx = Number(transferTx);
client.persistentKeepalive = persistentKeepalive; client.persistentKeepalive = persistentKeepalive;
@ -398,4 +400,75 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
} }
} }
async getMetrics() {
const clients = await this.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
let wireguardSentBytes = '';
let wireguardReceivedBytes = '';
let wireguardLatestHandshakeSeconds = '';
for (const client of Object.values(clients)) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
wireguardSentBytes += `wireguard_sent_bytes{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${Number(client.transferTx)}\n`;
wireguardReceivedBytes += `wireguard_received_bytes{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${Number(client.transferRx)}\n`;
wireguardLatestHandshakeSeconds += `wireguard_latest_handshake_seconds{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${client.latestHandshakeAt ? (new Date().getTime() - new Date(client.latestHandshakeAt).getTime()) / 1000 : 0}\n`;
}
let returnText = '# HELP wg-easy and wireguard metrics\n';
returnText += '\n# HELP wireguard_configured_peers\n';
returnText += '# TYPE wireguard_configured_peers gauge\n';
returnText += `wireguard_configured_peers{interface="wg0"} ${Number(wireguardPeerCount)}\n`;
returnText += '\n# HELP wireguard_enabled_peers\n';
returnText += '# TYPE wireguard_enabled_peers gauge\n';
returnText += `wireguard_enabled_peers{interface="wg0"} ${Number(wireguardEnabledPeersCount)}\n`;
returnText += '\n# HELP wireguard_connected_peers\n';
returnText += '# TYPE wireguard_connected_peers gauge\n';
returnText += `wireguard_connected_peers{interface="wg0"} ${Number(wireguardConnectedPeersCount)}\n`;
returnText += '\n# HELP wireguard_sent_bytes Bytes sent to the peer\n';
returnText += '# TYPE wireguard_sent_bytes counter\n';
returnText += `${wireguardSentBytes}`;
returnText += '\n# HELP wireguard_received_bytes Bytes received from the peer\n';
returnText += '# TYPE wireguard_received_bytes counter\n';
returnText += `${wireguardReceivedBytes}`;
returnText += '\n# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake\n';
returnText += '# TYPE wireguard_latest_handshake_seconds gauge\n';
returnText += `${wireguardLatestHandshakeSeconds}`;
return returnText;
}
async getMetricsJSON() {
const clients = await this.getClients();
let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0;
let wireguardConnectedPeersCount = 0;
for (const client of Object.values(clients)) {
wireguardPeerCount++;
if (client.enabled === true) {
wireguardEnabledPeersCount++;
}
if (client.endpoint !== null) {
wireguardConnectedPeersCount++;
}
}
return {
wireguard_configured_peers: Number(wireguardPeerCount),
wireguard_enabled_peers: Number(wireguardEnabledPeersCount),
wireguard_connected_peers: Number(wireguardConnectedPeersCount),
};
}
}; };

Loading…
Cancel
Save