Shared stats registry + gRPC Stats API
A single namespaced counter map on the engine that every service reports into: ServiceCtx::count(key) is folded in after each command, the engine bumps its own events (botserv.kick/ban/warn/greet/trigger), and a per-service command counter (nickserv.command, chanserv.command, …) is tallied in dispatch. stats_snapshot() merges those with live gauges from the store (accounts/channels/bots/opers total). Exposed over gRPC as a new Stats service (Get -> map<string,uint64>), authorized by the same bearer token as Directory/Accounts — so any consumer (a dashboard, Django) reads every module's stats through one pipe. New service is additive to the proto; existing RPCs untouched.
This commit is contained in:
parent
5d1bfb3144
commit
d4e2c905f2
4 changed files with 111 additions and 8 deletions
27
src/grpc.rs
27
src/grpc.rs
|
|
@ -23,12 +23,14 @@ pub mod pb {
|
|||
use pb::accounts_server::{Accounts, AccountsServer};
|
||||
use pb::directory_server::{Directory, DirectoryServer};
|
||||
use pb::replication_event::Kind;
|
||||
use pb::stats_server::{Stats, StatsServer};
|
||||
use pb::{
|
||||
AccountDropped, AccountEmailSet, AccountRecord, AccountRegistered, AccountReply, AccountVerified,
|
||||
AuthenticateReply, AuthenticateRequest, ChannelDescSet, ChannelDropped, ChannelFounderSet, ChannelRecord,
|
||||
ChannelRegistered, ConfirmRequest, DropRequest, ForceLogoutReply, ForceLogoutRequest, GroupNickRequest,
|
||||
NickGrouped, NickUngrouped, RegisterRequest, ReplicationEvent, SetEmailRequest, SetPasswordRequest,
|
||||
SnapshotRequest, SnapshotResponse, Status as PbStatus, SubscribeRequest, UngroupNickRequest,
|
||||
SnapshotRequest, SnapshotResponse, Status as PbStatus, StatsRequest, StatsResponse, SubscribeRequest,
|
||||
UngroupNickRequest,
|
||||
};
|
||||
|
||||
type Shared = Arc<Mutex<Engine>>;
|
||||
|
|
@ -298,6 +300,25 @@ fn describe(s: AuthorityStatus) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
// Read-only stats: the shared counter registry plus live gauges, for dashboards.
|
||||
struct StatsService {
|
||||
engine: Shared,
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Stats for StatsService {
|
||||
async fn get(&self, req: Request<StatsRequest>) -> Result<Response<StatsResponse>, Status> {
|
||||
authorize(&req, &self.token)?;
|
||||
let counters = self.engine.lock().await.stats_snapshot().into_iter().collect();
|
||||
let collected_at = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
Ok(Response::new(StatsResponse { counters, collected_at }))
|
||||
}
|
||||
}
|
||||
|
||||
// Start the listener, if configured. Absent [grpc] in config.toml = no-op, same
|
||||
// pattern as gossip being optional.
|
||||
pub async fn run(engine: Shared, cfg: GrpcCfg, outbound: broadcast::Sender<LogEntry>) {
|
||||
|
|
@ -307,6 +328,7 @@ pub async fn run(engine: Shared, cfg: GrpcCfg, outbound: broadcast::Sender<LogEn
|
|||
};
|
||||
let has_tls = cfg.tls.is_some();
|
||||
let accounts_svc = AccountsService { engine: engine.clone(), token: cfg.token.clone() };
|
||||
let stats_svc = StatsService { engine: engine.clone(), token: cfg.token.clone() };
|
||||
let directory_svc = DirectoryService { engine, outbound, token: cfg.token };
|
||||
let mut server = Server::builder();
|
||||
if let Some(tls) = &cfg.tls {
|
||||
|
|
@ -320,10 +342,11 @@ pub async fn run(engine: Shared, cfg: GrpcCfg, outbound: broadcast::Sender<LogEn
|
|||
Err(e) => return tracing::error!(%e, "grpc TLS setup failed"),
|
||||
};
|
||||
}
|
||||
tracing::info!(%addr, tls = has_tls, "grpc directory + accounts API listening");
|
||||
tracing::info!(%addr, tls = has_tls, "grpc directory + accounts + stats API listening");
|
||||
if let Err(e) = server
|
||||
.add_service(DirectoryServer::new(directory_svc))
|
||||
.add_service(AccountsServer::new(accounts_svc))
|
||||
.add_service(StatsServer::new(stats_svc))
|
||||
.serve(addr)
|
||||
.await
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue