Make Logging configurable

This commit is contained in:
Snowiiii 2024-10-11 16:41:52 +02:00
parent 9ac4bdbd50
commit bcbde20623
8 changed files with 141 additions and 15 deletions

View File

@ -4,11 +4,15 @@ use serde::{Deserialize, Serialize};
pub struct CommandsConfig { pub struct CommandsConfig {
/// Are commands from the Console accepted ? /// Are commands from the Console accepted ?
pub use_console: bool, 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 { impl Default for CommandsConfig {
fn default() -> Self { fn default() -> Self {
Self { use_console: true } Self {
use_console: true,
log_console: true,
}
} }
} }

View File

@ -1,4 +1,5 @@
use log::warn; use log::warn;
use logging::LoggingConfig;
use pumpkin_core::{Difficulty, GameMode}; use pumpkin_core::{Difficulty, GameMode};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
@ -10,6 +11,7 @@ use std::{
}; };
pub mod auth; pub mod auth;
pub mod logging;
pub mod proxy; pub mod proxy;
pub mod resource_pack; pub mod resource_pack;
@ -46,6 +48,7 @@ pub struct AdvancedConfiguration {
pub commands: CommandsConfig, pub commands: CommandsConfig,
pub rcon: RCONConfig, pub rcon: RCONConfig,
pub pvp: PVPConfig, pub pvp: PVPConfig,
pub logging: LoggingConfig,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]

View File

@ -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,
}

View File

@ -13,6 +13,31 @@ pub struct RCONConfig {
/// The maximum number of concurrent RCON connections allowed. /// The maximum number of concurrent RCON connections allowed.
/// If 0 there is no limit /// If 0 there is no limit
pub max_connections: u32, 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 { impl Default for RCONConfig {
@ -22,6 +47,7 @@ impl Default for RCONConfig {
address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575), address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575),
password: "".to_string(), password: "".to_string(),
max_connections: 0, max_connections: 0,
logging: Default::default(),
} }
} }
} }

View File

@ -61,7 +61,7 @@ base64 = "0.22.1"
png = "0.17.14" png = "0.17.14"
# logging # logging
simple_logger = "5.0.0" simple_logger = { version = "5.0.0", features = ["threads"] }
log.workspace = true log.workspace = true
# networking # networking

View File

@ -227,6 +227,13 @@ impl Player {
pub fn handle_chat_command(&self, server: &Arc<Server>, command: SChatCommand) { pub fn handle_chat_command(&self, server: &Arc<Server>, command: SChatCommand) {
let dispatcher = server.command_dispatcher.clone(); let dispatcher = server.command_dispatcher.clone();
dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command); 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<Server>, ground: SSetPlayerGround) { pub fn handle_player_ground(&self, _server: &Arc<Server>, ground: SSetPlayerGround) {

View File

@ -16,6 +16,7 @@
#[cfg(target_os = "wasi")] #[cfg(target_os = "wasi")]
compile_error!("Compiling for WASI targets is not supported!"); compile_error!("Compiling for WASI targets is not supported!");
use log::LevelFilter;
use mio::net::TcpListener; use mio::net::TcpListener;
use mio::{Events, Interest, Poll, Token}; use mio::{Events, Interest, Poll, Token};
@ -38,6 +39,38 @@ pub mod server;
pub mod util; pub mod util;
pub mod world; 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<()> { fn main() -> io::Result<()> {
use std::sync::Arc; use std::sync::Arc;
@ -46,10 +79,7 @@ fn main() -> io::Result<()> {
use pumpkin_core::text::{color::NamedColor, TextComponent}; use pumpkin_core::text::{color::NamedColor, TextComponent};
use rcon::RCONServer; use rcon::RCONServer;
simple_logger::SimpleLogger::new() init_logger();
.with_level(log::LevelFilter::Info)
.init()
.unwrap();
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() .enable_all()

View File

@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{self, Read, Write}, io::{self, Read, Write},
net::SocketAddr,
sync::Arc, sync::Arc,
}; };
@ -9,7 +10,7 @@ use mio::{
Events, Interest, Poll, Token, Events, Interest, Poll, Token,
}; };
use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket}; use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket};
use pumpkin_config::RCONConfig; use pumpkin_config::{RCONConfig, ADVANCED_CONFIG};
use thiserror::Error; use thiserror::Error;
use crate::server::Server; use crate::server::Server;
@ -71,11 +72,9 @@ impl RCONServer {
return Err(e); return Err(e);
} }
}; };
log::info!("Accepted connection from: {}", address);
if config.max_connections != 0 if config.max_connections != 0
&& connections.len() >= config.max_connections as usize && connections.len() >= config.max_connections as usize
{ {
log::warn!("Max RCON connections reached");
break; break;
} }
@ -87,7 +86,7 @@ impl RCONServer {
Interest::READABLE.add(Interest::WRITABLE), Interest::READABLE.add(Interest::WRITABLE),
) )
.unwrap(); .unwrap();
connections.insert(token, RCONClient::new(connection)); connections.insert(token, RCONClient::new(connection, address));
}, },
token => { token => {
@ -98,6 +97,13 @@ impl RCONServer {
}; };
if done { if done {
if let Some(mut client) = connections.remove(&token) { 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)?; poll.registry().deregister(&mut client.connection)?;
} }
} }
@ -116,15 +122,17 @@ impl RCONServer {
pub struct RCONClient { pub struct RCONClient {
connection: TcpStream, connection: TcpStream,
address: SocketAddr,
logged_in: bool, logged_in: bool,
incoming: Vec<u8>, incoming: Vec<u8>,
closed: bool, closed: bool,
} }
impl RCONClient { impl RCONClient {
pub const fn new(connection: TcpStream) -> Self { pub const fn new(connection: TcpStream, address: SocketAddr) -> Self {
Self { Self {
connection, connection,
address,
logged_in: false, logged_in: false,
incoming: Vec::new(), incoming: Vec::new(),
closed: false, 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. // 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| { let _ = self.poll(server, password).await.map_err(|e| {
log::error!("rcon error: {e}"); log::error!("RCON error: {e}");
self.closed = true; self.closed = true;
}); });
} }
@ -161,16 +169,21 @@ impl RCONClient {
None => return Ok(()), None => return Ok(()),
}; };
let config = &ADVANCED_CONFIG.rcon;
match packet.get_type() { match packet.get_type() {
ServerboundPacket::Auth => { ServerboundPacket::Auth => {
let body = packet.get_body(); let body = packet.get_body();
if !body.is_empty() && packet.get_body() == password { if !body.is_empty() && packet.get_body() == password {
self.send(ClientboundPacket::AuthResponse, packet.get_id(), "".into()) self.send(ClientboundPacket::AuthResponse, packet.get_id(), "".into())
.await?; .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; self.logged_in = true;
} else { } 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()) self.send(ClientboundPacket::AuthResponse, -1, "".into())
.await?; .await?;
self.closed = true; self.closed = true;
@ -186,6 +199,9 @@ impl RCONClient {
packet.get_body(), packet.get_body(),
); );
for line in output { for line in output {
if config.logging.log_commands {
log::info!("RCON ({}): {}", self.address, line);
}
self.send(ClientboundPacket::Output, packet.get_id(), line) self.send(ClientboundPacket::Output, packet.get_id(), line)
.await?; .await?;
} }