feat: send logs into admin room
Log entries will automatically be deduplicated, so a message won't be sent if the same line has already been sent in the last 30 mins
This commit is contained in:
		
							parent
							
								
									ecea0d4af2
								
							
						
					
					
						commit
						9439f2c183
					
				
					 6 changed files with 87 additions and 23 deletions
				
			
		|  | @ -39,7 +39,7 @@ impl Database { | |||
|     /// Tries to remove the old database but ignores all errors.
 | ||||
|     pub fn try_remove(server_name: &str) -> Result<()> { | ||||
|         let mut path = ProjectDirs::from("xyz", "koesters", "conduit") | ||||
|             .ok_or(Error::BadConfig( | ||||
|             .ok_or_else(|| Error::bad_config( | ||||
|                 "The OS didn't return a valid home directory path.", | ||||
|             ))? | ||||
|             .data_dir() | ||||
|  | @ -59,7 +59,7 @@ impl Database { | |||
|             .map(|x| Ok::<_, Error>(x.to_owned())) | ||||
|             .unwrap_or_else(|_| { | ||||
|                 let path = ProjectDirs::from("xyz", "koesters", "conduit") | ||||
|                     .ok_or(Error::BadConfig( | ||||
|                     .ok_or_else(|| Error::bad_config( | ||||
|                         "The OS didn't return a valid home directory path.", | ||||
|                     ))? | ||||
|                     .data_dir() | ||||
|  | @ -67,7 +67,7 @@ impl Database { | |||
| 
 | ||||
|                 Ok(path | ||||
|                     .to_str() | ||||
|                     .ok_or(Error::BadConfig("Database path contains invalid unicode."))? | ||||
|                     .ok_or_else(|| Error::bad_config("Database path contains invalid unicode."))? | ||||
|                     .to_owned()) | ||||
|             })?; | ||||
| 
 | ||||
|  | @ -79,7 +79,7 @@ impl Database { | |||
|                         .get_int("cache_capacity") | ||||
|                         .unwrap_or(1024 * 1024 * 1024), | ||||
|                 ) | ||||
|                 .map_err(|_| Error::BadConfig("Cache capacity needs to be a u64."))?, | ||||
|                 .map_err(|_| Error::bad_config("Cache capacity needs to be a u64."))?, | ||||
|             ) | ||||
|             .print_profile_on_drop(false) | ||||
|             .open()?; | ||||
|  |  | |||
|  | @ -62,12 +62,12 @@ impl Globals { | |||
|                 .unwrap_or("localhost") | ||||
|                 .to_string() | ||||
|                 .try_into() | ||||
|                 .map_err(|_| Error::BadConfig("Invalid server_name."))?, | ||||
|                 .map_err(|_| Error::bad_config("Invalid server_name."))?, | ||||
|             max_request_size: config | ||||
|                 .get_int("max_request_size") | ||||
|                 .unwrap_or(20 * 1024 * 1024) // Default to 20 MB
 | ||||
|                 .try_into() | ||||
|                 .map_err(|_| Error::BadConfig("Invalid max_request_size."))?, | ||||
|                 .map_err(|_| Error::bad_config("Invalid max_request_size."))?, | ||||
|             registration_disabled: config.get_bool("registration_disabled").unwrap_or(false), | ||||
|             encryption_disabled: config.get_bool("encryption_disabled").unwrap_or(false), | ||||
|             federation_enabled: config.get_bool("federation_enabled").unwrap_or(false), | ||||
|  |  | |||
							
								
								
									
										61
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								src/error.rs
									
									
									
									
									
								
							|  | @ -1,7 +1,14 @@ | |||
| use std::{time::Duration, collections::HashMap, sync::RwLock, time::Instant}; | ||||
| 
 | ||||
| use log::error; | ||||
| use ruma::api::client::{error::ErrorKind, r0::uiaa::UiaaInfo}; | ||||
| use ruma::{ | ||||
|     api::client::{error::ErrorKind, r0::uiaa::UiaaInfo}, | ||||
|     events::room::message, | ||||
| }; | ||||
| use thiserror::Error; | ||||
| 
 | ||||
| use crate::{database::admin::AdminCommand, Database}; | ||||
| 
 | ||||
| #[cfg(feature = "conduit_bin")] | ||||
| use { | ||||
|     crate::RumaResponse, | ||||
|  | @ -53,6 +60,11 @@ impl Error { | |||
|         error!("BadDatabase: {}", message); | ||||
|         Self::BadDatabase(message) | ||||
|     } | ||||
| 
 | ||||
|     pub fn bad_config(message: &'static str) -> Self { | ||||
|         error!("BadConfig: {}", message); | ||||
|         Self::BadConfig(message) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "conduit_bin")] | ||||
|  | @ -95,3 +107,50 @@ where | |||
|         .respond_to(r) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct ConduitLogger { | ||||
|     pub db: Database, | ||||
|     pub last_logs: RwLock<HashMap<String, Instant>>, | ||||
| } | ||||
| 
 | ||||
| impl log::Log for ConduitLogger { | ||||
|     fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool { | ||||
|         true | ||||
|     } | ||||
| 
 | ||||
|     fn log(&self, record: &log::Record<'_>) { | ||||
|         let output = format!("{} - {}", record.level(), record.args()); | ||||
| 
 | ||||
|         println!("{}", output); | ||||
| 
 | ||||
|         if self.enabled(record.metadata()) | ||||
|             && record | ||||
|                 .module_path() | ||||
|                 .map_or(false, |path| path.starts_with("conduit::")) | ||||
|         { | ||||
|             if self | ||||
|                 .last_logs | ||||
|                 .read() | ||||
|                 .unwrap() | ||||
|                 .get(&output) | ||||
|                 .map_or(false, |i| i.elapsed() < Duration::from_secs(60 * 30)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if let Ok(mut_last_logs) = &mut self.last_logs.try_write() { | ||||
|                 mut_last_logs.insert(output.clone(), Instant::now()); | ||||
|             } | ||||
| 
 | ||||
|             self.db.admin.send(AdminCommand::SendTextMessage( | ||||
|                 message::TextMessageEventContent { | ||||
|                     body: output, | ||||
|                     formatted: None, | ||||
|                     relates_to: None, | ||||
|                 }, | ||||
|             )); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn flush(&self) {} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										17
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/main.rs
									
									
									
									
									
								
							|  | @ -11,7 +11,8 @@ mod ruma_wrapper; | |||
| mod utils; | ||||
| 
 | ||||
| pub use database::Database; | ||||
| pub use error::{Error, Result}; | ||||
| pub use error::{ConduitLogger, Error, Result}; | ||||
| use log::LevelFilter; | ||||
| pub use pdu::PduEvent; | ||||
| pub use rocket::State; | ||||
| pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse}; | ||||
|  | @ -19,6 +20,9 @@ pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse}; | |||
| use rocket::{fairing::AdHoc, routes}; | ||||
| 
 | ||||
| fn setup_rocket() -> rocket::Rocket { | ||||
|     // Force log level off, so we can use our own logger
 | ||||
|     std::env::set_var("ROCKET_LOG", "off"); | ||||
| 
 | ||||
|     rocket::ignite() | ||||
|         .mount( | ||||
|             "/", | ||||
|  | @ -133,6 +137,12 @@ fn setup_rocket() -> rocket::Rocket { | |||
|             let data = Database::load_or_create(rocket.config().await).expect("valid config"); | ||||
| 
 | ||||
|             data.sending.start_handler(&data.globals, &data.rooms); | ||||
|             log::set_boxed_logger(Box::new(ConduitLogger { | ||||
|                 db: data.clone(), | ||||
|                 last_logs: Default::default(), | ||||
|             })) | ||||
|             .unwrap(); | ||||
|             log::set_max_level(LevelFilter::Info); | ||||
| 
 | ||||
|             Ok(rocket.manage(data)) | ||||
|         })) | ||||
|  | @ -140,10 +150,5 @@ fn setup_rocket() -> rocket::Rocket { | |||
| 
 | ||||
| #[rocket::main] | ||||
| async fn main() { | ||||
|     // Default log level
 | ||||
|     if std::env::var("ROCKET_LOG").is_err() { | ||||
|         std::env::set_var("ROCKET_LOG", "critical"); | ||||
|     } | ||||
| 
 | ||||
|     setup_rocket().launch().await.unwrap(); | ||||
| } | ||||
|  |  | |||
|  | @ -97,7 +97,7 @@ where | |||
|             handle.read_to_end(&mut body).await.unwrap(); | ||||
| 
 | ||||
|             let http_request = http_request.body(body.clone()).unwrap(); | ||||
|             log::info!("{:?}", http_request); | ||||
|             log::debug!("{:?}", http_request); | ||||
| 
 | ||||
|             match <T as Outgoing>::Incoming::try_from(http_request) { | ||||
|                 Ok(t) => Success(Ruma { | ||||
|  |  | |||
|  | @ -58,12 +58,12 @@ where | |||
|     T: Debug, | ||||
| { | ||||
|     if !globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let resolver = AsyncResolver::tokio_from_system_conf() | ||||
|         .await | ||||
|         .map_err(|_| Error::BadConfig("Failed to set up trust dns resolver with system config."))?; | ||||
|         .map_err(|_| Error::bad_config("Failed to set up trust dns resolver with system config."))?; | ||||
| 
 | ||||
|     let mut host = None; | ||||
| 
 | ||||
|  | @ -213,7 +213,7 @@ where | |||
| #[cfg_attr(feature = "conduit_bin", get("/_matrix/federation/v1/version"))] | ||||
| pub fn get_server_version(db: State<'_, Database>) -> ConduitResult<get_server_version::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     Ok(get_server_version::Response { | ||||
|  | @ -276,7 +276,7 @@ pub async fn get_public_rooms_filtered_route( | |||
|     body: Ruma<get_public_rooms_filtered::v1::Request<'_>>, | ||||
| ) -> ConduitResult<get_public_rooms_filtered::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let response = client_server::get_public_rooms_filtered_helper( | ||||
|  | @ -323,7 +323,7 @@ pub async fn get_public_rooms_route( | |||
|     body: Ruma<get_public_rooms::v1::Request<'_>>, | ||||
| ) -> ConduitResult<get_public_rooms::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let response = client_server::get_public_rooms_filtered_helper( | ||||
|  | @ -370,7 +370,7 @@ pub fn send_transaction_message_route<'a>( | |||
|     body: Ruma<send_transaction_message::v1::Request<'_>>, | ||||
| ) -> ConduitResult<send_transaction_message::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     //dbg!(&*body);
 | ||||
|  | @ -423,7 +423,7 @@ pub fn get_missing_events_route<'a>( | |||
|     body: Ruma<get_missing_events::v1::Request<'_>>, | ||||
| ) -> ConduitResult<get_missing_events::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let mut queued_events = body.latest_events.clone(); | ||||
|  | @ -468,7 +468,7 @@ pub fn get_profile_information_route<'a>( | |||
|     body: Ruma<get_profile_information::v1::Request<'_>>, | ||||
| ) -> ConduitResult<get_profile_information::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let mut displayname = None; | ||||
|  | @ -500,7 +500,7 @@ pub fn get_user_devices_route<'a>( | |||
|     body: Ruma<membership::v1::Request<'_>>, | ||||
| ) -> ConduitResult<get_profile_information::v1::Response> { | ||||
|     if !db.globals.federation_enabled() { | ||||
|         return Err(Error::BadConfig("Federation is disabled.")); | ||||
|         return Err(Error::bad_config("Federation is disabled.")); | ||||
|     } | ||||
| 
 | ||||
|     let mut displayname = None; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue