|
- #include <mutex>
-
- #include <common.hpp>
- #include <asset.hpp>
- #include <system.hpp>
- #include <settings.hpp>
- // #include <unistd.h> // for dup2
-
-
- namespace rack {
- namespace logger {
-
-
- std::string logPath;
- static FILE* outputFile = NULL;
- static std::mutex mutex;
- static bool truncated = false;
- const static long maxSize = 10 * 1000 * 1000; // 10 MB
-
-
- static bool fileEndsWith(FILE* file, std::string str) {
- // Seek to last `len` characters
- size_t len = str.size();
- std::fseek(file, -long(len), SEEK_END);
- char actual[len];
- if (std::fread(actual, 1, len, file) != len)
- return false;
- return std::string(actual, len) == str;
- }
-
- static bool isTruncated() {
- if (logPath.empty())
- return false;
-
- // Open existing log file
- FILE* file = std::fopen(logPath.c_str(), "r");
- if (!file)
- return false;
- DEFER({std::fclose(file);});
-
- if (fileEndsWith(file, "END"))
- return false;
- // legacy <=v1
- if (fileEndsWith(file, "Destroying logger\n"))
- return false;
- return true;
- }
-
-
- bool init() {
- if (outputFile)
- return true;
-
- std::lock_guard<std::mutex> lock(mutex);
- truncated = false;
-
- // Don't open a file in development mode.
- if (logPath.empty()) {
- outputFile = stderr;
- }
- else {
- truncated = isTruncated();
-
- outputFile = std::fopen(logPath.c_str(), "w");
- if (!outputFile) {
- std::fprintf(stderr, "Could not open log at %s\n", logPath.c_str());
- return false;
- }
- }
-
- return true;
- }
-
- void destroy() {
- std::lock_guard<std::mutex> lock(mutex);
- if (outputFile && outputFile != stderr) {
- // Print end token so we know if the logger exited cleanly.
- std::fprintf(outputFile, "END");
- std::fclose(outputFile);
- }
- outputFile = NULL;
- }
-
- static const char* const levelLabels[] = {
- "debug",
- "info",
- "warn",
- "fatal",
- };
-
- static const int levelColors[] = {
- 35,
- 34,
- 33,
- 31,
- };
-
- static void logVa(Level level, const char* filename, int line, const char* func, const char* format, va_list args) {
- if (!outputFile)
- return;
-
- // Record logging time before calling OS functions
- double nowTime = system::getTime();
-
- // Check if log size is full
- if (outputFile != stderr) {
- long pos = std::ftell(outputFile);
- if (pos >= maxSize)
- return;
- }
-
- std::lock_guard<std::mutex> lock(mutex);
-
- if (outputFile == stderr)
- std::fprintf(outputFile, "\x1B[%dm", levelColors[level]);
- std::fprintf(outputFile, "[%.03f %s %s:%d %s] ", nowTime, levelLabels[level], filename, line, func);
- if (outputFile == stderr)
- std::fprintf(outputFile, "\x1B[0m");
- std::vfprintf(outputFile, format, args);
- std::fprintf(outputFile, "\n");
- // Note: This adds around 10us, but it's important for logging to finish writing the file, and logging is not used in performance critical code.
- std::fflush(outputFile);
- }
-
- void log(Level level, const char* filename, int line, const char* func, const char* format, ...) {
- va_list args;
- va_start(args, format);
- logVa(level, filename, line, func, format, args);
- va_end(args);
- }
-
- bool wasTruncated() {
- return truncated;
- }
-
-
- } // namespace logger
- } // namespace rack
|