mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-12 14:00:01 -04:00
json stats
This commit is contained in:
parent
14b5bd796b
commit
d0ef7b476c
4 changed files with 104 additions and 116 deletions
|
@ -27,7 +27,7 @@ pty-process = { version = "0.4.0", features = ["async", "tokio"], optional = tru
|
||||||
regex = "1.10.6"
|
regex = "1.10.6"
|
||||||
rustls-pemfile = "2.1.3"
|
rustls-pemfile = "2.1.3"
|
||||||
serde = { version = "1.0.208", features = ["derive"] }
|
serde = { version = "1.0.208", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.125", optional = true }
|
serde_json = "1.0.125"
|
||||||
serde_yaml = { version = "0.9.34", optional = true }
|
serde_yaml = { version = "0.9.34", optional = true }
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
shell-words = { version = "1.1.0", optional = true }
|
shell-words = { version = "1.1.0", optional = true }
|
||||||
|
@ -43,7 +43,6 @@ wisp-mux = { version = "5.0.0", path = "../wisp", features = ["fastwebsockets",
|
||||||
[features]
|
[features]
|
||||||
default = ["toml"]
|
default = ["toml"]
|
||||||
|
|
||||||
json = ["dep:serde_json"]
|
|
||||||
yaml = ["dep:serde_yaml"]
|
yaml = ["dep:serde_yaml"]
|
||||||
toml = ["dep:toml"]
|
toml = ["dep:toml"]
|
||||||
|
|
||||||
|
|
3
server/e.toml
Normal file
3
server/e.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[server]
|
||||||
|
enable_stats_endpoint = true
|
||||||
|
stats_endpoint = ["tcp", "127.0.0.1:5000"]
|
|
@ -67,7 +67,7 @@ pub enum StatsEndpoint {
|
||||||
/// Stats on the same listener as the Wisp server.
|
/// Stats on the same listener as the Wisp server.
|
||||||
SameServer(String),
|
SameServer(String),
|
||||||
/// Stats on this address and socket type.
|
/// Stats on this address and socket type.
|
||||||
SeparateServer((SocketType, String)),
|
SeparateServer(BindAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -467,44 +467,32 @@ impl StreamConfig {
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn ser(&self) -> anyhow::Result<String> {
|
pub fn ser(&self) -> anyhow::Result<String> {
|
||||||
Ok(match CLI.format {
|
Ok(match CLI.format {
|
||||||
|
ConfigFormat::Json => serde_json::to_string_pretty(self)?,
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
ConfigFormat::Toml => toml::to_string_pretty(self)?,
|
ConfigFormat::Toml => toml::to_string_pretty(self)?,
|
||||||
#[cfg(feature = "json")]
|
|
||||||
ConfigFormat::Json => serde_json::to_string_pretty(self)?,
|
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
ConfigFormat::Yaml => serde_yaml::to_string(self)?,
|
ConfigFormat::Yaml => serde_yaml::to_string(self)?,
|
||||||
#[cfg(not(any(feature = "toml", feature = "json", feature = "yaml")))]
|
|
||||||
ConfigFormat::None => String::new(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn de(string: String) -> anyhow::Result<Self> {
|
pub fn de(string: String) -> anyhow::Result<Self> {
|
||||||
Ok(match CLI.format {
|
Ok(match CLI.format {
|
||||||
|
ConfigFormat::Json => serde_json::from_str(&string)?,
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
ConfigFormat::Toml => toml::from_str(&string)?,
|
ConfigFormat::Toml => toml::from_str(&string)?,
|
||||||
#[cfg(feature = "json")]
|
|
||||||
ConfigFormat::Json => serde_json::from_str(&string)?,
|
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
ConfigFormat::Yaml => serde_yaml::from_str(&string)?,
|
ConfigFormat::Yaml => serde_yaml::from_str(&string)?,
|
||||||
#[cfg(not(any(feature = "toml", feature = "json", feature = "yaml")))]
|
|
||||||
ConfigFormat::None => {
|
|
||||||
let _ = string;
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, ValueEnum)]
|
#[derive(Clone, Copy, Eq, PartialEq, ValueEnum)]
|
||||||
pub enum ConfigFormat {
|
pub enum ConfigFormat {
|
||||||
|
Json,
|
||||||
#[cfg(feature = "toml")]
|
#[cfg(feature = "toml")]
|
||||||
Toml,
|
Toml,
|
||||||
#[cfg(feature = "json")]
|
|
||||||
Json,
|
|
||||||
#[cfg(feature = "yaml")]
|
#[cfg(feature = "yaml")]
|
||||||
Yaml,
|
Yaml,
|
||||||
#[cfg(not(any(feature = "toml", feature = "json", feature = "yaml")))]
|
|
||||||
None,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfigFormat {
|
impl Default for ConfigFormat {
|
||||||
|
@ -512,13 +500,10 @@ impl Default for ConfigFormat {
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(feature = "toml")] {
|
if #[cfg(feature = "toml")] {
|
||||||
Self::Toml
|
Self::Toml
|
||||||
} else if #[cfg(feature = "json")] {
|
|
||||||
Self::Json
|
|
||||||
} else if #[cfg(feature = "yaml")] {
|
} else if #[cfg(feature = "yaml")] {
|
||||||
Self::Yaml
|
Self::Yaml
|
||||||
} else {
|
} else {
|
||||||
compile_error!("no config format feature enabled!");
|
Self::Json
|
||||||
Self::None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![feature(ip)]
|
#![feature(ip)]
|
||||||
#![deny(clippy::todo)]
|
#![deny(clippy::todo)]
|
||||||
|
|
||||||
use std::{fmt::Write, fs::read_to_string, net::IpAddr};
|
use std::{collections::HashMap, fs::read_to_string, net::IpAddr};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
@ -16,6 +16,7 @@ use lazy_static::lazy_static;
|
||||||
use listener::ServerListener;
|
use listener::ServerListener;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use route::{route_stats, ServerRouteResult};
|
use route::{route_stats, ServerRouteResult};
|
||||||
|
use serde::Serialize;
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wisp_mux::{ConnectPacket, StreamType};
|
use wisp_mux::{ConnectPacket, StreamType};
|
||||||
|
@ -93,92 +94,90 @@ fn format_stream_type(stream_type: StreamType) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_stats() -> anyhow::Result<String> {
|
#[derive(Serialize)]
|
||||||
let mut out = String::new();
|
struct MemoryStats {
|
||||||
let len = CLIENTS.len();
|
active: f64,
|
||||||
|
allocated: f64,
|
||||||
|
mapped: f64,
|
||||||
|
metadata: f64,
|
||||||
|
resident: f64,
|
||||||
|
retained: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct StreamStats {
|
||||||
|
stream_type: String,
|
||||||
|
requested: String,
|
||||||
|
resolved: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(ConnectPacket, ConnectPacket)> for StreamStats {
|
||||||
|
fn from(value: (ConnectPacket, ConnectPacket)) -> Self {
|
||||||
|
Self {
|
||||||
|
stream_type: format_stream_type(value.0.stream_type).to_string(),
|
||||||
|
requested: format!(
|
||||||
|
"{}:{}",
|
||||||
|
value.0.destination_hostname, value.0.destination_port
|
||||||
|
),
|
||||||
|
resolved: format!(
|
||||||
|
"{}:{}",
|
||||||
|
value.1.destination_hostname, value.1.destination_port
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ClientStats {
|
||||||
|
wsproxy: bool,
|
||||||
|
streams: HashMap<String, StreamStats>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ServerStats {
|
||||||
|
config: String,
|
||||||
|
clients: HashMap<String, ClientStats>,
|
||||||
|
memory: MemoryStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_stats() -> anyhow::Result<String> {
|
||||||
use tikv_jemalloc_ctl::stats::{active, allocated, mapped, metadata, resident, retained};
|
use tikv_jemalloc_ctl::stats::{active, allocated, mapped, metadata, resident, retained};
|
||||||
tikv_jemalloc_ctl::epoch::advance()?;
|
tikv_jemalloc_ctl::epoch::advance()?;
|
||||||
|
|
||||||
writeln!(&mut out, "Memory usage:")?;
|
let memory = MemoryStats {
|
||||||
writeln!(
|
active: active::read()? as f64 / (1024 * 1024) as f64,
|
||||||
&mut out,
|
allocated: allocated::read()? as f64 / (1024 * 1024) as f64,
|
||||||
"\tActive: {:?} MiB",
|
mapped: mapped::read()? as f64 / (1024 * 1024) as f64,
|
||||||
active::read()? as f64 / (1024 * 1024) as f64
|
metadata: metadata::read()? as f64 / (1024 * 1024) as f64,
|
||||||
)?;
|
resident: resident::read()? as f64 / (1024 * 1024) as f64,
|
||||||
writeln!(
|
retained: retained::read()? as f64 / (1024 * 1024) as f64,
|
||||||
&mut out,
|
};
|
||||||
"\tAllocated: {:?} MiB",
|
|
||||||
allocated::read()? as f64 / (1024 * 1024) as f64
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\tMapped: {:?} MiB",
|
|
||||||
mapped::read()? as f64 / (1024 * 1024) as f64
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\tMetadata: {:?} MiB",
|
|
||||||
metadata::read()? as f64 / (1024 * 1024) as f64
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\tResident: {:?} MiB",
|
|
||||||
resident::read()? as f64 / (1024 * 1024) as f64
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\tRetained: {:?} MiB",
|
|
||||||
retained::read()? as f64 / (1024 * 1024) as f64
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writeln!(
|
let clients = CLIENTS
|
||||||
&mut out,
|
.iter()
|
||||||
"{} clients connected{}",
|
.map(|x| {
|
||||||
len,
|
(
|
||||||
if len != 0 { ":" } else { "" }
|
x.key().to_string(),
|
||||||
)?;
|
ClientStats {
|
||||||
|
wsproxy: x.value().1,
|
||||||
|
streams: x
|
||||||
|
.value()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|x| (x.key().to_string(), StreamStats::from(x.value().clone())))
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
for client in CLIENTS.iter() {
|
let stats = ServerStats {
|
||||||
let len = client.value().0.len();
|
config: CONFIG.ser()?,
|
||||||
|
clients,
|
||||||
|
memory,
|
||||||
|
};
|
||||||
|
|
||||||
writeln!(
|
Ok(serde_json::to_string_pretty(&stats)?)
|
||||||
&mut out,
|
|
||||||
"\tClient \"{}\"{}: {} streams connected{}",
|
|
||||||
client.key(),
|
|
||||||
if client.value().1 { " (wsproxy)" } else { "" },
|
|
||||||
len,
|
|
||||||
if len != 0 && CONFIG.server.verbose_stats {
|
|
||||||
":"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if CONFIG.server.verbose_stats {
|
|
||||||
for stream in client.value().0.iter() {
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\t\tStream \"{}\": {}",
|
|
||||||
stream.key(),
|
|
||||||
format_stream_type(stream.value().0.stream_type)
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\t\t\tRequested: {}:{}",
|
|
||||||
stream.value().0.destination_hostname,
|
|
||||||
stream.value().0.destination_port
|
|
||||||
)?;
|
|
||||||
writeln!(
|
|
||||||
&mut out,
|
|
||||||
"\t\t\tResolved: {}:{}",
|
|
||||||
stream.value().1.destination_hostname,
|
|
||||||
stream.value().1.destination_port
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(out)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_stream(stream: ServerRouteResult, id: String) {
|
fn handle_stream(stream: ServerRouteResult, id: String) {
|
||||||
|
@ -230,24 +229,26 @@ async fn main() -> anyhow::Result<()> {
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("failed to bind to address {}", CONFIG.server.bind.1))?;
|
.with_context(|| format!("failed to bind to address {}", CONFIG.server.bind.1))?;
|
||||||
|
|
||||||
if let Some(bind_addr) = CONFIG.server.stats_endpoint.get_bindaddr() {
|
if CONFIG.server.enable_stats_endpoint {
|
||||||
info!("stats server listening on {:?}", bind_addr);
|
if let Some(bind_addr) = CONFIG.server.stats_endpoint.get_bindaddr() {
|
||||||
let mut stats_listener = ServerListener::new(&bind_addr).await.with_context(|| {
|
info!("stats server listening on {:?}", bind_addr);
|
||||||
format!("failed to bind to address {} for stats server", bind_addr.1)
|
let mut stats_listener = ServerListener::new(&bind_addr).await.with_context(|| {
|
||||||
})?;
|
format!("failed to bind to address {} for stats server", bind_addr.1)
|
||||||
|
})?;
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
match stats_listener.accept().await {
|
match stats_listener.accept().await {
|
||||||
Ok((stream, _)) => {
|
Ok((stream, _)) => {
|
||||||
if let Err(e) = route_stats(stream).await {
|
if let Err(e) = route_stats(stream).await {
|
||||||
error!("error while routing stats client: {:?}", e);
|
error!("error while routing stats client: {:?}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => error!("error while accepting stats client: {:?}", e),
|
||||||
}
|
}
|
||||||
Err(e) => error!("error while accepting stats client: {:?}", e),
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stats_endpoint = CONFIG.server.stats_endpoint.get_endpoint();
|
let stats_endpoint = CONFIG.server.stats_endpoint.get_endpoint();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue