Files
2026-07-17 15:29:53 -04:00

141 lines
3.9 KiB
C++

#pragma once
#include <LittleFS.h>
#include "config.h"
// Rolling debug log stored in LittleFS at /debug.log
// Format: one JSON line per entry: {"t":<millis>,"m":"message"}
// Max MAX_LOG_ENTRIES entries; oldest are trimmed on save.
#define LOG_FILE "/debug.log"
#define MAX_LOG_ENTRIES 250
#define MAX_LOG_MSG 128
class DebugLog {
public:
void begin() {
_entryCount = 0;
if (LittleFS.exists(LOG_FILE)) {
File f = LittleFS.open(LOG_FILE, "r");
if (f) {
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() > 0) _entryCount++;
}
f.close();
}
}
}
void log(const char* msg) {
// Mirror to serial for live debugging
Serial.printf("[LOG] %s\n", msg);
if (_entryCount >= MAX_LOG_ENTRIES) {
trimLog();
}
File f = LittleFS.open(LOG_FILE, "a");
if (!f) return;
char escaped[MAX_LOG_MSG * 2];
escapeJson(msg, escaped, sizeof(escaped));
f.printf("{\"t\":%lu,\"m\":\"%s\"}\n", millis(), escaped);
f.close();
_entryCount++;
}
void logf(const char* fmt, ...) {
char buf[MAX_LOG_MSG];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
log(buf);
}
// Send the entire log as a JSON array over serial (handles "get_log" cmd).
void sendOverSerial() {
Serial.print("{\"rsp\":\"log\",\"entries\":[");
Serial.flush();
if (LittleFS.exists(LOG_FILE)) {
File f = LittleFS.open(LOG_FILE, "r");
if (f) {
bool first = true;
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0) continue;
if (!first) Serial.print(",");
Serial.print(line);
first = false;
Serial.flush();
}
f.close();
}
}
Serial.println("]}");
Serial.flush();
}
void clear() {
LittleFS.remove(LOG_FILE);
_entryCount = 0;
}
private:
int _entryCount = 0;
// Drop the oldest half of the log when MAX_LOG_ENTRIES is hit.
void trimLog() {
File f = LittleFS.open(LOG_FILE, "r");
if (!f) return;
String lines[MAX_LOG_ENTRIES];
int count = 0;
while (f.available() && count < MAX_LOG_ENTRIES) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() > 0) {
lines[count++] = line;
}
}
f.close();
int keepFrom = count / 2;
File out = LittleFS.open(LOG_FILE, "w");
if (!out) return;
for (int i = keepFrom; i < count; i++) {
out.println(lines[i]);
}
out.close();
_entryCount = count - keepFrom;
}
void escapeJson(const char* input, char* output, size_t maxLen) {
size_t o = 0;
for (const char* p = input; *p && o < maxLen - 7; p++) {
unsigned char c = (unsigned char)*p;
if (c == '"' || c == '\\') {
output[o++] = '\\';
output[o++] = c;
} else if (c == '\n') {
output[o++] = '\\'; output[o++] = 'n';
} else if (c == '\r') {
output[o++] = '\\'; output[o++] = 'r';
} else if (c == '\t') {
output[o++] = '\\'; output[o++] = 't';
} else if (c < 0x20) {
o += snprintf(output + o, maxLen - o, "\\u%04x", c);
} else {
output[o++] = c;
}
}
output[o] = '\0';
}
};