Add clean-logs-older-than option
This commit is contained in:
parent
4c7d108e09
commit
880c98ecfb
17 changed files with 360 additions and 10 deletions
|
|
@ -111,6 +111,32 @@ instead:
|
||||||
~$ jay set-log-level debug
|
~$ jay set-log-level debug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Log File Cleanup
|
||||||
|
|
||||||
|
Jay creates a new log file each time it starts. Over time, old log files can
|
||||||
|
accumulate. To automatically delete old log files on startup, use the
|
||||||
|
`clean-logs-older-than` option:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
clean-logs-older-than.days = 7
|
||||||
|
```
|
||||||
|
|
||||||
|
The table accepts `weeks` and `days` fields. At least one must be specified.
|
||||||
|
They can be combined and accept fractional values:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[clean-logs-older-than]
|
||||||
|
weeks = 2
|
||||||
|
days = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Log files belonging to other running Jay instances (e.g. on another VT) are
|
||||||
|
never deleted, even if they are older than the specified age.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This setting only takes effect at compositor startup. It cannot be triggered
|
||||||
|
> by reloading the configuration.
|
||||||
|
|
||||||
## Focus Follows Mouse
|
## Focus Follows Mouse
|
||||||
|
|
||||||
When enabled, moving the pointer over a window automatically gives it keyboard
|
When enabled, moving the pointer over a window automatically gives it keyboard
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,9 @@ log-level = "debug"
|
||||||
> The `log-level` config setting is read at startup and cannot be changed by
|
> The `log-level` config setting is read at startup and cannot be changed by
|
||||||
> reloading the configuration. Use `jay set-log-level` for runtime changes.
|
> reloading the configuration. Use `jay set-log-level` for runtime changes.
|
||||||
|
|
||||||
|
To automatically clean up old log files, see
|
||||||
|
[Log File Cleanup](configuration/misc.md#log-file-cleanup).
|
||||||
|
|
||||||
## Performance issues
|
## Performance issues
|
||||||
|
|
||||||
If you experience dropped frames, stuttering, or high latency, try the
|
If you experience dropped frames, stuttering, or high latency, try the
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ use {
|
||||||
atomic::{AtomicBool, Ordering::Relaxed},
|
atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
},
|
},
|
||||||
task::{Context, Poll, Waker},
|
task::{Context, Poll, Waker},
|
||||||
time::Duration,
|
time::{Duration, SystemTime},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -776,6 +776,10 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::SetLogLevel { level })
|
self.send(&ClientMessage::SetLogLevel { level })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clean_logs_older_than(&self, time: SystemTime) {
|
||||||
|
self.send(&ClientMessage::CleanLogsOlderThan { time })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unset_env(&self, key: &str) {
|
pub fn unset_env(&self, key: &str) {
|
||||||
self.send(&ClientMessage::UnsetEnv { key });
|
self.send(&ClientMessage::UnsetEnv { key });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use {
|
||||||
xwayland::XScalingMode,
|
xwayland::XScalingMode,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
std::time::Duration,
|
std::time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -861,6 +861,9 @@ pub enum ClientMessage<'a> {
|
||||||
SeatWarpMouseToFocus {
|
SeatWarpMouseToFocus {
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
},
|
},
|
||||||
|
CleanLogsOlderThan {
|
||||||
|
time: SystemTime,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
//! Note that you can use the `log` crate for logging. All invocations of `log::info` etc.
|
//! Note that you can use the `log` crate for logging. All invocations of `log::info` etc.
|
||||||
//! automatically log into the compositors log.
|
//! automatically log into the compositors log.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use {
|
||||||
|
serde::{Deserialize, Serialize},
|
||||||
|
std::time::SystemTime,
|
||||||
|
};
|
||||||
|
|
||||||
/// The log level of the compositor or a log message.
|
/// The log level of the compositor or a log message.
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||||
|
|
@ -19,3 +22,13 @@ pub enum LogLevel {
|
||||||
pub fn set_log_level(level: LogLevel) {
|
pub fn set_log_level(level: LogLevel) {
|
||||||
get!().set_log_level(level);
|
get!().set_log_level(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this function is called during startup, Jay's log files before `time` are deleted.
|
||||||
|
///
|
||||||
|
/// The current log file is never deleted, nor are any other logfiles of active Jay instances (e.g.
|
||||||
|
/// on another VT), even if `time` is in the future.
|
||||||
|
///
|
||||||
|
/// Calling this function after startup has no effect.
|
||||||
|
pub fn clean_logs_older_than(time: SystemTime) {
|
||||||
|
get!().clean_logs_older_than(time);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,7 @@ fn start_compositor2(
|
||||||
egg_state: Default::default(),
|
egg_state: Default::default(),
|
||||||
control_centers: Default::default(),
|
control_centers: Default::default(),
|
||||||
virtual_outputs: Default::default(),
|
virtual_outputs: Default::default(),
|
||||||
|
clean_logs_older_than: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
@ -457,6 +458,7 @@ async fn start_compositor3(state: Rc<State>, test_future: Option<TestFuture>) {
|
||||||
if state.create_default_seat.get() && state.globals.seats.is_empty() {
|
if state.create_default_seat.get() && state.globals.seats.is_empty() {
|
||||||
state.create_seat(DEFAULT_SEAT_NAME);
|
state.create_seat(DEFAULT_SEAT_NAME);
|
||||||
}
|
}
|
||||||
|
state.perform_clean_logs_older_than();
|
||||||
state.update_ei_acceptor();
|
state.update_ei_acceptor();
|
||||||
|
|
||||||
let _geh = start_global_event_handlers(&state);
|
let _geh = start_global_event_handlers(&state);
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ use {
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
time::Duration,
|
time::{Duration, SystemTime},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{OwnedFd, c, fcntl_dupfd_cloexec},
|
uapi::{OwnedFd, c, fcntl_dupfd_cloexec},
|
||||||
|
|
@ -1856,6 +1856,10 @@ impl ConfigProxyHandler {
|
||||||
self.state.set_log_level(level.into());
|
self.state.set_log_level(level.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_clean_logs_older_than(&self, time: SystemTime) {
|
||||||
|
self.state.clean_logs_older_than.set(Some(time));
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_grab(&self, kb: InputDevice, grab: bool) -> Result<(), CphError> {
|
fn handle_grab(&self, kb: InputDevice, grab: bool) -> Result<(), CphError> {
|
||||||
let kb = self.get_kb(kb)?;
|
let kb = self.get_kb(kb)?;
|
||||||
kb.grab(grab);
|
kb.grab(grab);
|
||||||
|
|
@ -3376,6 +3380,7 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::GetConnectorByName { name } => self.handle_get_connector_by_name(name),
|
ClientMessage::GetConnectorByName { name } => self.handle_get_connector_by_name(name),
|
||||||
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
|
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
|
||||||
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
|
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
|
||||||
|
ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
src/logger.rs
105
src/logger.rs
|
|
@ -4,7 +4,7 @@ use {
|
||||||
utils::{atomic_enum::AtomicEnum, errorfmt::ErrorFmt, oserror::OsError},
|
utils::{atomic_enum::AtomicEnum, errorfmt::ErrorFmt, oserror::OsError},
|
||||||
},
|
},
|
||||||
backtrace::Backtrace,
|
backtrace::Backtrace,
|
||||||
bstr::BString,
|
bstr::{BStr, BString, ByteSlice},
|
||||||
log::{LevelFilter, Log, Metadata, Record},
|
log::{LevelFilter, Log, Metadata, Record},
|
||||||
parking_lot::Mutex,
|
parking_lot::Mutex,
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -17,9 +17,11 @@ use {
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicI32, AtomicU32, Ordering::Relaxed},
|
atomic::{AtomicI32, AtomicU32, Ordering::Relaxed},
|
||||||
},
|
},
|
||||||
|
thread,
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
},
|
},
|
||||||
uapi::{Errno, Fd, OwnedFd, Ustring, c, format_ustr},
|
thiserror::Error,
|
||||||
|
uapi::{AsUstr, Dirent, Errno, Fd, OwnedFd, Ustring, c, format_ustr},
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
|
@ -80,6 +82,17 @@ impl Logger {
|
||||||
log::set_max_level(filter);
|
log::set_max_level(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clean_logs_older_than(&self, time: SystemTime) {
|
||||||
|
let time_formatted = humantime::format_rfc3339_millis(time);
|
||||||
|
log::info!("Cleaning unused log files older than {}", time_formatted);
|
||||||
|
let path = self.path();
|
||||||
|
thread::spawn(move || {
|
||||||
|
if let Err(e) = clean_logs_older_than(path.as_bstr(), time) {
|
||||||
|
log::error!("Could not clean log files: {}", ErrorFmt(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn level(&self) -> LogLevel {
|
pub fn level(&self) -> LogLevel {
|
||||||
self.level.load(Relaxed)
|
self.level.load(Relaxed)
|
||||||
}
|
}
|
||||||
|
|
@ -105,6 +118,7 @@ impl Logger {
|
||||||
|
|
||||||
pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) {
|
pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) {
|
||||||
let log_dir = create_log_dir(ty);
|
let log_dir = create_log_dir(ty);
|
||||||
|
let mut flock_fail_count = 0;
|
||||||
for i in 0.. {
|
for i in 0.. {
|
||||||
let file_name = format_ustr!(
|
let file_name = format_ustr!(
|
||||||
"{}/{ty}-{}-{}.txt",
|
"{}/{ty}-{}-{}.txt",
|
||||||
|
|
@ -117,7 +131,22 @@ pub fn open_log_file(ty: &str) -> (Ustring, OwnedFd) {
|
||||||
c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY,
|
c::O_CREAT | c::O_EXCL | c::O_CLOEXEC | c::O_WRONLY,
|
||||||
0o644,
|
0o644,
|
||||||
) {
|
) {
|
||||||
Ok(f) => return (file_name, f),
|
Ok(f) => {
|
||||||
|
if let Err(e) = uapi::flock(f.raw(), c::LOCK_EX | c::LOCK_NB) {
|
||||||
|
log::warn!("Unable to flock just-opened logfile: {}", ErrorFmt(e));
|
||||||
|
flock_fail_count += 1;
|
||||||
|
if flock_fail_count > 10 {
|
||||||
|
log::error!(concat!(
|
||||||
|
"Failed to flock just-opened logfile more than 10 times in a row. ",
|
||||||
|
"Not flocking the logfile, if the cleanup routine later succeeds to ",
|
||||||
|
"flock this logfile, it will be deleted even if it is still in use."
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (file_name, f);
|
||||||
|
}
|
||||||
Err(Errno(c::EEXIST)) => {}
|
Err(Errno(c::EEXIST)) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let e: OsError = e.into();
|
let e: OsError = e.into();
|
||||||
|
|
@ -209,3 +238,73 @@ impl Log for LogWrapper {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum CleanLogsError {
|
||||||
|
#[error("Log path has no parent")]
|
||||||
|
NoParent,
|
||||||
|
#[error("Could not open the log directory")]
|
||||||
|
OpenDir(#[source] OsError),
|
||||||
|
#[error("Could not enumerate directory entry")]
|
||||||
|
ReadDir(#[source] OsError),
|
||||||
|
#[error("Could not open the log file")]
|
||||||
|
OpenFile(#[source] OsError),
|
||||||
|
#[error("Could not stat the log file")]
|
||||||
|
Stat(#[source] OsError),
|
||||||
|
#[error("Could not unlink the log file")]
|
||||||
|
Unlink(#[source] OsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean_logs_older_than(current_log_path: &BStr, time: SystemTime) -> Result<(), CleanLogsError> {
|
||||||
|
let current_log_path = current_log_path.to_path_lossy();
|
||||||
|
let parent = current_log_path.parent().ok_or(CleanLogsError::NoParent)?;
|
||||||
|
let mut dir = uapi::opendir(parent)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(CleanLogsError::OpenDir)?;
|
||||||
|
let parent = uapi::open(parent, c::O_PATH | c::O_CLOEXEC | c::O_DIRECTORY, 0)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(CleanLogsError::OpenDir)?;
|
||||||
|
let time = time
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_secs() as c::time_t;
|
||||||
|
while let Some(entry) = uapi::readdir(&mut dir) {
|
||||||
|
let entry = entry.map_err(Into::into).map_err(CleanLogsError::ReadDir)?;
|
||||||
|
if let Err(err) = process_entry(parent.raw(), &entry, time) {
|
||||||
|
log::error!(
|
||||||
|
"Could not clean log file {}: {}",
|
||||||
|
entry.name().as_ustr().display(),
|
||||||
|
ErrorFmt(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn process_entry(
|
||||||
|
parent: c::c_int,
|
||||||
|
entry: &Dirent,
|
||||||
|
time: c::time_t,
|
||||||
|
) -> Result<(), CleanLogsError> {
|
||||||
|
if entry.d_type != c::DT_REG {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let name = entry.name();
|
||||||
|
let file = uapi::openat(parent, name, c::O_RDONLY | c::O_CLOEXEC, 0)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(CleanLogsError::OpenFile)?;
|
||||||
|
let stat = uapi::fstat(*file)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(CleanLogsError::Stat)?;
|
||||||
|
if stat.st_mtime >= time {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
if uapi::flock(file.raw(), c::LOCK_EX | c::LOCK_NB).is_err() {
|
||||||
|
log::info!("Preserving file still in use: {}", name.as_ustr().display());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
uapi::unlinkat(parent, name, 0)
|
||||||
|
.map_err(Into::into)
|
||||||
|
.map_err(CleanLogsError::Unlink)?;
|
||||||
|
log::info!("Deleted {}", name.as_ustr().display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
11
src/state.rs
11
src/state.rs
|
|
@ -150,7 +150,7 @@ use {
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Duration,
|
time::{Duration, SystemTime},
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{OwnedFd, c},
|
uapi::{OwnedFd, c},
|
||||||
|
|
@ -305,6 +305,7 @@ pub struct State {
|
||||||
pub egg_state: EggState,
|
pub egg_state: EggState,
|
||||||
pub control_centers: ControlCenters,
|
pub control_centers: ControlCenters,
|
||||||
pub virtual_outputs: VirtualOutputs,
|
pub virtual_outputs: VirtualOutputs,
|
||||||
|
pub clean_logs_older_than: Cell<Option<SystemTime>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -1760,6 +1761,14 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn perform_clean_logs_older_than(&self) {
|
||||||
|
if let Some(time) = self.clean_logs_older_than.get()
|
||||||
|
&& let Some(logger) = &self.logger
|
||||||
|
{
|
||||||
|
logger.clean_logs_older_than(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn colors_changed(&self) {
|
fn colors_changed(&self) {
|
||||||
struct V;
|
struct V;
|
||||||
impl NodeVisitorBase for V {
|
impl NodeVisitorBase for V {
|
||||||
|
|
|
||||||
|
|
@ -523,6 +523,7 @@ pub struct Config {
|
||||||
pub keymaps: Vec<ConfigKeymap>,
|
pub keymaps: Vec<ConfigKeymap>,
|
||||||
pub auto_reload: Option<bool>,
|
pub auto_reload: Option<bool>,
|
||||||
pub log_level: Option<LogLevel>,
|
pub log_level: Option<LogLevel>,
|
||||||
|
pub clean_logs_older_than: Option<Duration>,
|
||||||
pub theme: Theme,
|
pub theme: Theme,
|
||||||
pub egui: Egui,
|
pub egui: Egui,
|
||||||
pub gfx_api: Option<GfxApi>,
|
pub gfx_api: Option<GfxApi>,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use {
|
||||||
pub mod action;
|
pub mod action;
|
||||||
mod actions;
|
mod actions;
|
||||||
mod capabilities;
|
mod capabilities;
|
||||||
|
mod clean_logs_older_than;
|
||||||
mod client_match;
|
mod client_match;
|
||||||
mod client_rule;
|
mod client_rule;
|
||||||
mod color;
|
mod color;
|
||||||
|
|
|
||||||
57
toml-config/src/config/parsers/clean_logs_older_than.rs
Normal file
57
toml-config/src/config/parsers/clean_logs_older_than.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
config::{
|
||||||
|
context::Context,
|
||||||
|
extractor::{Extractor, ExtractorError, fltorint, opt},
|
||||||
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
|
},
|
||||||
|
toml::{
|
||||||
|
toml_span::{DespanExt, Span, Spanned, SpannedExt},
|
||||||
|
toml_value::Value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexmap::IndexMap,
|
||||||
|
std::time::{Duration, TryFromFloatSecsError},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum CleanLogsOlderThanParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error(transparent)]
|
||||||
|
Extract(#[from] ExtractorError),
|
||||||
|
#[error("At least one of the `weeks` or `days` fields must be specified")]
|
||||||
|
WeeksOrDays,
|
||||||
|
#[error("Duration is invalid")]
|
||||||
|
InvalidDuration(#[source] TryFromFloatSecsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CleanLogsOlderThanParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
||||||
|
impl Parser for CleanLogsOlderThanParser<'_> {
|
||||||
|
type Value = Duration;
|
||||||
|
type Error = CleanLogsOlderThanParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||||
|
|
||||||
|
fn parse_table(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let mut ext = Extractor::new(self.0, span, table);
|
||||||
|
let (weeks, days) = ext.extract((opt(fltorint("weeks")), opt(fltorint("days"))))?;
|
||||||
|
if weeks.is_none() && days.is_none() {
|
||||||
|
return Err(CleanLogsOlderThanParserError::WeeksOrDays.spanned(span));
|
||||||
|
}
|
||||||
|
const SECONDS_IN_WEEK: f64 = 604800.0;
|
||||||
|
const SECONDS_IN_DAY: f64 = 86400.0;
|
||||||
|
let duration = Duration::try_from_secs_f64(
|
||||||
|
weeks.despan().unwrap_or_default() * SECONDS_IN_WEEK
|
||||||
|
+ days.despan().unwrap_or_default() * SECONDS_IN_DAY,
|
||||||
|
)
|
||||||
|
.map_err(CleanLogsOlderThanParserError::InvalidDuration)
|
||||||
|
.map_err(|e| e.spanned(span))?;
|
||||||
|
Ok(duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ use {
|
||||||
parsers::{
|
parsers::{
|
||||||
action::ActionParser,
|
action::ActionParser,
|
||||||
actions::ActionsParser,
|
actions::ActionsParser,
|
||||||
|
clean_logs_older_than::CleanLogsOlderThanParser,
|
||||||
client_rule::ClientRulesParser,
|
client_rule::ClientRulesParser,
|
||||||
color_management::ColorManagementParser,
|
color_management::ColorManagementParser,
|
||||||
connector::ConnectorsParser,
|
connector::ConnectorsParser,
|
||||||
|
|
@ -152,6 +153,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
show_titles,
|
show_titles,
|
||||||
fallback_output_mode_val,
|
fallback_output_mode_val,
|
||||||
egui_val,
|
egui_val,
|
||||||
|
clean_logs_older_than_val,
|
||||||
),
|
),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
|
|
@ -211,6 +213,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
recover(opt(bol("show-titles"))),
|
recover(opt(bol("show-titles"))),
|
||||||
opt(val("fallback-output-mode")),
|
opt(val("fallback-output-mode")),
|
||||||
opt(val("egui")),
|
opt(val("egui")),
|
||||||
|
opt(val("clean-logs-older-than")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let mut keymap = None;
|
let mut keymap = None;
|
||||||
|
|
@ -307,6 +310,17 @@ impl Parser for ConfigParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut clean_logs_older_than = None;
|
||||||
|
if let Some(value) = clean_logs_older_than_val {
|
||||||
|
match value.parse(&mut CleanLogsOlderThanParser(self.0)) {
|
||||||
|
Ok(v) => {
|
||||||
|
clean_logs_older_than = Some(v);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse clean-logs-older-than: {}", self.0.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut theme = Theme::default();
|
let mut theme = Theme::default();
|
||||||
if let Some(value) = theme_val {
|
if let Some(value) = theme_val {
|
||||||
match value.parse(&mut ThemeParser(self.0)) {
|
match value.parse(&mut ThemeParser(self.0)) {
|
||||||
|
|
@ -567,6 +581,7 @@ impl Parser for ConfigParser<'_> {
|
||||||
keymaps,
|
keymaps,
|
||||||
auto_reload: auto_reload.despan(),
|
auto_reload: auto_reload.despan(),
|
||||||
log_level,
|
log_level,
|
||||||
|
clean_logs_older_than,
|
||||||
theme,
|
theme,
|
||||||
egui,
|
egui,
|
||||||
gfx_api,
|
gfx_api,
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ use {
|
||||||
io::Async,
|
io::Async,
|
||||||
is_reload,
|
is_reload,
|
||||||
keyboard::Keymap,
|
keyboard::Keymap,
|
||||||
logging::set_log_level,
|
logging::{clean_logs_older_than, set_log_level},
|
||||||
on_devices_enumerated, on_idle, on_unload, open_control_center, quit, reload,
|
on_devices_enumerated, on_idle, on_unload, open_control_center, quit, reload,
|
||||||
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
|
||||||
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
set_float_above_fullscreen, set_idle, set_idle_grace_period,
|
||||||
|
|
@ -67,7 +67,7 @@ use {
|
||||||
os::{fd::AsRawFd, unix::ffi::OsStrExt},
|
os::{fd::AsRawFd, unix::ffi::OsStrExt},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::Duration,
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
},
|
},
|
||||||
uapi::{
|
uapi::{
|
||||||
Errno,
|
Errno,
|
||||||
|
|
@ -1494,6 +1494,11 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
|
||||||
if let Some(level) = config.log_level {
|
if let Some(level) = config.log_level {
|
||||||
set_log_level(level);
|
set_log_level(level);
|
||||||
}
|
}
|
||||||
|
if let Some(duration) = config.clean_logs_older_than {
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let in_the_past = now.checked_sub(duration).unwrap_or(UNIX_EPOCH);
|
||||||
|
clean_logs_older_than(in_the_past);
|
||||||
|
}
|
||||||
if let Some(idle) = config.idle {
|
if let Some(idle) = config.idle {
|
||||||
set_idle(Some(idle));
|
set_idle(Some(idle));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -645,6 +645,23 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"CleanLogsOlderThan": {
|
||||||
|
"description": "The definition of how old logfiles need to be for them to be automatically deleted.\n\nOmitted values are set to 0. At least one of `weeks` or `days` must be specified.\n\n- Example:\n\n ```toml\n clean-logs-older-than.weeks = 2\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"weeks": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The number of weeks.",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"days": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The number of days.",
|
||||||
|
"minimum": 0.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
"ClickMethod": {
|
"ClickMethod": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The click method to apply to an input device.\n\nSee the libinput documentation for more details.\n",
|
"description": "The click method to apply to an input device.\n\nSee the libinput documentation for more details.\n",
|
||||||
|
|
@ -964,6 +981,10 @@
|
||||||
"description": "Sets the log level of the compositor.\n\nThis setting cannot be changed by re-loading the configuration. Use\n`jay set-log-level` instead.\n\n- Example:\n\n ```toml\n log-level = \"debug\"\n ```\n",
|
"description": "Sets the log level of the compositor.\n\nThis setting cannot be changed by re-loading the configuration. Use\n`jay set-log-level` instead.\n\n- Example:\n\n ```toml\n log-level = \"debug\"\n ```\n",
|
||||||
"$ref": "#/$defs/LogLevel"
|
"$ref": "#/$defs/LogLevel"
|
||||||
},
|
},
|
||||||
|
"clean-logs-older-than": {
|
||||||
|
"description": "If specified on startup, deletes Jay's log files older than the specified time period.\n\nPossible keys in the table are `days` and `weeks`.\n\n- Example:\n\n ```toml\n clean-logs-older-than.days = 7\n ```\n",
|
||||||
|
"$ref": "#/$defs/CleanLogsOlderThan"
|
||||||
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"description": "Sets the theme of the compositor.\n",
|
"description": "Sets the theme of the compositor.\n",
|
||||||
"$ref": "#/$defs/Theme"
|
"$ref": "#/$defs/Theme"
|
||||||
|
|
|
||||||
|
|
@ -960,6 +960,40 @@ The string should have one of the following values:
|
||||||
The brightness in cd/m^2.
|
The brightness in cd/m^2.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-CleanLogsOlderThan"></a>
|
||||||
|
### `CleanLogsOlderThan`
|
||||||
|
|
||||||
|
The definition of how old logfiles need to be for them to be automatically deleted.
|
||||||
|
|
||||||
|
Omitted values are set to 0. At least one of `weeks` or `days` must be specified.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
clean-logs-older-than.weeks = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Values of this type should be tables.
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `weeks` (optional):
|
||||||
|
|
||||||
|
The number of weeks.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
- `days` (optional):
|
||||||
|
|
||||||
|
The number of days.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be greater than or equal to 0.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-ClickMethod"></a>
|
<a name="types-ClickMethod"></a>
|
||||||
### `ClickMethod`
|
### `ClickMethod`
|
||||||
|
|
||||||
|
|
@ -1836,6 +1870,20 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [LogLevel](#types-LogLevel).
|
The value of this field should be a [LogLevel](#types-LogLevel).
|
||||||
|
|
||||||
|
- `clean-logs-older-than` (optional):
|
||||||
|
|
||||||
|
If specified on startup, deletes Jay's log files older than the specified time period.
|
||||||
|
|
||||||
|
Possible keys in the table are `days` and `weeks`.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
clean-logs-older-than.days = 7
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of this field should be a [CleanLogsOlderThan](#types-CleanLogsOlderThan).
|
||||||
|
|
||||||
- `theme` (optional):
|
- `theme` (optional):
|
||||||
|
|
||||||
Sets the theme of the compositor.
|
Sets the theme of the compositor.
|
||||||
|
|
|
||||||
|
|
@ -2680,6 +2680,19 @@ Config:
|
||||||
```toml
|
```toml
|
||||||
log-level = "debug"
|
log-level = "debug"
|
||||||
```
|
```
|
||||||
|
clean-logs-older-than:
|
||||||
|
ref: CleanLogsOlderThan
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
If specified on startup, deletes Jay's log files older than the specified time period.
|
||||||
|
|
||||||
|
Possible keys in the table are `days` and `weeks`.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
clean-logs-older-than.days = 7
|
||||||
|
```
|
||||||
theme:
|
theme:
|
||||||
ref: Theme
|
ref: Theme
|
||||||
required: false
|
required: false
|
||||||
|
|
@ -3218,6 +3231,31 @@ GracePeriod:
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
|
|
||||||
|
CleanLogsOlderThan:
|
||||||
|
kind: table
|
||||||
|
description: |
|
||||||
|
The definition of how old logfiles need to be for them to be automatically deleted.
|
||||||
|
|
||||||
|
Omitted values are set to 0. At least one of `weeks` or `days` must be specified.
|
||||||
|
|
||||||
|
- Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
clean-logs-older-than.weeks = 2
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
weeks:
|
||||||
|
description: The number of weeks.
|
||||||
|
kind: number
|
||||||
|
minimum: 0
|
||||||
|
required: false
|
||||||
|
days:
|
||||||
|
description: The number of days.
|
||||||
|
kind: number
|
||||||
|
minimum: 0
|
||||||
|
required: false
|
||||||
|
|
||||||
|
|
||||||
RepeatRate:
|
RepeatRate:
|
||||||
kind: table
|
kind: table
|
||||||
description: |
|
description: |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue