Skip to content

Commit cd69498

Browse files
committed
init pass error handling
1 parent 6856d17 commit cd69498

File tree

6 files changed

+143
-48
lines changed

6 files changed

+143
-48
lines changed

Cargo.lock

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ hyper = "0.13"
1616
prometheus = "0.10"
1717
futures = "0.3"
1818
anyhow = "1.0"
19+
log = "0.4"
20+
env_logger = { version = "0.8", features = ["termcolor", "humantime"] }

src/collectors/github_rate_limit.rs

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use prometheus::{core::Collector, IntGauge, Opts};
22

33
use crate::Config;
4+
use anyhow::{Error, Result};
5+
use log::{debug, error};
46
use reqwest::header::{ACCEPT, AUTHORIZATION, USER_AGENT};
57
use reqwest::{Client, Method, Request};
68
use std::collections::HashMap;
@@ -24,7 +26,7 @@ enum GithubReqBuilder {
2426
}
2527

2628
impl GithubReqBuilder {
27-
fn build_request(&self, client: &Client, token: &str) -> Result<Request, reqwest::Error> {
29+
fn build_request(&self, client: &Client, token: &str) -> Result<Request> {
2830
let rb = match self {
2931
Self::User => client.request(Method::GET, GH_API_USER_ENDPOINT),
3032
Self::RateLimit => client.request(Method::GET, GH_API_RATE_LIMIT_ENDPOINT),
@@ -37,12 +39,12 @@ impl GithubReqBuilder {
3739
.header(AUTHORIZATION, format!("{} {}", "token", token))
3840
.header(ACCEPT, "application/vnd.github.v3+json")
3941
.build()
42+
.map_err(Error::from)
4043
}
4144
}
4245

4346
#[derive(Clone)]
4447
pub struct GitHubRateLimit {
45-
descriptions: Vec<prometheus::core::Desc>,
4648
users: Vec<User>,
4749
}
4850

@@ -54,14 +56,17 @@ impl GitHubRateLimit {
5456
.map(|v| v.trim().to_string())
5557
.collect();
5658

57-
let users = Self::get_users_for_tokens(tokens).await;
58-
let descriptions = Vec::new();
59-
60-
let rv = Self {
61-
users,
62-
descriptions,
59+
let users = match Self::get_users_for_tokens(tokens).await {
60+
Ok(v) => v,
61+
Err(e) => {
62+
error!("Unable to get usernames for rate limit stats: {:?}", e);
63+
//TODO: we going empty for now, how do we want to handle this in the future?
64+
Vec::new()
65+
}
6366
};
6467

68+
let rv = Self { users };
69+
6570
let refresh_rate = config.gh_rate_limit_stats_cache_refresh;
6671
let mut rv2 = rv.clone();
6772
tokio::spawn(async move {
@@ -74,12 +79,12 @@ impl GitHubRateLimit {
7479
rv
7580
}
7681

77-
async fn get_users_for_tokens(tokens: Vec<String>) -> Vec<User> {
82+
async fn get_users_for_tokens(tokens: Vec<String>) -> Result<Vec<User>> {
7883
let ns = String::from("rustinfra_github_rate_limit");
7984
let mut rv: Vec<User> = Vec::new();
8085
for token in tokens.into_iter() {
8186
let ns2 = ns.clone();
82-
let username = GitHubRateLimit::get_github_api_username(&token).await;
87+
let username = GitHubRateLimit::get_github_api_username(&token).await?;
8388
let user_future = tokio::task::spawn_blocking(move || {
8489
let rate_limit = IntGauge::with_opts(
8590
Opts::new("limit", "Rate limit.")
@@ -119,10 +124,10 @@ impl GitHubRateLimit {
119124
rv.push(user);
120125
}
121126

122-
rv
127+
Ok(rv)
123128
}
124129

125-
async fn get_github_api_username(token: &str) -> String {
130+
async fn get_github_api_username(token: &str) -> Result<String> {
126131
#[derive(serde::Deserialize)]
127132
struct GithubUser {
128133
pub login: String,
@@ -135,34 +140,47 @@ impl GitHubRateLimit {
135140
let u = client
136141
.execute(req)
137142
.await
138-
.unwrap()
143+
.map_err(Error::from)?
139144
.json::<GithubUser>()
140145
.await
141-
.unwrap();
146+
.map_err(Error::from)?;
142147

143-
u.login
148+
Ok(u.login)
144149
}
145150

146151
async fn update_stats(&mut self) {
152+
debug!("Updating rate limit stats");
153+
147154
#[derive(Debug, serde::Deserialize)]
148155
struct GithubRateLimit {
149156
pub rate: HashMap<String, usize>,
150157
}
151158

152159
let client = reqwest::Client::new();
153-
154-
//FIXME: we will (might?) need a RWLock on users structure
155160
for u in self.users.iter_mut() {
156-
let req = GithubReqBuilder::RateLimit
157-
.build_request(&client, &u.token)
158-
.unwrap();
159-
let mut data = client
160-
.execute(req)
161-
.await
162-
.unwrap()
163-
.json::<GithubRateLimit>()
164-
.await
165-
.unwrap();
161+
let req = match GithubReqBuilder::RateLimit.build_request(&client, &u.token) {
162+
Ok(r) => r,
163+
Err(e) => {
164+
error!("Unable to build request to update stats: {:?}", e);
165+
return;
166+
}
167+
};
168+
169+
let response = match client.execute(req).await {
170+
Ok(resp) => resp,
171+
Err(e) => {
172+
error!("Unable to execute request to update stats: {:?}", e);
173+
return;
174+
}
175+
};
176+
177+
let mut data = match response.json::<GithubRateLimit>().await {
178+
Ok(d) => d,
179+
Err(e) => {
180+
error!("Unable to deserialize rate limit stats: {:?}", e);
181+
return;
182+
}
183+
};
166184

167185
let remaining = data.rate.remove("remaining").unwrap_or(0);
168186
let limit = data.rate.remove("limit").unwrap_or(0);
@@ -177,7 +195,8 @@ impl GitHubRateLimit {
177195

178196
impl Collector for GitHubRateLimit {
179197
fn desc(&self) -> std::vec::Vec<&prometheus::core::Desc> {
180-
self.descriptions.iter().collect()
198+
// descriptions are being defined in the initialization of the metrics options
199+
Vec::default()
181200
}
182201

183202
fn collect(&self) -> std::vec::Vec<prometheus::proto::MetricFamily> {

src/collectors/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
mod github_rate_limit;
2+
23
pub use crate::collectors::github_rate_limit::GitHubRateLimit;
34

45
use crate::MetricProvider;
6+
use anyhow::Result;
57
use futures::FutureExt;
8+
use log::info;
69

710
// register collectors for metrics gathering
8-
pub async fn register_collectors(p: &MetricProvider) -> Result<(), prometheus::Error> {
11+
pub async fn register_collectors(p: &MetricProvider) -> Result<()> {
912
GitHubRateLimit::new(&p.config)
10-
.map(|rl| p.register_collector(rl))
13+
.map(|rl| {
14+
info!("Registering GitHubRateLimit collector");
15+
p.register_collector(rl)
16+
})
1117
.await
1218
}

src/lib.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
pub mod collectors;
44
mod config;
5+
56
pub use config::Config;
67

78
use prometheus::core::Collector;
89
use prometheus::{Encoder, Registry};
910

11+
use anyhow::{Error, Result};
1012
use futures::future;
1113
use futures::task::{Context, Poll};
1214
use hyper::service::Service;
1315
use hyper::{Body, Method, Request, Response, StatusCode};
16+
use log::{debug, error};
1417

1518
#[derive(Clone, Debug)]
1619
pub struct MetricProvider {
@@ -24,15 +27,19 @@ impl MetricProvider {
2427
Self { register, config }
2528
}
2629

27-
fn register_collector(
28-
&self,
29-
collector: impl Collector + 'static,
30-
) -> Result<(), prometheus::Error> {
31-
self.register.register(Box::new(collector))
30+
fn register_collector(&self, collector: impl Collector + 'static) -> Result<()> {
31+
self.register
32+
.register(Box::new(collector))
33+
.map_err(Error::from)
3234
}
3335

34-
fn gather_with_encoder<BUF: std::io::Write>(&self, encoder: impl Encoder, buf: &mut BUF) {
35-
encoder.encode(&self.register.gather(), buf).unwrap();
36+
fn gather_with_encoder<BUF>(&self, encoder: impl Encoder, buf: &mut BUF) -> Result<()>
37+
where
38+
BUF: std::io::Write,
39+
{
40+
encoder
41+
.encode(&self.register.gather(), buf)
42+
.map_err(Error::from)
3643
}
3744

3845
pub fn into_service(self) -> MetricProviderFactory {
@@ -50,17 +57,26 @@ impl Service<Request<Body>> for MetricProvider {
5057
}
5158

5259
fn call(&mut self, req: Request<Body>) -> Self::Future {
60+
debug!("New Request to endpoint {}", req.uri().path());
61+
5362
let output = match (req.method(), req.uri().path()) {
5463
// Metrics handler
5564
(&Method::GET, "/metrics") => {
5665
let encoder = prometheus::TextEncoder::new();
5766
let mut buffer = Vec::<u8>::new();
58-
self.gather_with_encoder(encoder, &mut buffer);
59-
60-
Response::builder()
61-
.status(StatusCode::OK)
62-
.body(Body::from(buffer))
63-
.unwrap()
67+
match self.gather_with_encoder(encoder, &mut buffer) {
68+
Ok(_) => Response::builder()
69+
.status(StatusCode::OK)
70+
.body(Body::from(buffer))
71+
.unwrap(),
72+
Err(e) => {
73+
error!("{:?}", e);
74+
Response::builder()
75+
.status(StatusCode::INTERNAL_SERVER_ERROR)
76+
.body(Body::empty())
77+
.unwrap()
78+
}
79+
}
6480
}
6581
// All other paths and methods
6682
_ => Response::builder()

src/main.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1+
use anyhow::{bail, Result};
12
use hyper::Server;
3+
use log::info;
24
use monitorbot::Config;
35
use monitorbot::{collectors::register_collectors, MetricProvider};
46
use std::net::SocketAddr;
57

68
#[tokio::main]
7-
async fn main() -> Result<(), Box<dyn std::error::Error>> {
9+
async fn main() -> Result<()> {
810
dotenv::dotenv().ok();
11+
env_logger::init();
912

1013
let config = Config::from_env()?;
1114
let port = config.port;
1215
let addr = SocketAddr::from(([0, 0, 0, 0], port));
1316

1417
let provider = MetricProvider::new(config);
1518
if let Err(e) = register_collectors(&provider).await {
16-
eprintln!("Unable to register collectors: {:#?}", e);
17-
return Ok(());
19+
bail!("(Registering collectors) {}", e)
1820
}
1921

2022
let server = Server::bind(&addr).serve(provider.into_service());
21-
println!("Server listening on port {}", port);
23+
info!("Server listening on port: {}", port);
2224

2325
if let Err(e) = server.await {
24-
eprintln!("Server error: {}", e);
26+
bail!("(Hyper server error) {}", e);
2527
}
2628

2729
Ok(())

0 commit comments

Comments
 (0)