Add file logging

This commit is contained in:
Theo Arends 2025-01-10 12:15:24 +01:00
parent ed21cfd487
commit 3365fd0d0b
7 changed files with 106 additions and 11 deletions

View File

@ -332,6 +332,7 @@
#define D_CMND_OTAURL "OtaUrl"
#define D_CMND_SERIALLOG "SerialLog"
#define D_CMND_SYSLOG "SysLog"
#define D_CMND_FILELOG "FileLog"
#define D_CMND_LOGHOST "LogHost"
#define D_CMND_LOGPORT "LogPort"
#define D_CMND_IPADDRESS "IPAddress"

View File

@ -297,6 +297,7 @@ const char WIFI_HOSTNAME[] = WIFI_DEFAULT_HOSTNAME; // Override by user_confi
#define TASM_FILE_ZIGBEE_DATA "/zbdata" // Zigbee last known values of devices
#define TASM_FILE_AUTOEXEC "/autoexec.bat" // Commands executed after restart
#define TASM_FILE_CONFIG "/config.sys" // Settings executed after restart
#define TASM_FILE_LOG "/log%d" // Log file
#ifndef DNS_TIMEOUT
#define DNS_TIMEOUT 1000 // Milliseconds

View File

@ -241,10 +241,7 @@ typedef union {
typedef union {
uint32_t data; // Allow bit manipulation
struct {
uint32_t spare00 : 1; // bit 0
uint32_t spare01 : 1; // bit 1
uint32_t spare02 : 1; // bit 2
uint32_t spare03 : 1; // bit 3
uint32_t log_file_idx : 4; // bit 0.3 (v14.4.1.2) - FileLog log rotate index
uint32_t spare04 : 1; // bit 4
uint32_t spare05 : 1; // bit 5
uint32_t spare06 : 1; // bit 6
@ -696,9 +693,7 @@ typedef struct {
uint16_t influxdb_period; // 538 520
uint16_t rf_duplicate_time; // 53A 522
uint8_t global_sensor_index[3]; // 53C 4C5
uint8_t free_53F[1]; // 53F
uint8_t filelog_level; // 53F
uint16_t tcp_baudrate; // 540
uint16_t button_debounce; // 542
uint32_t ipv4_address[5]; // 544

View File

@ -2649,6 +2649,9 @@ 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; }
#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; }
if (TasmotaGlobal.uptime < 3) { highest_loglevel = LOG_LEVEL_DEBUG_MORE; } // Log all before setup correct log level
@ -2697,6 +2700,9 @@ uint32_t HighestLogLevel() {
uint32_t highest_loglevel = TasmotaGlobal.seriallog_level;
if (Settings->weblog_level > highest_loglevel) { 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; }
#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; }
if (TasmotaGlobal.uptime < 3) { highest_loglevel = LOG_LEVEL_DEBUG_MORE; } // Log all before setup correct log level

View File

@ -29,6 +29,9 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|"
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOREAD "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|"
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|"
#ifdef USE_UFILESYS
D_CMND_FILELOG "|"
#endif // USE_UFILESYS
D_CMND_SERIALBUFFER "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" D_CMND_SERIALDELIMITER "|"
D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|"
D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
@ -69,6 +72,9 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
&CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution,
&CmndModule, &CmndModules, &CmndGpio, &CmndGpioRead, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport,
#ifdef USE_UFILESYS
&CmndFilelog,
#endif // USE_UFILESYS
&CmndSerialBuffer, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, &CmndSerialDelimiter,
&CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout,
&CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
@ -922,11 +928,19 @@ void CmndStatus(void)
}
if ((0 == payload) || (3 == payload)) {
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\""
D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\""
Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\""
#ifdef USE_UFILESYS
D_CMND_FILELOG "\":%d,\""
#endif // USE_UFILESYS
D_CMND_SYSLOG "\":%d,\"" D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\""
D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\""
D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]}}"),
Settings->seriallog_level, Settings->weblog_level, Settings->mqttlog_level, Settings->syslog_level,
SettingsText(SET_SYSLOG_HOST), Settings->syslog_port, EscapeJSONString(SettingsText(SET_STASSID1)).c_str(), EscapeJSONString(SettingsText(SET_STASSID2)).c_str(), Settings->tele_period,
Settings->seriallog_level, Settings->weblog_level, Settings->mqttlog_level,
#ifdef USE_UFILESYS
Settings->filelog_level,
#endif // USE_UFILESYS
Settings->syslog_level, SettingsText(SET_SYSLOG_HOST), Settings->syslog_port,
EscapeJSONString(SettingsText(SET_STASSID1)).c_str(), EscapeJSONString(SettingsText(SET_STASSID2)).c_str(), Settings->tele_period,
Settings->flag2.data, Settings->flag.data, ToHex_P((unsigned char*)Settings->param, PARAM8_SIZE, stemp2, sizeof(stemp2)),
Settings->flag3.data, Settings->flag4.data, Settings->flag5.data, Settings->flag6.data);
CmndStatusResponse(3);
@ -2147,6 +2161,15 @@ void CmndLogport(void)
ResponseCmndNumber(Settings->syslog_port);
}
#ifdef USE_UFILESYS
void CmndFilelog(void) {
if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) {
Settings->filelog_level = XdrvMailbox.payload;
}
ResponseCmndNumber(Settings->filelog_level);
}
#endif // USE_UFILESYS
void CmndIpAddress(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) {

View File

@ -1176,6 +1176,9 @@ void PerformEverySecond(void)
#ifdef SYSLOG_UPDATE_SECOND
SyslogAsync(false);
#endif // SYSLOG_UPDATE_SECOND
#ifdef USE_UFILESYS
FileLoggingAsync(false);
#endif // USE_UFILESYS
ResetGlobalValues();
@ -1346,6 +1349,9 @@ void Every250mSeconds(void)
// Check if log refresh needed in case of fast buffer fill
MqttPublishLoggingAsync(true);
SyslogAsync(true);
#ifdef USE_UFILESYS
FileLoggingAsync(true);
#endif // USE_UFILESYS
/*-------------------------------------------------------------------------------------------*\
* Every second at 0.25 second interval

View File

@ -494,6 +494,69 @@ bool TfsRenameFile(const char *fname1, const char *fname2) {
return true;
}
/*********************************************************************************************\
* Log file
*
* Rotate max 8 x 100kB log files /log1 -> /log2 ... /log8 -> /log1 ...
* Keep at least 100kB free on filesystem
* Filesystem needs to ba larger than 110kB
\*********************************************************************************************/
void FileLoggingAsync(bool refresh) {
static uint32_t index = 1;
if (!ffs_type || !Settings->filelog_level) { return; }
if (refresh && !NeedLogRefresh(Settings->filelog_level, index)) { return; }
if (UfsSize() < 110) { return; } // File system too small
char fname[14];
bool file_delete = false;
File file;
while (1) {
snprintf_P(fname, sizeof(fname), PSTR(TASM_FILE_LOG), Settings->mbflag2.log_file_idx +1); // /log1
if (file_delete) {
ffsp->remove(fname); // Delete previous log file
}
file = ffsp->open(fname, "a"); // Append
if (!file) {
file = ffsp->open(fname, "w"); // Make if not exists
if (!file) {
AddLog(LOG_LEVEL_INFO, PSTR("FLG: Save failed"));
return; // Failed to make file
}
}
if (file.size() > 100000) { // Rotate log file if size over 100k
file.close();
Settings->mbflag2.log_file_idx++;
if ((8 == Settings->mbflag2.log_file_idx) || // Keep max 100k x 8 log files
(UfsFree() < 110)) { // Keep free space around 100k
Settings->mbflag2.log_file_idx = 0;
file_delete = true;
}
} else {
break;
}
}
#ifdef USE_WEBCAM
WcInterrupt(0); // Stop stream if active to fix TG1WDT_SYS_RESET
#endif
char* line;
size_t len;
while (GetLog(Settings->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);
snprintf_P(fname, sizeof(fname), PSTR("\r\n"));
file.write((uint8_t*)fname, 2);
}
#ifdef USE_WEBCAM
WcInterrupt(1);
#endif
file.close();
}
/*********************************************************************************************\
* File command execute support
\*********************************************************************************************/