Add metrics page
Add a page to print metrics.
This commit is contained in:
parent
beb039aa3c
commit
a12ad80cba
1 changed files with 133 additions and 5 deletions
138
src/main.rs
138
src/main.rs
|
@ -15,6 +15,7 @@ use regex::Regex;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
data::{FromData, ToByteUnit},
|
data::{FromData, ToByteUnit},
|
||||||
futures::TryFutureExt,
|
futures::TryFutureExt,
|
||||||
|
get,
|
||||||
http::{HeaderMap, Status},
|
http::{HeaderMap, Status},
|
||||||
outcome::Outcome::{self, Failure, Success},
|
outcome::Outcome::{self, Failure, Success},
|
||||||
post, routes,
|
post, routes,
|
||||||
|
@ -31,6 +32,7 @@ use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::BufReader,
|
io::BufReader,
|
||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
@ -72,6 +74,19 @@ struct Opts {
|
||||||
command: Option<Command>,
|
command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct WebhookeyMetrics {
|
||||||
|
requests_received: Mutex<usize>,
|
||||||
|
requests_invalid: Mutex<usize>,
|
||||||
|
hooks_successful: Mutex<usize>,
|
||||||
|
hooks_forbidden: Mutex<usize>,
|
||||||
|
hooks_unmatched: Mutex<usize>,
|
||||||
|
commands_executed: Mutex<usize>,
|
||||||
|
commands_execution_failed: Mutex<usize>,
|
||||||
|
commands_successful: Mutex<usize>,
|
||||||
|
commands_failed: Mutex<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(deny_unknown_fields, untagged)]
|
#[serde(deny_unknown_fields, untagged)]
|
||||||
enum AddrType {
|
enum AddrType {
|
||||||
|
@ -425,6 +440,18 @@ impl<'r> FromData<'r> for Hooks {
|
||||||
request: &'r Request<'_>,
|
request: &'r Request<'_>,
|
||||||
data: Data<'r>,
|
data: Data<'r>,
|
||||||
) -> Outcome<Self, (Status, Self::Error), Data<'r>> {
|
) -> Outcome<Self, (Status, Self::Error), Data<'r>> {
|
||||||
|
{
|
||||||
|
let requests_received = &mut request
|
||||||
|
.guard::<&State<WebhookeyMetrics>>()
|
||||||
|
.await
|
||||||
|
.unwrap() // TODO: Check if unwrap need to be fixed
|
||||||
|
.requests_received
|
||||||
|
.lock()
|
||||||
|
.unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
|
||||||
|
**requests_received += 1;
|
||||||
|
}
|
||||||
|
|
||||||
match Hooks::get_commands(request, data).await {
|
match Hooks::get_commands(request, data).await {
|
||||||
Ok(hooks) => {
|
Ok(hooks) => {
|
||||||
if hooks.inner.is_empty() {
|
if hooks.inner.is_empty() {
|
||||||
|
@ -432,6 +459,16 @@ impl<'r> FromData<'r> for Hooks {
|
||||||
.client_ip()
|
.client_ip()
|
||||||
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
|
.unwrap_or(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
|
||||||
|
|
||||||
|
let hooks_unmatched = &mut request
|
||||||
|
.guard::<&State<WebhookeyMetrics>>()
|
||||||
|
.await
|
||||||
|
.unwrap() // TODO: Check if unwrap need to be fixed
|
||||||
|
.hooks_unmatched
|
||||||
|
.lock()
|
||||||
|
.unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
|
||||||
|
**hooks_unmatched += 1;
|
||||||
|
|
||||||
warn!("Unmatched hook from {}", &client_ip);
|
warn!("Unmatched hook from {}", &client_ip);
|
||||||
return Failure((Status::NotFound, WebhookeyError::UnmatchedHook(*client_ip)));
|
return Failure((Status::NotFound, WebhookeyError::UnmatchedHook(*client_ip)));
|
||||||
}
|
}
|
||||||
|
@ -440,10 +477,32 @@ impl<'r> FromData<'r> for Hooks {
|
||||||
}
|
}
|
||||||
Err(WebhookeyError::Unauthorized(e)) => {
|
Err(WebhookeyError::Unauthorized(e)) => {
|
||||||
error!("{}", WebhookeyError::Unauthorized(e));
|
error!("{}", WebhookeyError::Unauthorized(e));
|
||||||
|
|
||||||
|
let hooks_forbidden = &mut request
|
||||||
|
.guard::<&State<WebhookeyMetrics>>()
|
||||||
|
.await
|
||||||
|
.unwrap() // TODO: Check if unwrap need to be fixed
|
||||||
|
.hooks_forbidden
|
||||||
|
.lock()
|
||||||
|
.unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
|
||||||
|
**hooks_forbidden += 1;
|
||||||
|
|
||||||
Failure((Status::Unauthorized, WebhookeyError::Unauthorized(e)))
|
Failure((Status::Unauthorized, WebhookeyError::Unauthorized(e)))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("{}", e);
|
error!("{}", e);
|
||||||
|
|
||||||
|
let requests_invalid = &mut request
|
||||||
|
.guard::<&State<WebhookeyMetrics>>()
|
||||||
|
.await
|
||||||
|
.unwrap() // TODO: Check if unwrap need to be fixed
|
||||||
|
.requests_invalid
|
||||||
|
.lock()
|
||||||
|
.unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
|
||||||
|
**requests_invalid += 1;
|
||||||
|
|
||||||
Failure((Status::BadRequest, e))
|
Failure((Status::BadRequest, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,7 +510,11 @@ impl<'r> FromData<'r> for Hooks {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/", format = "json", data = "<hooks>")]
|
#[post("/", format = "json", data = "<hooks>")]
|
||||||
async fn receive_hook<'a>(address: SocketAddr, hooks: Hooks) -> Status {
|
async fn receive_hook<'a>(
|
||||||
|
address: SocketAddr,
|
||||||
|
hooks: Hooks,
|
||||||
|
metrics: &State<WebhookeyMetrics>,
|
||||||
|
) -> Status {
|
||||||
info!("Post request received from: {}", address);
|
info!("Post request received from: {}", address);
|
||||||
|
|
||||||
hooks.inner.iter().for_each(|(name, command)| {
|
hooks.inner.iter().for_each(|(name, command)| {
|
||||||
|
@ -462,9 +525,27 @@ async fn receive_hook<'a>(address: SocketAddr, hooks: Hooks) -> Status {
|
||||||
info!("Command `{}` exited with return code: {}", &command, status);
|
info!("Command `{}` exited with return code: {}", &command, status);
|
||||||
trace!("Output of command `{}` on stdout: {:?}", &command, &stdout);
|
trace!("Output of command `{}` on stdout: {:?}", &command, &stdout);
|
||||||
debug!("Output of command `{}` on stderr: {:?}", &command, &stderr);
|
debug!("Output of command `{}` on stderr: {:?}", &command, &stderr);
|
||||||
|
|
||||||
|
let commands_executed = &mut metrics.commands_executed.lock().unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
**commands_executed += 1;
|
||||||
|
|
||||||
|
match status {
|
||||||
|
0 => {
|
||||||
|
let commands_successful = &mut metrics.commands_successful.lock().unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
**commands_successful += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let commands_failed = &mut metrics.commands_failed.lock().unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
**commands_failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Execution of `{}` failed: {}", &command, e);
|
error!("Execution of `{}` failed: {}", &command, e);
|
||||||
|
|
||||||
|
let command_execution_failed =
|
||||||
|
&mut metrics.commands_execution_failed.lock().unwrap(); // TODO: Check if unwrap need to be fixed
|
||||||
|
**command_execution_failed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -472,6 +553,49 @@ async fn receive_hook<'a>(address: SocketAddr, hooks: Hooks) -> Status {
|
||||||
Status::Ok
|
Status::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/metrics")]
|
||||||
|
async fn metrics(metrics: &State<WebhookeyMetrics>) -> String {
|
||||||
|
format!(
|
||||||
|
r"# HELP webhookey_requests_received Number of requests received
|
||||||
|
# TYPE webhookey_requests_received gauge
|
||||||
|
webhookey_requests_received {:?}
|
||||||
|
# HELP webhookey_requests_invalid Number of invalid requests received
|
||||||
|
# TYPE webhookey_requests_invalid gauge
|
||||||
|
webhookey_requests_invalid {:?}
|
||||||
|
# HELP webhookey_hooks_successful Number of successfully executed hooks
|
||||||
|
# TYPE webhookey_hooks_successful gauge
|
||||||
|
webhookey_hooks_sucessful {:?}
|
||||||
|
# HELP webhookey_hooks_forbidden Number of forbidden requests
|
||||||
|
# TYPE webhookey_hooks_forbidden gauge
|
||||||
|
webhookey_hooks_forbidden {:?}
|
||||||
|
# HELP webhookey_hooks_unmatched Number of unmatched requests
|
||||||
|
# TYPE webhookey_hooks_unmatched gauge
|
||||||
|
webhookey_hooks_unmatched {:?}
|
||||||
|
# HELP webhookey_commands_executed Number of commands executed
|
||||||
|
# TYPE webhookey_commands_executed gauge
|
||||||
|
webhookey_commands_executed {:?}
|
||||||
|
# HELP webhookey_commands_execution_failed Number of commands failed to execute
|
||||||
|
# TYPE webhookey_commands_execution_failed gauge
|
||||||
|
webhookey_commands_execution_failed {:?}
|
||||||
|
# HELP webhookey_commands_successful Number of executed commands returning return code 0
|
||||||
|
# TYPE webhookey_commands_successful gauge
|
||||||
|
webhookey_commands_successful {:?}
|
||||||
|
# HELP webhookey_commands_failed Number of executed commands returning different return code than 0
|
||||||
|
# TYPE webhookey_commands_failed gauge
|
||||||
|
webhookey_commands_failed {:?}
|
||||||
|
",
|
||||||
|
metrics.requests_received.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.requests_invalid.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.hooks_successful.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.hooks_forbidden.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.hooks_unmatched.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.commands_executed.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.commands_execution_failed.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.commands_successful.lock().unwrap(), // TODO: Check if unwrap need to be fixed
|
||||||
|
metrics.commands_failed.lock().unwrap() // TODO: Check if unwrap need to be fixed
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::main]
|
#[rocket::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
@ -492,8 +616,9 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/", routes![receive_hook])
|
.mount("/", routes![receive_hook, metrics])
|
||||||
.manage(config)
|
.manage(config)
|
||||||
|
.manage(WebhookeyMetrics::default())
|
||||||
.launch()
|
.launch()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -531,7 +656,8 @@ mod tests {
|
||||||
|
|
||||||
let rocket = rocket::build()
|
let rocket = rocket::build()
|
||||||
.mount("/", routes![receive_hook])
|
.mount("/", routes![receive_hook])
|
||||||
.manage(config);
|
.manage(config)
|
||||||
|
.manage(WebhookeyMetrics::default());
|
||||||
|
|
||||||
let client = Client::tracked(rocket).await.unwrap();
|
let client = Client::tracked(rocket).await.unwrap();
|
||||||
let response = client
|
let response = client
|
||||||
|
@ -718,7 +844,8 @@ mod tests {
|
||||||
|
|
||||||
let rocket = rocket::build()
|
let rocket = rocket::build()
|
||||||
.mount("/", routes![receive_hook])
|
.mount("/", routes![receive_hook])
|
||||||
.manage(config);
|
.manage(config)
|
||||||
|
.manage(WebhookeyMetrics::default());
|
||||||
|
|
||||||
let client = Client::tracked(rocket).await.unwrap();
|
let client = Client::tracked(rocket).await.unwrap();
|
||||||
|
|
||||||
|
@ -775,7 +902,8 @@ mod tests {
|
||||||
|
|
||||||
let rocket = rocket::build()
|
let rocket = rocket::build()
|
||||||
.mount("/", routes![receive_hook])
|
.mount("/", routes![receive_hook])
|
||||||
.manage(config);
|
.manage(config)
|
||||||
|
.manage(WebhookeyMetrics::default());
|
||||||
|
|
||||||
let client = Client::tracked(rocket).await.unwrap();
|
let client = Client::tracked(rocket).await.unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue