166 lines
4.5 KiB
C++
166 lines
4.5 KiB
C++
#include "DebugLog.h"
|
|
|
|
#include <android/log.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstdarg>
|
|
#include <cstdio>
|
|
#include <ctime>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
namespace {
|
|
|
|
std::mutex g_mtx;
|
|
FILE* g_fp = nullptr;
|
|
std::string g_path;
|
|
constexpr size_t kMaxSize = 2 * 1024 * 1024; // 2 MB before rotation
|
|
|
|
// Throttle bookkeeping: one slot per distinct key passed to log_throttled().
|
|
struct ThrottleSlot {
|
|
int64_t last_emit_ms = 0; // when we last actually wrote a line for this key
|
|
uint32_t suppressed = 0; // number of calls swallowed since last_emit_ms
|
|
};
|
|
std::unordered_map<std::string, ThrottleSlot> g_throttle;
|
|
constexpr int64_t kThrottleWindowMs = 2000; // 2 seconds
|
|
|
|
pid_t currentTid() {
|
|
return static_cast<pid_t>(syscall(SYS_gettid));
|
|
}
|
|
|
|
int64_t nowMs() {
|
|
timespec ts{};
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return static_cast<int64_t>(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
|
|
}
|
|
|
|
// Assumes g_mtx is held. Writes a single pre-formatted line to disk + logcat.
|
|
void writeLineLocked(const char* body) {
|
|
pid_t tid = currentTid();
|
|
|
|
timespec ts{};
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
struct tm tm_info{};
|
|
localtime_r(&ts.tv_sec, &tm_info);
|
|
char timebuf[32];
|
|
strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm_info);
|
|
|
|
if (g_fp) {
|
|
std::fprintf(g_fp, "[%s.%03ld][tid=%d] %s\n",
|
|
timebuf, ts.tv_nsec / 1000000, tid, body);
|
|
std::fflush(g_fp);
|
|
}
|
|
__android_log_print(ANDROID_LOG_INFO, "FACE_DBG", "[tid=%d] %s", tid, body);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace DebugLog {
|
|
|
|
void init(const char* internalDataPath) {
|
|
std::lock_guard<std::mutex> lk(g_mtx);
|
|
if (g_fp) {
|
|
return; // already initialized
|
|
}
|
|
|
|
const char* base = (internalDataPath && *internalDataPath) ? internalDataPath : "/data/local/tmp";
|
|
g_path = std::string(base) + "/face_sdk_debug.log";
|
|
|
|
// Rotate once if current file is too large.
|
|
struct stat st{};
|
|
if (stat(g_path.c_str(), &st) == 0 && static_cast<size_t>(st.st_size) > kMaxSize) {
|
|
std::string backup = g_path + ".old";
|
|
std::remove(backup.c_str());
|
|
std::rename(g_path.c_str(), backup.c_str());
|
|
}
|
|
|
|
g_fp = std::fopen(g_path.c_str(), "a");
|
|
if (g_fp) {
|
|
time_t now = time(nullptr);
|
|
struct tm tm_info{};
|
|
localtime_r(&now, &tm_info);
|
|
char tbuf[64];
|
|
strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", &tm_info);
|
|
std::fprintf(g_fp,
|
|
"\n========== DebugLog opened @ %s (pid=%d) ==========\n",
|
|
tbuf, getpid());
|
|
std::fflush(g_fp);
|
|
}
|
|
|
|
__android_log_print(ANDROID_LOG_INFO, "FACE_DBG",
|
|
"DebugLog file path: %s", g_path.c_str());
|
|
}
|
|
|
|
void log(const char* fmt, ...) {
|
|
char buf[1024];
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int n = std::vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
if (n < 0) return;
|
|
|
|
std::lock_guard<std::mutex> lk(g_mtx);
|
|
writeLineLocked(buf);
|
|
}
|
|
|
|
void log_throttled(const char* key, const char* fmt, ...) {
|
|
char buf[1024];
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
int n = std::vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
if (n < 0) return;
|
|
|
|
const char* k = key ? key : "";
|
|
int64_t now = nowMs();
|
|
|
|
std::lock_guard<std::mutex> lk(g_mtx);
|
|
auto it = g_throttle.find(k);
|
|
if (it == g_throttle.end()) {
|
|
// First time we see this key -> always log.
|
|
ThrottleSlot slot;
|
|
slot.last_emit_ms = now;
|
|
slot.suppressed = 0;
|
|
g_throttle.emplace(k, slot);
|
|
writeLineLocked(buf);
|
|
return;
|
|
}
|
|
|
|
ThrottleSlot& slot = it->second;
|
|
int64_t elapsed = now - slot.last_emit_ms;
|
|
if (elapsed < kThrottleWindowMs) {
|
|
// Still inside the 2s window -> swallow.
|
|
++slot.suppressed;
|
|
return;
|
|
}
|
|
|
|
// Window elapsed: emit the line, annotated with how many we swallowed.
|
|
if (slot.suppressed > 0) {
|
|
char annotated[1200];
|
|
std::snprintf(annotated, sizeof(annotated),
|
|
"%s (repeated %u times in last %lldms, key=%s)",
|
|
buf, slot.suppressed, (long long)elapsed, k);
|
|
writeLineLocked(annotated);
|
|
} else {
|
|
writeLineLocked(buf);
|
|
}
|
|
slot.last_emit_ms = now;
|
|
slot.suppressed = 0;
|
|
}
|
|
|
|
void flush() {
|
|
std::lock_guard<std::mutex> lk(g_mtx);
|
|
if (g_fp) std::fflush(g_fp);
|
|
}
|
|
|
|
std::string getLogPath() {
|
|
std::lock_guard<std::mutex> lk(g_mtx);
|
|
return g_path;
|
|
}
|
|
|
|
} // namespace DebugLog
|