Skip to content

Commit dd244c8

Browse files
authored
Merge pull request #9 from njasm/patch_err_handling
tiny change towards better error handling
2 parents a3a3858 + 56713ad commit dd244c8

File tree

6 files changed

+132
-55
lines changed

6 files changed

+132
-55
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: 35 additions & 34 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::{Context, 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, Error> {
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,49 +39,50 @@ 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

4951
impl GitHubRateLimit {
50-
pub async fn new(config: &Config) -> Self {
52+
pub async fn new(config: &Config) -> Result<Self, Error> {
5153
let tokens: Vec<String> = config
5254
.gh_rate_limit_tokens
5355
.split(',')
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+
let users = Self::get_users_for_tokens(tokens)
60+
.await
61+
.context("Unable to get usernames for rate limit stats")?;
5962

60-
let rv = Self {
61-
users,
62-
descriptions,
63-
};
63+
let rv = Self { users };
6464

6565
let refresh_rate = config.gh_rate_limit_stats_cache_refresh;
6666
let mut rv2 = rv.clone();
6767
tokio::spawn(async move {
6868
loop {
69-
rv2.update_stats().await;
69+
if let Err(e) = rv2.update_stats().await {
70+
error!("{:#?}", e);
71+
}
72+
7073
tokio::time::delay_for(Duration::from_secs(refresh_rate)).await;
7174
}
7275
});
7376

74-
rv
77+
Ok(rv)
7578
}
7679

77-
async fn get_users_for_tokens(tokens: Vec<String>) -> Vec<User> {
80+
async fn get_users_for_tokens(tokens: Vec<String>) -> Result<Vec<User>, Error> {
7881
let ns = String::from("monitorbot_github_rate_limit");
7982
let mut rv: Vec<User> = Vec::new();
8083
for token in tokens.into_iter() {
8184
let ns2 = ns.clone();
82-
let username = GitHubRateLimit::get_github_api_username(&token).await;
85+
let username = GitHubRateLimit::get_github_api_username(&token).await?;
8386
let user_future = tokio::task::spawn_blocking(move || {
8487
let rate_limit = IntGauge::with_opts(
8588
Opts::new("limit", "Rate limit.")
@@ -119,50 +122,45 @@ impl GitHubRateLimit {
119122
rv.push(user);
120123
}
121124

122-
rv
125+
Ok(rv)
123126
}
124127

125-
async fn get_github_api_username(token: &str) -> String {
128+
async fn get_github_api_username(token: &str) -> Result<String, Error> {
126129
#[derive(serde::Deserialize)]
127130
struct GithubUser {
128131
pub login: String,
129132
}
130133

131134
let client = reqwest::Client::new();
132-
let req = GithubReqBuilder::User
133-
.build_request(&client, &token)
134-
.unwrap();
135-
let u = client
136-
.execute(req)
137-
.await
138-
.unwrap()
139-
.json::<GithubUser>()
140-
.await
141-
.unwrap();
135+
let req = GithubReqBuilder::User.build_request(&client, &token)?;
136+
let u = client.execute(req).await?.json::<GithubUser>().await?;
142137

143-
u.login
138+
Ok(u.login)
144139
}
145140

146-
async fn update_stats(&mut self) {
141+
async fn update_stats(&mut self) -> Result<(), Error> {
142+
debug!("Updating rate limit stats");
143+
147144
#[derive(Debug, serde::Deserialize)]
148145
struct GithubRateLimit {
149146
pub rate: HashMap<String, usize>,
150147
}
151148

152149
let client = reqwest::Client::new();
153-
154-
//FIXME: we will (might?) need a RWLock on users structure
155150
for u in self.users.iter_mut() {
156151
let req = GithubReqBuilder::RateLimit
157152
.build_request(&client, &u.token)
158-
.unwrap();
159-
let mut data = client
153+
.context("Unable to build request to update stats")?;
154+
155+
let response = client
160156
.execute(req)
161157
.await
162-
.unwrap()
158+
.context("Unable to execute request to update stats")?;
159+
160+
let mut data = response
163161
.json::<GithubRateLimit>()
164162
.await
165-
.unwrap();
163+
.context("Unable to deserialize rate limit stats")?;
166164

167165
let remaining = data.rate.remove("remaining").unwrap_or(0);
168166
let limit = data.rate.remove("limit").unwrap_or(0);
@@ -172,12 +170,15 @@ impl GitHubRateLimit {
172170
u.reset.set(reset as i64);
173171
u.limit.set(limit as i64);
174172
}
173+
174+
Ok(())
175175
}
176176
}
177177

178178
impl Collector for GitHubRateLimit {
179179
fn desc(&self) -> std::vec::Vec<&prometheus::core::Desc> {
180-
self.descriptions.iter().collect()
180+
// descriptions are being defined in the initialization of the metrics options
181+
Vec::default()
181182
}
182183

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

src/collectors/mod.rs

Lines changed: 9 additions & 3 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;
5-
use futures::FutureExt;
6+
use anyhow::{Error, Result};
7+
use futures::TryFutureExt;
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<(), Error> {
912
GitHubRateLimit::new(&p.config)
10-
.map(|rl| p.register_collector(rl))
13+
.and_then(|rl| async {
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<(), Error> {
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<(), Error>
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, Error, 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<(), Error> {
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)