Added logging to bot

+ Cleaned up error handling to make it easier to handle multiple error levels
main
Joey Hines 2021-12-12 11:55:09 -07:00
parent 199516f3cc
commit c45bb592db
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
10 changed files with 124 additions and 80 deletions

3
Cargo.lock generated
View File

@ -442,11 +442,13 @@ dependencies = [
"async-trait", "async-trait",
"config", "config",
"geoffrey_models", "geoffrey_models",
"log",
"reqwest", "reqwest",
"serde 1.0.130", "serde 1.0.130",
"serde_json", "serde_json",
"serde_plain", "serde_plain",
"serenity", "serenity",
"simple_logger",
"structopt", "structopt",
"tokio", "tokio",
] ]
@ -471,6 +473,7 @@ name = "geoffrey_models"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"log",
"serde 1.0.130", "serde 1.0.130",
"serde_json", "serde_json",
] ]

View File

@ -2,37 +2,7 @@ use log::{LevelFilter, SetLoggerError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
#[derive(Serialize, Deserialize, Clone, Debug)] use geoffrey_models::logging::LogLevel;
pub enum LogLevel {
None,
Warn,
Info,
Debug,
}
impl From<&str> for LogLevel {
#[allow(clippy::wildcard_in_or_patterns)]
fn from(s: &str) -> Self {
let s = s.to_lowercase();
match s.as_str() {
"warn" | "w" => LogLevel::Warn,
"info" | "i" => LogLevel::Info,
"debug" | "d" => LogLevel::Debug,
"none" | _ => LogLevel::None,
}
}
}
impl From<LogLevel> for LevelFilter {
fn from(l: LogLevel) -> Self {
match l {
LogLevel::None => LevelFilter::Off,
LogLevel::Warn => LevelFilter::Warn,
LogLevel::Info => LevelFilter::Info,
LogLevel::Debug => LevelFilter::Debug,
}
}
}
pub fn init_logging(log_level: LogLevel) -> Result<(), SetLoggerError> { pub fn init_logging(log_level: LogLevel) -> Result<(), SetLoggerError> {
SimpleLogger::new() SimpleLogger::new()

View File

@ -1,24 +1,28 @@
mod commands;
mod config;
mod context;
mod helper;
mod logging;
use crate::commands::add_token::AddToken;
use crate::commands::{command_filter, Command};
use crate::config::GeoffreyAPIConfig;
use crate::context::Context;
use crate::logging::{init_logging, LogLevel};
use geoffrey_models::models::parameters::add_token_params::AddTokenParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::token::Permissions;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use structopt::StructOpt; use structopt::StructOpt;
use geoffrey_models::logging::LogLevel;
use geoffrey_models::models::parameters::add_token_params::AddTokenParams;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::token::Permissions;
use crate::commands::{Command, command_filter};
use crate::commands::add_token::AddToken;
use crate::config::GeoffreyAPIConfig;
use crate::context::Context;
use crate::logging::init_logging;
mod commands;
mod config;
mod context;
mod helper;
mod logging;
pub type Result<T> = std::result::Result<T, GeoffreyAPIError>; pub type Result<T> = std::result::Result<T, GeoffreyAPIError>;
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]

View File

@ -16,3 +16,5 @@ serde_plain = "1.0.0"
async-trait = "0.1.51" async-trait = "0.1.51"
config = "0.11.0" config = "0.11.0"
structopt = "0.3.21" structopt = "0.3.21"
log = "0.4.14"
simple_logger = "1.13.0"

View File

@ -98,33 +98,29 @@ pub trait BotCommand {
resp resp
} else { } else {
match err { match err {
CommandError::GeoffreyApi(err) => match err { CommandError::GeoffreyApi(GeoffreyAPIError::PlayerNotRegistered) => {
GeoffreyAPIError::PlayerNotRegistered => { "You need to register before using this command!".to_string()
"You need to register before using this command!".to_string() }
} CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
GeoffreyAPIError::EntryNotFound => {
"Couldn't find that, maybe look for something that exists?".to_string() "Couldn't find that, maybe look for something that exists?".to_string()
} }
GeoffreyAPIError::PermissionInsufficient => { CommandError::GeoffreyApi(GeoffreyAPIError::PermissionInsufficient) => {
"Looks like you don't have permission for that.".to_string() "Looks like you don't have permission for that.".to_string()
} }
GeoffreyAPIError::EntryNotUnique => { CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotUnique) => {
"Slow down, I already know that thing. Try a new name.".to_string() "Slow down, I already know that thing. Try a new name.".to_string()
} }
GeoffreyAPIError::DatabaseError(_) => "How the heck u mess that up".to_string(), CommandError::GeoffreyApi(GeoffreyAPIError::MultipleLocationsMatch) => {
GeoffreyAPIError::TokenNotAuthorized => "WHO ARE YOU????".to_string(), "I couldn't match a single location, narrow down your search".to_string()
GeoffreyAPIError::MultipleLocationsMatch => { }
"I couldn't match a single location, narrow down your search".to_string() CommandError::GeoffreyApi(GeoffreyAPIError::ParameterInvalid(err)) => {
} format!(
GeoffreyAPIError::ParameterInvalid(err) => { "Welp, you some how messed up the {} parameter, great job",
format!( err
"Welp, you some how messed up the {} parameter, great job", )
err }
)
}
},
_ => { _ => {
println!("GeoffreyBot got an unhandled error: {}", err); log::warn!("GeoffreyBot got an unhandled error: {}", err);
format!("OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our \ format!("OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our \
headquarters are working VEWY HAWD to fix this! (Error in command {})", Self::command_name()) headquarters are working VEWY HAWD to fix this! (Error in command {})", Self::command_name())
} }
@ -149,6 +145,8 @@ pub trait BotCommand {
) -> Result<String, CommandError> { ) -> Result<String, CommandError> {
let mut args = Self::process_arguments(command_interact).await?; let mut args = Self::process_arguments(command_interact).await?;
log::info!("Running command {}, with args {:?}", Self::command_name(), args);
args.set_token(ctx.cfg.api.token.clone()); args.set_token(ctx.cfg.api.token.clone());
args.set_user_id(user_id); args.set_user_id(user_id);

View File

@ -0,0 +1,11 @@
use log::{LevelFilter, SetLoggerError};
use simple_logger::SimpleLogger;
use geoffrey_models::logging::LogLevel;
pub fn init_logging(log_level: LogLevel) -> Result<(), SetLoggerError> {
SimpleLogger::new()
.with_level(LevelFilter::Warn)
.with_module_level("geoffrey_bot", log_level.into())
.init()
}

View File

@ -1,6 +1,7 @@
mod bot; mod bot;
mod configs; mod configs;
mod context; mod context;
mod logging;
use crate::bot::create_commands; use crate::bot::create_commands;
use crate::configs::GeoffreyBotConfig; use crate::configs::GeoffreyBotConfig;
@ -22,12 +23,22 @@ use serenity::{
}; };
use std::path::PathBuf; use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use geoffrey_models::logging::LogLevel;
use crate::logging::init_logging;
#[derive(Debug, StructOpt, Clone)] #[derive(Debug, StructOpt, Clone)]
#[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")] #[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")]
struct Args { struct Args {
#[structopt(env = "GEOFFREY_BOT_CONFIG", parse(from_os_str))] #[structopt(env = "GEOFFREY_BOT_CONFIG", parse(from_os_str))]
config: PathBuf, config: PathBuf,
#[structopt(
short,
long,
env = "GEOFFREY_LOG_LEVEL",
parse(from_str),
default_value = "Info"
)]
log_level: LogLevel,
} }
struct HttpClient; struct HttpClient;
@ -41,13 +52,13 @@ struct Handler;
#[async_trait] #[async_trait]
impl EventHandler for Handler { impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) { async fn ready(&self, ctx: Context, ready: Ready) {
println!("{} is connected!", ready.user.name); log::info!("{} is connected!", ready.user.name);
let commands = create_commands(&ctx).await.unwrap(); let commands = create_commands(&ctx).await.unwrap();
println!("The following bot have been registered:"); log::debug!("The following bot have been registered:");
for command in commands { for command in commands {
println!( log::debug!(
"{}: {} - {:?}", "{}: {} - {:?}",
command.name, command.description, command.options command.name, command.description, command.options
); );
@ -92,7 +103,7 @@ impl EventHandler for Handler {
.await .await
.unwrap() .unwrap()
} else if let Interaction::Ping(_) = interaction { } else if let Interaction::Ping(_) = interaction {
println!("Ping recv'ed"); // do nothing
} }
} }
} }
@ -100,21 +111,30 @@ impl EventHandler for Handler {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let args: Args = Args::from_args(); let args: Args = Args::from_args();
init_logging(args.log_level).expect("Unable to init logging");
let cfg = match GeoffreyBotConfig::new(args.config.as_path()) { let cfg = match GeoffreyBotConfig::new(args.config.as_path()) {
Ok(cfg) => cfg, Ok(cfg) => cfg,
Err(e) => { Err(e) => {
println!("Error opening config: {}", e); log::warn!("Error opening config: {}", e);
return; return;
} }
}; };
let mut client = Client::builder(cfg.discord.token.clone()) let client = Client::builder(cfg.discord.token.clone())
.event_handler(Handler) .event_handler(Handler)
.application_id(cfg.discord.app_id) .application_id(cfg.discord.app_id)
.await .await;
.expect("Error creating Geoffrey client");
let mut client = match client {
Ok(client) => client,
Err(e) => {
log::warn!("Unable to init serenity client: {}", e);
return;
}
};
// Block for initializing global data
{ {
let mut data = client.data.write().await; let mut data = client.data.write().await;
@ -124,7 +144,7 @@ async fn main() {
}) })
} }
if let Err(why) = client.start().await { if let Err(e) = client.start().await {
println!("Client error: {:?}", why); log::warn!("Client error: {:?}", e);
} }
} }

