mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-12 06:33:34 +08:00
feat(logger): add SDLOG_ROTATE, disentangle cleanup thresholds
The previous cleanup logic conflated two independent concerns into
SDLOG_MAX_SIZE: the maximum size of a single log file (rotation
trigger) AND the minimum free space to maintain. That was broken:
the 4095 MB default meant "keep 4 GB free", which over-cleaned on
large SD cards and was impossible to satisfy on small flash.
Disentangle the two:
- SDLOG_ROTATE (new, int %, default 90): maximum disk usage percentage.
Cleanup guarantees at least (100 - SDLOG_ROTATE)% free even during
writing of a new log file. Setting 0 disables space-based cleanup;
100 allows filling the disk completely.
- SDLOG_MAX_SIZE (default lowered from 4095 to 1024): pure max file
size. The value is added on top of the rotate-derived threshold as
headroom for the next file write, so the rotate guarantee holds
even mid-write.
- SDLOG_DIRS_MAX: removed. Directory count limits were confusing and
orthogonal to the space-management goal; free-space cleanup alone
covers the use case. Drop the param and all remaining overrides
(rc.board_defaults, rcS, rc.replay, 1002_standard_vtol.hil).
Cleanup threshold is now:
((100 - SDLOG_ROTATE)% of disk) + SDLOG_MAX_SIZE
Small-flash boards can override SDLOG_MAX_SIZE to get more retained
logs within the available space. kakuteh7v2 and airbrainh743 drop
their SDLOG_DIRS_MAX overrides accordingly.
Update docs/en/dev_log/logging.md with the new semantics and a
worked example for the typical 8 GB SD case.
This commit is contained in:
@@ -20,7 +20,6 @@ module: replay
|
||||
ignore_others: false
|
||||
EOF
|
||||
|
||||
param set SDLOG_DIRS_MAX 7
|
||||
param set SDLOG_PROFILE 3
|
||||
|
||||
# apply all params before ekf starts, as some params cannot be changed after startup
|
||||
|
||||
@@ -172,7 +172,6 @@ param set-default -s MC_AT_EN 1
|
||||
param set-default SDLOG_MODE 1
|
||||
# enable default, estimator replay and vision/avoidance logging profiles
|
||||
param set-default SDLOG_PROFILE 131
|
||||
param set-default SDLOG_DIRS_MAX 7
|
||||
|
||||
param set-default TRIG_INTERFACE 3
|
||||
|
||||
|
||||
@@ -56,8 +56,6 @@ param set-default NAV_DLL_ACT 2
|
||||
param set-default RTL_DESCEND_ALT 10
|
||||
param set-default RTL_RETURN_ALT 30
|
||||
|
||||
param set-default SDLOG_DIRS_MAX 7
|
||||
|
||||
param set-default VT_F_TRANS_THR 0.75
|
||||
param set-default VT_TYPE 2
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
|
||||
# W25N NAND flash with littlefs (128 MB): larger buffer, auto-rotate
|
||||
set LOGGER_BUF 32
|
||||
param set-default SDLOG_DIRS_MAX 3
|
||||
# W25N NAND flash with littlefs (128 MB): small log file size so we can keep
|
||||
# a few recent logs. Default SDLOG_ROTATE=90 keeps at least 10% free during
|
||||
# writing (no bad block management yet, so avoid hammering the flash near full).
|
||||
param set-default SDLOG_MAX_SIZE 40
|
||||
|
||||
# Store missions in RAM
|
||||
param set-default SYS_DM_BACKEND 1
|
||||
|
||||
@@ -38,7 +38,7 @@ param set-default SYS_DM_BACKEND 1
|
||||
# Ignore that there is no SD card
|
||||
param set-default COM_ARM_SDCARD 0
|
||||
|
||||
# W25N NAND flash with littlefs (128 MB): larger buffer, auto-rotate
|
||||
set LOGGER_BUF 32
|
||||
param set-default SDLOG_DIRS_MAX 3
|
||||
# W25N NAND flash with littlefs (128 MB): small log file size so we can keep
|
||||
# a few recent logs. Default SDLOG_ROTATE=90 keeps at least 10% free during
|
||||
# writing (no bad block management yet, so avoid hammering the flash near full).
|
||||
param set-default SDLOG_MAX_SIZE 30
|
||||
|
||||
@@ -86,10 +86,19 @@ There are several scripts to analyze and convert logging files in the [pyulog](h
|
||||
## Log Cleanup
|
||||
|
||||
PX4 automatically manages log storage by cleaning up old logs when starting to log.
|
||||
Cleanup is triggered based on two criteria:
|
||||
Two parameters control how much space logs may use:
|
||||
|
||||
- **Storage-based cleanup**: Ensures minimum free space (300 MB or 10% of disk, whichever is smaller) is available.
|
||||
- **Count-based cleanup**: If [SDLOG_DIRS_MAX](../advanced_config/parameter_reference.md#SDLOG_DIRS_MAX) is set, limits the total number of log directories.
|
||||
- [SDLOG_ROTATE](../advanced_config/parameter_reference.md#SDLOG_ROTATE) is the maximum disk usage percentage (default 90).
|
||||
Cleanup ensures at least `(100 - SDLOG_ROTATE)%` of the disk stays free at all times, **even while writing a new log file**.
|
||||
Setting it to `0` disables space-based cleanup entirely; setting it to `100` lets logs fill the disk completely.
|
||||
- [SDLOG_MAX_SIZE](../advanced_config/parameter_reference.md#SDLOG_MAX_SIZE) is the maximum size of a single log file in MB
|
||||
(default 1024). It also reserves headroom so that a full new file always fits after cleanup.
|
||||
|
||||
At log start, the cleanup threshold is `((100 - SDLOG_ROTATE)% of disk) + SDLOG_MAX_SIZE`.
|
||||
Oldest logs are deleted until the free space meets this threshold.
|
||||
For example, on an 8 GB card with defaults, cleanup keeps at least `820 + 1024 = ~1.8 GB` free at log start,
|
||||
so ~6 GB is usable for logs and disk usage never exceeds 90% during writing.
|
||||
Small flash targets override `SDLOG_MAX_SIZE` to a smaller value to keep more logs within the available space.
|
||||
|
||||
The cleanup algorithm prioritizes deleting logs from the directory naming scheme not currently in use.
|
||||
PX4 uses two directory naming schemes:
|
||||
|
||||
@@ -1449,11 +1449,14 @@ void Logger::start_log_file(LogType type)
|
||||
_max_log_file_size = 0; // unlimited
|
||||
}
|
||||
|
||||
// Cleanup old logs if needed (storage-based and/or count-based)
|
||||
// Cleanup old logs if needed.
|
||||
// SDLOG_ROTATE is the max disk-usage percentage; cleanup ensures at least
|
||||
// (100 - rotate)% is free even during writing. SDLOG_MAX_SIZE is passed so
|
||||
// there's always room for the next log file on top of the free-space target.
|
||||
hrt_abstime cleanup_start = hrt_absolute_time();
|
||||
|
||||
if (util::cleanup_old_logs(LOG_ROOT[(int)LogType::Full], _mavlink_log_pub,
|
||||
(uint32_t)max_size_mb, _param_sdlog_dirs_max.get()) == 1) {
|
||||
(uint32_t)_param_sdlog_rotate.get(), (uint32_t)max_size_mb) == 1) {
|
||||
return; // Not enough space even after cleanup
|
||||
}
|
||||
|
||||
|
||||
@@ -401,8 +401,8 @@ private:
|
||||
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamInt<px4::params::SDLOG_UTC_OFFSET>) _param_sdlog_utc_offset,
|
||||
(ParamInt<px4::params::SDLOG_DIRS_MAX>) _param_sdlog_dirs_max,
|
||||
(ParamInt<px4::params::SDLOG_MAX_SIZE>) _param_sdlog_max_size,
|
||||
(ParamInt<px4::params::SDLOG_ROTATE>) _param_sdlog_rotate,
|
||||
(ParamInt<px4::params::SDLOG_PROFILE>) _param_sdlog_profile,
|
||||
(ParamInt<px4::params::SDLOG_MISSION>) _param_sdlog_mission,
|
||||
(ParamBool<px4::params::SDLOG_BOOT_BAT>) _param_sdlog_boot_bat,
|
||||
|
||||
@@ -102,34 +102,36 @@ parameters:
|
||||
min: 0
|
||||
max: 4095
|
||||
reboot_required: true
|
||||
SDLOG_DIRS_MAX:
|
||||
description:
|
||||
short: Maximum number of log directories to keep
|
||||
long: 'If there are more log directories than this value, the system will
|
||||
delete the oldest directories when starting to log. Cleanup prioritizes
|
||||
directories from the naming scheme not currently in use (e.g., sess dirs
|
||||
are deleted first when date dirs are being used). If set to 0, directories
|
||||
are only removed based on storage space. The minimum free space threshold
|
||||
is 300 MB or 10% of disk size, whichever is smaller. Note: this does not
|
||||
apply to mission log files.'
|
||||
type: int32
|
||||
default: 0
|
||||
min: 0
|
||||
max: 1000
|
||||
reboot_required: false
|
||||
SDLOG_MAX_SIZE:
|
||||
description:
|
||||
short: Maximum log file size
|
||||
long: 'Maximum size of a single log file in megabytes. When reached,
|
||||
the log file is closed and a new one is started.
|
||||
Cleanup of old logs always happens at log start (not boot) to allow
|
||||
downloading logs via FTP before deletion.
|
||||
the log file is closed and a new one is started. This value is also
|
||||
added to the cleanup threshold (see SDLOG_ROTATE) to reserve headroom
|
||||
for the next log file. A value of 0 disables both file rotation and
|
||||
the cleanup reservation.
|
||||
Must stay below the FAT32 file size limit of 4 GiB.'
|
||||
type: int32
|
||||
default: 4095
|
||||
default: 1024
|
||||
min: 0
|
||||
max: 4095
|
||||
reboot_required: false
|
||||
SDLOG_ROTATE:
|
||||
description:
|
||||
short: Maximum disk usage percentage
|
||||
long: 'Maximum percentage of disk space that logs may occupy during operation,
|
||||
including while writing a new log file. For example, a value of 90 means
|
||||
at least 10% of disk is always kept free, even while writing. A value of
|
||||
100 lets logs fill the disk completely. A value of 0 disables space-based
|
||||
cleanup entirely. At log start, oldest logs are deleted as needed to
|
||||
maintain this guarantee, accounting for the next file write of up to
|
||||
SDLOG_MAX_SIZE. Cleanup always happens at log start (not boot) so logs
|
||||
can be downloaded via FTP before deletion.'
|
||||
type: int32
|
||||
default: 90
|
||||
min: 0
|
||||
max: 100
|
||||
reboot_required: false
|
||||
SDLOG_UUID:
|
||||
description:
|
||||
short: Log UUID
|
||||
|
||||
+18
-34
@@ -138,7 +138,7 @@ bool scan_log_directories(const char *log_root_dir, LogDirInfo &info)
|
||||
}
|
||||
|
||||
int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
uint32_t target_free_mb, int32_t max_log_dirs_to_keep)
|
||||
uint32_t rotate_pct, uint32_t max_file_size_mb)
|
||||
{
|
||||
uint64_t avail_bytes = 0;
|
||||
uint64_t total_bytes = 0;
|
||||
@@ -147,25 +147,21 @@ int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
// Calculate cleanup threshold
|
||||
uint64_t cleanup_threshold;
|
||||
// Calculate cleanup threshold. rotate_pct is the maximum allowed disk usage;
|
||||
// we guarantee the disk never exceeds rotate_pct% full even during writing of
|
||||
// a new log file. So at cleanup time, free space must be at least
|
||||
// ((100 - rotate_pct)% of disk) + max log file size, where the latter term
|
||||
// reserves headroom for the next file write.
|
||||
// rotate_pct == 0 disables space-based cleanup entirely.
|
||||
uint64_t cleanup_threshold = 0;
|
||||
|
||||
if (target_free_mb > 0) {
|
||||
cleanup_threshold = (uint64_t)target_free_mb * 1024ULL * 1024ULL;
|
||||
|
||||
} else {
|
||||
// Default: 300 MiB or 10% of disk, whichever is smaller
|
||||
cleanup_threshold = 300ULL * 1024ULL * 1024ULL;
|
||||
|
||||
if (total_bytes / 10 < cleanup_threshold) {
|
||||
cleanup_threshold = total_bytes / 10;
|
||||
}
|
||||
if (rotate_pct > 0 && rotate_pct <= 100) {
|
||||
cleanup_threshold = (total_bytes * (100 - rotate_pct)) / 100;
|
||||
cleanup_threshold += (uint64_t)max_file_size_mb * 1024ULL * 1024ULL;
|
||||
}
|
||||
|
||||
// Early out if we have enough space and no directory limit
|
||||
bool need_space_cleanup = avail_bytes < cleanup_threshold;
|
||||
|
||||
if (!need_space_cleanup && max_log_dirs_to_keep <= 0) {
|
||||
// Early out if we have enough space
|
||||
if (avail_bytes >= cleanup_threshold) {
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
@@ -176,26 +172,18 @@ int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
return PX4_OK; // ignore if we cannot access the log directory
|
||||
}
|
||||
|
||||
int total_dirs = info.num_sess + info.num_dates;
|
||||
bool need_count_cleanup = (max_log_dirs_to_keep > 0) && (total_dirs > max_log_dirs_to_keep);
|
||||
|
||||
if (!need_space_cleanup && !need_count_cleanup) {
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
PX4_INFO("Log cleanup: %u MiB free, threshold %u MiB, %d dirs (max %" PRId32 ")",
|
||||
(unsigned)(avail_bytes / 1024U / 1024U), (unsigned)(cleanup_threshold / 1024U / 1024U),
|
||||
total_dirs, max_log_dirs_to_keep > 0 ? max_log_dirs_to_keep : -1);
|
||||
PX4_INFO("Log cleanup: %u MiB free, threshold %u MiB",
|
||||
(unsigned)(avail_bytes / 1024U / 1024U), (unsigned)(cleanup_threshold / 1024U / 1024U));
|
||||
|
||||
// Determine if we currently have valid time (using date dirs) or not (using sess dirs)
|
||||
// Delete from the "other" scheme first to avoid deleting current log
|
||||
uint64_t utc_time_usec;
|
||||
bool have_time = get_log_time(utc_time_usec, 0, false);
|
||||
|
||||
// Cleanup oldest .ulg files one by one until conditions are met
|
||||
// Cleanup oldest .ulg files one by one until we have enough free space
|
||||
int empty_dir_failures = 0;
|
||||
|
||||
while (need_space_cleanup || need_count_cleanup) {
|
||||
while (avail_bytes < cleanup_threshold) {
|
||||
char oldest_file[LOG_DIR_LEN] = "";
|
||||
char oldest_dir[LOG_DIR_LEN];
|
||||
|
||||
@@ -203,7 +191,6 @@ int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
break;
|
||||
}
|
||||
|
||||
total_dirs = info.num_sess + info.num_dates;
|
||||
bool found_sess = info.num_sess > 0;
|
||||
bool found_date = info.num_dates > 0;
|
||||
|
||||
@@ -299,13 +286,10 @@ int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-check conditions
|
||||
// Re-check free space
|
||||
if (!get_free_space(log_root_dir, &avail_bytes, nullptr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
need_space_cleanup = avail_bytes < cleanup_threshold;
|
||||
need_count_cleanup = (max_log_dirs_to_keep > 0) && (total_dirs > max_log_dirs_to_keep);
|
||||
}
|
||||
|
||||
// Final check: if still not enough space, refuse to log
|
||||
|
||||
@@ -87,14 +87,23 @@ bool scan_log_directories(const char *log_root_dir, LogDirInfo &info);
|
||||
* Cleanup old logs to ensure sufficient free space. Deletes oldest files,
|
||||
* preferring the opposite directory type first (sess dirs when time is known,
|
||||
* date dirs when it is not), then falls back to its own type.
|
||||
*
|
||||
* The cleanup threshold is computed as:
|
||||
* ((100 - rotate_pct) / 100) * disk_size + max_file_size_mb
|
||||
*
|
||||
* i.e. after cleanup there is enough free space to write one more full log
|
||||
* file while still maintaining the rotate_pct usage guarantee.
|
||||
*
|
||||
* @param log_root_dir log root directory
|
||||
* @param mavlink_log_pub mavlink log publisher
|
||||
* @param target_free_mb target free space in MB (0 = use default minimum)
|
||||
* @param max_log_dirs_to_keep maximum log directories to keep (0 = unlimited)
|
||||
* @param rotate_pct maximum disk usage percentage (0 disables space-based
|
||||
* cleanup; 90 keeps at least 10% free during writing)
|
||||
* @param max_file_size_mb maximum log file size in MB; reserved as headroom
|
||||
* for the next log file write
|
||||
* @return 0 on success, 1 if not enough space even after cleanup
|
||||
*/
|
||||
int cleanup_old_logs(const char *log_root_dir, orb_advert_t &mavlink_log_pub,
|
||||
uint32_t target_free_mb, int32_t max_log_dirs_to_keep);
|
||||
uint32_t rotate_pct, uint32_t max_file_size_mb);
|
||||
|
||||
/**
|
||||
* Get UTC time in microseconds from CLOCK_REALTIME
|
||||
|
||||
Reference in New Issue
Block a user