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.

112 lines
2.4KB

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