View File

@ -10,3 +10,4 @@ edition = "2018"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
log = "0.4.14"

View File

@ -4,6 +4,7 @@ use serde::Serialize;
use std::fmt::Debug; use std::fmt::Debug;
pub mod models; pub mod models;
pub mod logging;
pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned + Debug { pub trait GeoffreyDatabaseModel: Serialize + DeserializeOwned + Debug {
fn id(&self) -> Option<u64>; fn id(&self) -> Option<u64>;

View File

@ -0,0 +1,34 @@
use log::LevelFilter;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum LogLevel {
None,
Warn,
Info,
Debug,
}
impl From<&str> for LogLevel {
#[allow(clippy::wildcard_in_or_patterns)]
fn from(s: &str) -> Self {
let s = s.to_lowercase();
match s.as_str() {
"warn" | "w" => LogLevel::Warn,
"info" | "i" => LogLevel::Info,
"debug" | "d" => LogLevel::Debug,
"none" | _ => LogLevel::None,
}
}
}
impl From<LogLevel> for LevelFilter {
fn from(l: LogLevel) -> Self {
match l {
LogLevel::None => LevelFilter::Off,
LogLevel::Warn => LevelFilter::Warn,
LogLevel::Info => LevelFilter::Info,
LogLevel::Debug => LevelFilter::Debug,
}
}
}