diff --git a/CHANGELOG.md b/CHANGELOG.md index a7afe1c87..47700d814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## [14.4.1.3] ### Added - Command `FileLog 0..4` to enable logging to filesystem using up to 16 rotating log files of 100kB (`#define FILE_LOG_SIZE 100`) +- Command `FileLog 10..14` to enable logging to filesystem using up to 16 log files of 100kB (`#define FILE_LOG_SIZE 100`) - I2S Opus stream and file support for opus/aac (#22795) - I2S command I2sLoop (#22807) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 90dd1fa3a..1ccccec18 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -118,6 +118,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ### Added - Command `SetOption163 1` to disable display of Device name in GUI header - Command `FileLog 0..4` to enable logging to filesystem using up to 16 rotating log files of 100kB (`#define FILE_LOG_SIZE 100`) +- Command `FileLog 10..14` to enable logging to filesystem using up to 16 log files of 100kB (`#define FILE_LOG_SIZE 100`) - Command I2sLoop [#22807](https://github.com/arendst/Tasmota/issues/22807) - Support for PCF85063 RTC [#22727](https://github.com/arendst/Tasmota/issues/22727) - Support for Senseair S88 CO2 sensor [#22733](https://github.com/arendst/Tasmota/issues/22733) diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino index 6a6c0e1d7..659bf8b4d 100755 --- a/tasmota/tasmota_support/support.ino +++ b/tasmota/tasmota_support/support.ino @@ -2650,7 +2650,8 @@ void AddLogData(uint32_t loglevel, const char* log_data, const char* log_data_pa uint32_t highest_loglevel = Settings->weblog_level; if (Settings->mqttlog_level > highest_loglevel) { highest_loglevel = Settings->mqttlog_level; } #ifdef USE_UFILESYS - if (Settings->filelog_level > highest_loglevel) { highest_loglevel = Settings->filelog_level; } + uint32_t filelog_level = Settings->filelog_level % 10; + if (filelog_level > highest_loglevel) { highest_loglevel = filelog_level; } #endif // USE_UFILESYS if (TasmotaGlobal.syslog_level > highest_loglevel) { highest_loglevel = TasmotaGlobal.syslog_level; } if (TasmotaGlobal.templog_level > highest_loglevel) { highest_loglevel = TasmotaGlobal.templog_level; } diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 23be5c157..1506611ca 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -2163,8 +2163,22 @@ void CmndLogport(void) #ifdef USE_UFILESYS void CmndFilelog(void) { - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings->filelog_level = XdrvMailbox.payload; + // Filelog 0 - Disable file logging + // Filelog 1..4 - Enable rotating file logging + // Filelog 10 - Remove log files and disable file logging + // Filelog 11..14 - Remove log files and enable file logging until filesystem is full or max rotates + if (XdrvMailbox.payload >= LOG_LEVEL_NONE) { + uint32_t filelog_level = XdrvMailbox.payload % 10; + uint32_t filelog_option = XdrvMailbox.payload / 10; + if (1 == filelog_option) { // Enable file logging until filesystem is full + FileLoggingDelete(); // Remove all log files + if (LOG_LEVEL_NONE == filelog_level) { // Remove log files and disable logging + filelog_option = 0; + } + } + if ((filelog_level >= LOG_LEVEL_NONE) && (filelog_level <= LOG_LEVEL_DEBUG_MORE)) { + Settings->filelog_level = (filelog_option * 10) + filelog_level; + } } ResponseCmndNumber(Settings->filelog_level); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino index 0fbaeb16d..6b482e7c4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino @@ -497,43 +497,80 @@ bool TfsRenameFile(const char *fname1, const char *fname2) { /*********************************************************************************************\ * Log file * - * Enable with command `FileLog 1..4` + * Enable with command `FileLog 1..4` or `FileLog 11..14` * Rotate max 16 x FILE_LOG_SIZE kB log files /log01 -> /log02 ... /log16 -> /log01 ... - * Filesystem needs to be larger than FILE_LOG_SIZE + LOG_BUFFER_SIZE + * Filesystem needs to be larger than 10k (FILE_LOG_FREE) \*********************************************************************************************/ #ifndef FILE_LOG_SIZE #define FILE_LOG_SIZE 100 // Log file size in kBytes (100kB is based on minimal filesystem of 320kB) #endif -#define FILE_LOG_FREE FILE_LOG_SIZE+(LOG_BUFFER_SIZE/1000) // Minimum free filesystem space in kBytes +#define FILE_LOG_FREE 10 // Minimum free filesystem space in kBytes + #define FILE_LOG_COUNT 16 // Number of log files (max 16 as four bits are reserved for index) #define FILE_LOG_NAME "/log%02d" // Log file name void FileLoggingAsync(bool refresh) { static uint32_t index = 1; // Rotating log buffer entry pointer - if (!ffs_type || !Settings->filelog_level) { return; } // No filesystem or [FileLog] disabled - if (refresh && !NeedLogRefresh(Settings->filelog_level, index)) { return; } // No log buffer changes - if (UfsSize() < FILE_LOG_FREE) { - Settings->filelog_level = 0; // [FileLog] disable - AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging diabled. Filesystem too small")); // Or FILE_LOG_SIZE too large - return; - } + uint32_t filelog_level = Settings->filelog_level % 10; + if (!ffs_type || !filelog_level) { return; } // No filesystem or [FileLog] disabled + if (refresh && !NeedLogRefresh(filelog_level, index)) { return; } // No log buffer changes + uint32_t filelog_option = Settings->filelog_level / 10; char fname[14]; - snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), Settings->mbflag2.log_file_idx +1); // /log01 - File file = ffsp->open(fname, "a"); // Append to existing log file - if (!file) { - if (UfsFree() < FILE_LOG_FREE) { + File file; + uint32_t log_file_idx = Settings->mbflag2.log_file_idx; // 0..15 + for (uint32_t retry = 0; retry <= 1; retry++) { + snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), log_file_idx +1); // /log01 + file = ffsp->open(fname, "a"); // Append to existing log file + if (!file) { + file = ffsp->open(fname, "w"); // Make new log file + if (!file) { + Settings->filelog_level = 0; // [FileLog] disable + AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging disabled. Save failed")); + return; // Failed to make file + } + } + + bool fs_full = (UfsFree() < FILE_LOG_FREE); + if (!fs_full && (file.size() < (FILE_LOG_SIZE * 1000))) { break; } + + file.close(); + + if (1 == retry) { Settings->filelog_level = 0; // [FileLog] disable - AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging disabled. Filesystem full")); + AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging disabled. No free space")); return; } - file = ffsp->open(fname, "w"); // Make new log file - if (!file) { + + // Rotate log file(s) as size is over FILE_LOG_SIZE or free space is less than FILE_LOG_FREE + uint32_t last_log_file_idx = log_file_idx; // 0..15 + log_file_idx++; + + if ((1 == filelog_option) && + (fs_full || (log_file_idx == FILE_LOG_COUNT))) { // Rotate until free space is less than FILE_LOG_FREE Settings->filelog_level = 0; // [FileLog] disable - AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging disabled. Save failed")); - return; // Failed to make file + AddLog(LOG_LEVEL_INFO, PSTR("FLG: Logging disabled. Max rotates")); + return; + } + + if (log_file_idx >= FILE_LOG_COUNT) { log_file_idx = 0; } // Rotate max 16 log files + snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), log_file_idx +1); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("FLG: Rotate file %s"), fname +1); // Skip leading slash + Settings->mbflag2.log_file_idx = log_file_idx; // Save for restart or power on + + if (0 == filelog_option) { // Remove oldest log file(s) + // Remove log file(s) taking into account non-sequential file names and different file sizes + uint32_t idx = log_file_idx; // Next log file index + do { // Need free space around FILE_LOG_SIZE so find oldest log file(s) and remove it + snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), idx +1); + if (ffsp->remove(fname)) { // Remove oldest (non-)sequential log file(s) + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("FLG: Delete file %s"), fname +1); // Skip leading slash + } + idx++; + if (idx >= FILE_LOG_COUNT) { idx = 0; } + } while ((UfsFree() < FILE_LOG_FREE) && (idx != last_log_file_idx)); } } @@ -542,7 +579,7 @@ void FileLoggingAsync(bool refresh) { #endif char* line; size_t len; - while (GetLog(Settings->filelog_level, &index, &line, &len)) { + while (GetLog(filelog_level, &index, &line, &len)) { // This will timeout on ESP32-webcam // But now solved with WcInterrupt(0) in support_esp.ino file.write((uint8_t*)line, len -1); // Write up to LOG_BUFFER_SIZE log data @@ -553,25 +590,19 @@ void FileLoggingAsync(bool refresh) { WcInterrupt(1); #endif - uint32_t file_size = file.size(); file.close(); - if (file_size > (FILE_LOG_SIZE * 1000)) { - // Rotate log file(s) as size is over FILE_LOG_SIZE - // Taking into account non-sequential file names and different file sizes - uint32_t log_file_idx = Settings->mbflag2.log_file_idx; // 0..15 - log_file_idx++; - if (log_file_idx >= FILE_LOG_COUNT) { log_file_idx = 0; } // Rotate max 16 log files - uint32_t idx = log_file_idx; - do { // Need free space around FILE_LOG_SIZE so find oldest log file(s) and remove it - snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), idx +1); - ffsp->remove(fname); // Remove oldest (non-)sequential log file(s) - idx++; - if (idx >= FILE_LOG_COUNT) { idx = 0; } - } while ((UfsFree() < FILE_LOG_FREE) && (idx != Settings->mbflag2.log_file_idx)); - Settings->mbflag2.log_file_idx = log_file_idx; // Save for restart or power on - snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), log_file_idx +1); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("FLG: Rotate file %s"), fname +1); // Skip leading slash +} + +void FileLoggingDelete(void) { + if (!ffs_type) { return; } // No filesystem + + char fname[14]; + for (uint32_t idx = 0; idx < FILE_LOG_COUNT; idx++) { + snprintf_P(fname, sizeof(fname), PSTR(FILE_LOG_NAME), idx +1); + ffsp->remove(fname); // Remove all log file(s) } + Settings->mbflag2.log_file_idx = 0; + AddLog(LOG_LEVEL_INFO, PSTR("FLG: Log files deleted")); } /*********************************************************************************************\ @@ -1819,7 +1850,6 @@ void Switch_FTP(void) { * Interface \*********************************************************************************************/ - bool Xdrv50(uint32_t function) { bool result = false;