From bcbde2062317d3cbd46982eb708486da4bf50ee8 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Fri, 11 Oct 2024 16:41:52 +0200 Subject: [PATCH] Make Logging configurable --- pumpkin-config/src/commands.rs | 8 ++++-- pumpkin-config/src/lib.rs | 3 +++ pumpkin-config/src/logging.rs | 40 +++++++++++++++++++++++++++++ pumpkin-config/src/rcon.rs | 26 +++++++++++++++++++ pumpkin/Cargo.toml | 2 +- pumpkin/src/client/player_packet.rs | 7 +++++ pumpkin/src/main.rs | 38 ++++++++++++++++++++++++--- pumpkin/src/rcon/mod.rs | 32 +++++++++++++++++------ 8 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 pumpkin-config/src/logging.rs diff --git a/pumpkin-config/src/commands.rs b/pumpkin-config/src/commands.rs index 94ee4d8c2..8ced89b47 100644 --- a/pumpkin-config/src/commands.rs +++ b/pumpkin-config/src/commands.rs @@ -4,11 +4,15 @@ use serde::{Deserialize, Serialize}; pub struct CommandsConfig { /// Are commands from the Console accepted ? pub use_console: bool, - // TODO: commands... + /// Should be commands from players be logged in console? + pub log_console: bool, // TODO: commands... } impl Default for CommandsConfig { fn default() -> Self { - Self { use_console: true } + Self { + use_console: true, + log_console: true, + } } } diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index a65caa444..438aa9064 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,4 +1,5 @@ use log::warn; +use logging::LoggingConfig; use pumpkin_core::{Difficulty, GameMode}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -10,6 +11,7 @@ use std::{ }; pub mod auth; +pub mod logging; pub mod proxy; pub mod resource_pack; @@ -46,6 +48,7 @@ pub struct AdvancedConfiguration { pub commands: CommandsConfig, pub rcon: RCONConfig, pub pvp: PVPConfig, + pub logging: LoggingConfig, } #[derive(Serialize, Deserialize)] diff --git a/pumpkin-config/src/logging.rs b/pumpkin-config/src/logging.rs new file mode 100644 index 000000000..d6bcc9027 --- /dev/null +++ b/pumpkin-config/src/logging.rs @@ -0,0 +1,40 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct LoggingConfig { + pub enabled: bool, + pub level: LevelFilter, + pub env: bool, + pub threads: bool, + pub color: bool, + pub timestamp: bool, +} + +impl Default for LoggingConfig { + fn default() -> Self { + Self { + enabled: true, + level: LevelFilter::Info, + env: false, + threads: true, + color: true, + timestamp: true, + } + } +} + +#[derive(Deserialize, Serialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum LevelFilter { + /// A level lower than all log levels. + Off, + /// Corresponds to the `Error` log level. + Error, + /// Corresponds to the `Warn` log level. + Warn, + /// Corresponds to the `Info` log level. + Info, + /// Corresponds to the `Debug` log level. + Debug, + /// Corresponds to the `Trace` log level. + Trace, +} diff --git a/pumpkin-config/src/rcon.rs b/pumpkin-config/src/rcon.rs index f15fa655e..f7de36b45 100644 --- a/pumpkin-config/src/rcon.rs +++ b/pumpkin-config/src/rcon.rs @@ -13,6 +13,31 @@ pub struct RCONConfig { /// The maximum number of concurrent RCON connections allowed. /// If 0 there is no limit pub max_connections: u32, + /// RCON Logging + pub logging: RCONLogging, +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct RCONLogging { + /// Whether successful RCON logins should be logged. + pub log_logged_successfully: bool, + /// Whether failed RCON login attempts with incorrect passwords should be logged. + pub log_wrong_password: bool, + /// Whether all RCON commands, regardless of success or failure, should be logged. + pub log_commands: bool, + /// Whether RCON quit commands should be logged. + pub log_quit: bool, +} + +impl Default for RCONLogging { + fn default() -> Self { + Self { + log_logged_successfully: true, + log_wrong_password: true, + log_commands: true, + log_quit: true, + } + } } impl Default for RCONConfig { @@ -22,6 +47,7 @@ impl Default for RCONConfig { address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575), password: "".to_string(), max_connections: 0, + logging: Default::default(), } } } diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 401417a60..3320c4d6c 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -61,7 +61,7 @@ base64 = "0.22.1" png = "0.17.14" # logging -simple_logger = "5.0.0" +simple_logger = { version = "5.0.0", features = ["threads"] } log.workspace = true # networking diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 10d9e5f8d..fd8a31a1a 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -227,6 +227,13 @@ impl Player { pub fn handle_chat_command(&self, server: &Arc, command: SChatCommand) { let dispatcher = server.command_dispatcher.clone(); dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command); + if ADVANCED_CONFIG.commands.log_console { + log::info!( + "Player ({}): executed command /{}", + self.gameprofile.name, + command.command + ); + } } pub fn handle_player_ground(&self, _server: &Arc, ground: SSetPlayerGround) { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index ab21ef1b3..62db705bf 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -16,6 +16,7 @@ #[cfg(target_os = "wasi")] compile_error!("Compiling for WASI targets is not supported!"); +use log::LevelFilter; use mio::net::TcpListener; use mio::{Events, Interest, Poll, Token}; @@ -38,6 +39,38 @@ pub mod server; pub mod util; pub mod world; +fn init_logger() { + use pumpkin_config::ADVANCED_CONFIG; + if ADVANCED_CONFIG.logging.enabled { + let mut logger = simple_logger::SimpleLogger::new(); + + if !ADVANCED_CONFIG.logging.timestamp { + logger = logger.without_timestamps(); + } + + if ADVANCED_CONFIG.logging.env { + logger = logger.env(); + } + + logger = logger.with_level(convert_logger_filter(ADVANCED_CONFIG.logging.level)); + + logger = logger.with_colors(ADVANCED_CONFIG.logging.color); + logger = logger.with_threads(ADVANCED_CONFIG.logging.threads); + logger.init().unwrap() + } +} + +fn convert_logger_filter(level: pumpkin_config::logging::LevelFilter) -> LevelFilter { + match level { + pumpkin_config::logging::LevelFilter::Off => LevelFilter::Off, + pumpkin_config::logging::LevelFilter::Error => LevelFilter::Error, + pumpkin_config::logging::LevelFilter::Warn => LevelFilter::Warn, + pumpkin_config::logging::LevelFilter::Info => LevelFilter::Info, + pumpkin_config::logging::LevelFilter::Debug => LevelFilter::Debug, + pumpkin_config::logging::LevelFilter::Trace => LevelFilter::Trace, + } +} + fn main() -> io::Result<()> { use std::sync::Arc; @@ -46,10 +79,7 @@ fn main() -> io::Result<()> { use pumpkin_core::text::{color::NamedColor, TextComponent}; use rcon::RCONServer; - simple_logger::SimpleLogger::new() - .with_level(log::LevelFilter::Info) - .init() - .unwrap(); + init_logger(); let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() diff --git a/pumpkin/src/rcon/mod.rs b/pumpkin/src/rcon/mod.rs index 252210f8c..a508f941a 100644 --- a/pumpkin/src/rcon/mod.rs +++ b/pumpkin/src/rcon/mod.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, io::{self, Read, Write}, + net::SocketAddr, sync::Arc, }; @@ -9,7 +10,7 @@ use mio::{ Events, Interest, Poll, Token, }; use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket}; -use pumpkin_config::RCONConfig; +use pumpkin_config::{RCONConfig, ADVANCED_CONFIG}; use thiserror::Error; use crate::server::Server; @@ -71,11 +72,9 @@ impl RCONServer { return Err(e); } }; - log::info!("Accepted connection from: {}", address); if config.max_connections != 0 && connections.len() >= config.max_connections as usize { - log::warn!("Max RCON connections reached"); break; } @@ -87,7 +86,7 @@ impl RCONServer { Interest::READABLE.add(Interest::WRITABLE), ) .unwrap(); - connections.insert(token, RCONClient::new(connection)); + connections.insert(token, RCONClient::new(connection, address)); }, token => { @@ -98,6 +97,13 @@ impl RCONServer { }; if done { if let Some(mut client) = connections.remove(&token) { + let config = &ADVANCED_CONFIG.rcon; + if config.logging.log_quit { + log::info!( + "RCON ({}): Client closed connection", + client.address + ); + } poll.registry().deregister(&mut client.connection)?; } } @@ -116,15 +122,17 @@ impl RCONServer { pub struct RCONClient { connection: TcpStream, + address: SocketAddr, logged_in: bool, incoming: Vec, closed: bool, } impl RCONClient { - pub const fn new(connection: TcpStream) -> Self { + pub const fn new(connection: TcpStream, address: SocketAddr) -> Self { Self { connection, + address, logged_in: false, incoming: Vec::new(), closed: false, @@ -147,7 +155,7 @@ impl RCONClient { } // If we get a close here, we might have a reply, which we still want to write. let _ = self.poll(server, password).await.map_err(|e| { - log::error!("rcon error: {e}"); + log::error!("RCON error: {e}"); self.closed = true; }); } @@ -161,16 +169,21 @@ impl RCONClient { None => return Ok(()), }; + let config = &ADVANCED_CONFIG.rcon; match packet.get_type() { ServerboundPacket::Auth => { let body = packet.get_body(); if !body.is_empty() && packet.get_body() == password { self.send(ClientboundPacket::AuthResponse, packet.get_id(), "".into()) .await?; - log::info!("RCON Client logged in successfully"); + if config.logging.log_logged_successfully { + log::info!("RCON ({}): Client logged in successfully", self.address); + } self.logged_in = true; } else { - log::warn!("RCON Client has tried wrong password"); + if config.logging.log_wrong_password { + log::info!("RCON ({}): Client has tried wrong password", self.address); + } self.send(ClientboundPacket::AuthResponse, -1, "".into()) .await?; self.closed = true; @@ -186,6 +199,9 @@ impl RCONClient { packet.get_body(), ); for line in output { + if config.logging.log_commands { + log::info!("RCON ({}): {}", self.address, line); + } self.send(ClientboundPacket::Output, packet.get_id(), line) .await?; }