You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
2.6KB

  1. #include <mutex>
  2. #include <common.hpp>
  3. #include <asset.hpp>
  4. #include <settings.hpp>
  5. #include <system.hpp>
  6. #include <unistd.h> // for dup2
  7. namespace rack {
  8. namespace logger {
  9. static FILE* outputFile = NULL;
  10. static double startTime = 0.0;
  11. static std::mutex logMutex;
  12. void init() {
  13. startTime = system::getTime();
  14. // Don't open a file in development mode.
  15. if (settings::devMode) {
  16. outputFile = stderr;
  17. return;
  18. }
  19. assert(!outputFile);
  20. outputFile = std::fopen(asset::logPath.c_str(), "w");
  21. if (!outputFile) {
  22. std::fprintf(stderr, "Could not open log at %s\n", asset::logPath.c_str());
  23. }
  24. // Redirect stdout and stderr to the file
  25. // Actually, disable this because we don't want to steal stdout/stderr from the DAW in Rack for DAWs.
  26. // dup2(fileno(outputFile), fileno(stdout));
  27. // dup2(fileno(outputFile), fileno(stderr));
  28. }
  29. void destroy() {
  30. if (outputFile && outputFile != stderr) {
  31. // Print end token so we know if the logger exited cleanly.
  32. std::fprintf(outputFile, "END");
  33. std::fclose(outputFile);
  34. }
  35. outputFile = NULL;
  36. }
  37. static const char* const levelLabels[] = {
  38. "debug",
  39. "info",
  40. "warn",
  41. "fatal"
  42. };
  43. static const int levelColors[] = {
  44. 35,
  45. 34,
  46. 33,
  47. 31
  48. };
  49. static void logVa(Level level, const char* filename, int line, const char* func, const char* format, va_list args) {
  50. std::lock_guard<std::mutex> lock(logMutex);
  51. if (!outputFile)
  52. return;
  53. double nowTime = system::getTime();
  54. double duration = nowTime - startTime;
  55. if (outputFile == stderr)
  56. std::fprintf(outputFile, "\x1B[%dm", levelColors[level]);
  57. std::fprintf(outputFile, "[%.03f %s %s:%d %s] ", duration, levelLabels[level], filename, line, func);
  58. if (outputFile == stderr)
  59. std::fprintf(outputFile, "\x1B[0m");
  60. std::vfprintf(outputFile, format, args);
  61. std::fprintf(outputFile, "\n");
  62. std::fflush(outputFile);
  63. }
  64. void log(Level level, const char* filename, int line, const char* func, const char* format, ...) {
  65. va_list args;
  66. va_start(args, format);
  67. logVa(level, filename, line, func, format, args);
  68. va_end(args);
  69. }
  70. static bool fileEndsWith(FILE* file, std::string str) {
  71. // Seek to last `len` characters
  72. size_t len = str.size();
  73. std::fseek(file, -long(len), SEEK_END);
  74. char actual[len];
  75. if (std::fread(actual, 1, len, file) != len)
  76. return false;
  77. return std::string(actual, len) == str;
  78. }
  79. bool isTruncated() {
  80. if (settings::devMode)
  81. return false;
  82. // Open existing log file
  83. FILE* file = std::fopen(asset::logPath.c_str(), "r");
  84. if (!file)
  85. return false;
  86. DEFER({std::fclose(file);});
  87. if (fileEndsWith(file, "END"))
  88. return false;
  89. // legacy <=v1
  90. if (fileEndsWith(file, "Destroying logger\n"))
  91. return false;
  92. return true;
  93. }
  94. } // namespace logger
  95. } // namespace rack