96 lines
3.4 KiB
JavaScript
96 lines
3.4 KiB
JavaScript
const prometheus = require('prom-client');
|
|
const register = prometheus.register;
|
|
|
|
const prometheusTimeout = GetConvarInt('prometheus_timeout', 5000);
|
|
const authEnabled = GetConvarInt('prometheus_auth_enabled', 0);
|
|
const authLogin = GetConvar('prometheus_login', 'admin');
|
|
const authPassword = GetConvar('prometheus_password', 'admin');
|
|
const authorizationHeader = 'Basic ' + new Buffer(authLogin + ':' + authPassword).toString('base64');
|
|
|
|
// Collect some default FiveM metrics
|
|
const playerCount = new prometheus.Gauge({ name: 'fxs_player_count', help: 'Number of connected players.' });
|
|
const playerConnections = new prometheus.Counter({ name: 'fxs_player_connections', help: 'Number of player connections.' });
|
|
const playerDisconnections = new prometheus.Counter({ name: 'fxs_player_disconnections', help: 'Number of player disconnections.' });
|
|
const averageLatency = new prometheus.Gauge({ name: 'fxs_average_player_latency', help: 'Average player latency.' });
|
|
const latencyHistogram = new prometheus.Histogram({ name: 'fxs_players_latency', help: 'Players latency.', buckets: [10, 20, 50, 70, 100, 120, 150, 160, 200] });
|
|
const minPlayerPing = new prometheus.Gauge({ name: 'fxs_min_player_ping', help: 'Minimum player ping.' });
|
|
const maxPlayerPing = new prometheus.Gauge({ name: 'fxs_max_player_ping', help: 'Maximum player ping.' });
|
|
|
|
setInterval(() => {
|
|
const numIndices = GetNumPlayerIndices();
|
|
|
|
let cumulativeLatency = 0;
|
|
let minPing, maxPing;
|
|
|
|
for (let playerIndex = 0; playerIndex < numIndices; playerIndex++) {
|
|
const player = GetPlayerFromIndex(playerIndex);
|
|
const playerPing = GetPlayerPing(player);
|
|
cumulativeLatency += playerPing;
|
|
if (!minPing || minPing > playerPing) {
|
|
minPing = playerPing;
|
|
}
|
|
if (!maxPing || maxPing < playerPing) {
|
|
maxPing = playerPing;
|
|
}
|
|
latencyHistogram.observe(playerPing);
|
|
}
|
|
|
|
playerCount.set(numIndices);
|
|
if (numIndices > 0) {
|
|
averageLatency.set(cumulativeLatency / numIndices);
|
|
minPlayerPing.set(minPing);
|
|
maxPlayerPing.set(maxPing);
|
|
} else {
|
|
averageLatency.set(0);
|
|
minPlayerPing.set(0);
|
|
maxPlayerPing.set(0);
|
|
}
|
|
}, prometheusTimeout);
|
|
|
|
on('playerConnecting', (playerName, setKickReason, tempPlayer) => {
|
|
playerConnections.inc();
|
|
});
|
|
|
|
on('playerDropped', (player, disconnectReason) => {
|
|
playerDisconnections.inc();
|
|
});
|
|
|
|
/**
|
|
* Server event
|
|
* @event prometheus:addMetric
|
|
*
|
|
* @param {string} type - The metric type, i.e. Gauge.
|
|
* @param {string} name - The metric name.
|
|
* @param {string} description - The metric description.
|
|
* @param {function} cb - A callback function to update the metric with a method name (i.e. set) and a value.
|
|
*/
|
|
on('prometheus:addMetric', (type, name, description, cb) => {
|
|
if (!prometheus[type]) {
|
|
console.error(`[FiveM Prometheus] Invalid metric type ${type} for ${name}`);
|
|
return;
|
|
}
|
|
let metric = new prometheus[type]({ name: name, help: description });
|
|
|
|
setInterval(() => {
|
|
cb((methodName, value) => {
|
|
if (metric[methodName]) {
|
|
metric[methodName](value);
|
|
}
|
|
});
|
|
}, prometheusTimeout);
|
|
});
|
|
|
|
on('prometheus:_getMetrics', (cb) => {
|
|
cb(register.metrics());
|
|
});
|
|
|
|
// Don't use this for now, there is a deadlock somewhere :/
|
|
/*SetHttpHandler((req, res) => {
|
|
const authorizedRequest = !authEnabled || req.headers['Authorization'] === authorizationHeader;
|
|
if (req.path === '/metrics' && authorizedRequest) {
|
|
res.send(register.metrics());
|
|
} else {
|
|
res.send('Route /' + GetCurrentResourceName() + req.path + ' not found.');
|
|
}
|
|
});*/
|