From 412323652c0e8f3268918c31407e682becd5c66f Mon Sep 17 00:00:00 2001
From: SimpleStation14 <130339894+SimpleStation14@users.noreply.github.com>
Date: Tue, 28 May 2024 19:48:38 -0400
Subject: [PATCH] Mirror: Add Prometheus stats for admin count (#323)
## Mirror of PR #26284: [Add Prometheus stats for admin
count](https://github.com/space-wizards/space-station-14/pull/26284)
from
[space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14)
###### `a1817a12dbb385275f4273c1abf48fac0a989ddd`
PR opened by
PJB3005 at 2024-03-20 10:12:29
UTC
---
PR changed 2 files with 106 additions and 1 deletions.
The PR had the following labels:
- Status: Needs Review
---
Original Body
> Fixes #20828
>
> Reports time series for admin count. Counts are separated by state
(active, AFK, or deadminned) and admin rank.
>
> Requires engine master
Co-authored-by: SimpleStation14
---
.../Managers/AdminManager.Metrics.cs | 98 +++++++++++++++++++
.../Administration/Managers/AdminManager.cs | 9 +-
2 files changed, 106 insertions(+), 1 deletion(-)
create mode 100644 Content.Server/Administration/Managers/AdminManager.Metrics.cs
diff --git a/Content.Server/Administration/Managers/AdminManager.Metrics.cs b/Content.Server/Administration/Managers/AdminManager.Metrics.cs
new file mode 100644
index 00000000000..2fea931f1b9
--- /dev/null
+++ b/Content.Server/Administration/Managers/AdminManager.Metrics.cs
@@ -0,0 +1,98 @@
+using System.Diagnostics.Metrics;
+using System.Runtime.InteropServices;
+using Content.Server.Afk;
+using Robust.Server.DataMetrics;
+
+namespace Content.Server.Administration.Managers;
+
+// Handles metrics reporting for active admin count and such.
+
+public sealed partial class AdminManager
+{
+ private Dictionary? _adminOnlineCounts;
+
+ private const int SentinelRankId = -1;
+
+ [Dependency] private readonly IMetricsManager _metrics = default!;
+ [Dependency] private readonly IAfkManager _afkManager = default!;
+ [Dependency] private readonly IMeterFactory _meterFactory = default!;
+
+ private void InitializeMetrics()
+ {
+ _metrics.UpdateMetrics += MetricsOnUpdateMetrics;
+
+ var meter = _meterFactory.Create("SS14.AdminManager");
+
+ meter.CreateObservableGauge(
+ "admins_online_count",
+ MeasureAdminCount,
+ null,
+ "The count of online admins");
+ }
+
+ private void MetricsOnUpdateMetrics()
+ {
+ _sawmill.Verbose("Updating metrics");
+
+ var dict = new Dictionary();
+
+ foreach (var (session, reg) in _admins)
+ {
+ var rankId = reg.RankId ?? SentinelRankId;
+
+ ref var counts = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, rankId, out _);
+
+ if (reg.Data.Active)
+ {
+ if (_afkManager.IsAfk(session))
+ counts.afk += 1;
+ else
+ counts.active += 1;
+ }
+ else
+ {
+ counts.deadminned += 1;
+ }
+ }
+
+ // Neither prometheus-net nor dotnet-counters seem to handle stuff well if we STOP returning measurements.
+ // i.e. if the last admin with a rank disconnects.
+ // So if we have EVER reported a rank, always keep reporting it.
+ if (_adminOnlineCounts != null)
+ {
+ foreach (var rank in _adminOnlineCounts.Keys)
+ {
+ CollectionsMarshal.GetValueRefOrAddDefault(dict, rank, out _);
+ }
+ }
+
+ // Make sure "no rank" is always available. Avoid "no data".
+ CollectionsMarshal.GetValueRefOrAddDefault(dict, SentinelRankId, out _);
+
+ _adminOnlineCounts = dict;
+ }
+
+ private IEnumerable> MeasureAdminCount()
+ {
+ if (_adminOnlineCounts == null)
+ yield break;
+
+ foreach (var (rank, (active, afk, deadminned)) in _adminOnlineCounts)
+ {
+ yield return new Measurement(
+ active,
+ new KeyValuePair("state", "active"),
+ new KeyValuePair("rank", rank == SentinelRankId ? "none" : rank.ToString()));
+
+ yield return new Measurement(
+ afk,
+ new KeyValuePair("state", "afk"),
+ new KeyValuePair("rank", rank == SentinelRankId ? "none" : rank.ToString()));
+
+ yield return new Measurement(
+ deadminned,
+ new KeyValuePair("state", "deadminned"),
+ new KeyValuePair("rank", rank == SentinelRankId ? "none" : rank.ToString()));
+ }
+ }
+}
diff --git a/Content.Server/Administration/Managers/AdminManager.cs b/Content.Server/Administration/Managers/AdminManager.cs
index 4eaa08fe9dd..b1cca46e63f 100644
--- a/Content.Server/Administration/Managers/AdminManager.cs
+++ b/Content.Server/Administration/Managers/AdminManager.cs
@@ -23,7 +23,7 @@
namespace Content.Server.Administration.Managers
{
- public sealed class AdminManager : IAdminManager, IPostInjectInit, IConGroupControllerImplementation
+ public sealed partial class AdminManager : IAdminManager, IPostInjectInit, IConGroupControllerImplementation
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerDbManager _dbManager = default!;
@@ -34,6 +34,7 @@ public sealed class AdminManager : IAdminManager, IPostInjectInit, IConGroupCont
[Dependency] private readonly IServerConsoleHost _consoleHost = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly ToolshedManager _toolshed = default!;
+ [Dependency] private readonly ILogManager _logManager = default!;
private readonly Dictionary _admins = new();
private readonly HashSet _promotedPlayers = new();
@@ -49,6 +50,8 @@ public sealed class AdminManager : IAdminManager, IPostInjectInit, IConGroupCont
private readonly AdminCommandPermissions _commandPermissions = new();
private readonly AdminCommandPermissions _toolshedCommandPermissions = new();
+ private ISawmill _sawmill = default!;
+
public bool IsAdmin(ICommonSession session, bool includeDeAdmin = false)
{
return GetAdminData(session, includeDeAdmin) != null;
@@ -181,6 +184,8 @@ public void ReloadAdminsWithRank(int rankId)
public void Initialize()
{
+ _sawmill = _logManager.GetSawmill("admin");
+
_netMgr.RegisterNetMessage();
// Cache permissions for loaded console commands with the requisite attributes.
@@ -234,6 +239,8 @@ public void Initialize()
}
_toolshed.ActivePermissionController = this;
+
+ InitializeMetrics();
}
public void PromoteHost(ICommonSession player)