logging.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #include "config.h"
  2. #include "logging.h"
  3. #include <cctype>
  4. #include <cstdarg>
  5. #include <cstdio>
  6. #include <cstring>
  7. #include <mutex>
  8. #include <optional>
  9. #include <string>
  10. #include <string_view>
  11. #include <utility>
  12. #include "alstring.h"
  13. #include "strutils.h"
  14. #if defined(_WIN32)
  15. #define WIN32_LEAN_AND_MEAN
  16. #include <windows.h>
  17. #elif defined(__ANDROID__)
  18. #include <android/log.h>
  19. #endif
  20. FILE *gLogFile{stderr};
  21. #ifdef _DEBUG
  22. LogLevel gLogLevel{LogLevel::Warning};
  23. #else
  24. LogLevel gLogLevel{LogLevel::Error};
  25. #endif
  26. namespace {
  27. using namespace std::string_view_literals;
  28. enum class LogState : uint8_t {
  29. FirstRun,
  30. Ready,
  31. Disable
  32. };
  33. std::mutex LogCallbackMutex;
  34. LogState gLogState{LogState::FirstRun};
  35. LogCallbackFunc gLogCallback{};
  36. void *gLogCallbackPtr{};
  37. constexpr auto GetLevelCode(LogLevel level) noexcept -> std::optional<char>
  38. {
  39. switch(level)
  40. {
  41. case LogLevel::Disable: break;
  42. case LogLevel::Error: return 'E';
  43. case LogLevel::Warning: return 'W';
  44. case LogLevel::Trace: return 'I';
  45. }
  46. return std::nullopt;
  47. }
  48. } // namespace
  49. void al_set_log_callback(LogCallbackFunc callback, void *userptr)
  50. {
  51. auto cblock = std::lock_guard{LogCallbackMutex};
  52. gLogCallback = callback;
  53. gLogCallbackPtr = callback ? userptr : nullptr;
  54. if(gLogState == LogState::FirstRun)
  55. {
  56. auto extlogopt = al::getenv("ALSOFT_DISABLE_LOG_CALLBACK");
  57. if(!extlogopt || *extlogopt != "1")
  58. gLogState = LogState::Ready;
  59. else
  60. gLogState = LogState::Disable;
  61. }
  62. }
  63. void al_print_impl(LogLevel level, const fmt::string_view fmt, fmt::format_args args)
  64. {
  65. const auto msg = fmt::vformat(fmt, std::move(args));
  66. auto prefix = "[ALSOFT] (--) "sv;
  67. switch(level)
  68. {
  69. case LogLevel::Disable: break;
  70. case LogLevel::Error: prefix = "[ALSOFT] (EE) "sv; break;
  71. case LogLevel::Warning: prefix = "[ALSOFT] (WW) "sv; break;
  72. case LogLevel::Trace: prefix = "[ALSOFT] (II) "sv; break;
  73. }
  74. if(gLogLevel >= level)
  75. {
  76. auto logfile = gLogFile;
  77. fmt::println(logfile, "{}{}", prefix, msg);
  78. fflush(logfile);
  79. }
  80. #if defined(_WIN32) && !defined(NDEBUG)
  81. /* OutputDebugStringW has no 'level' property to distinguish between
  82. * informational, warning, or error debug messages. So only print them for
  83. * non-Release builds.
  84. */
  85. OutputDebugStringW(utf8_to_wstr(fmt::format("{}{}\n", prefix, msg)).c_str());
  86. #elif defined(__ANDROID__)
  87. auto android_severity = [](LogLevel l) noexcept
  88. {
  89. switch(l)
  90. {
  91. case LogLevel::Trace: return ANDROID_LOG_DEBUG;
  92. case LogLevel::Warning: return ANDROID_LOG_WARN;
  93. case LogLevel::Error: return ANDROID_LOG_ERROR;
  94. /* Should not happen. */
  95. case LogLevel::Disable:
  96. break;
  97. }
  98. return ANDROID_LOG_ERROR;
  99. };
  100. /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) */
  101. __android_log_print(android_severity(level), "openal", "%.*s%s", al::sizei(prefix),
  102. prefix.data(), msg.c_str());
  103. #endif
  104. auto cblock = std::lock_guard{LogCallbackMutex};
  105. if(gLogState != LogState::Disable)
  106. {
  107. if(auto logcode = GetLevelCode(level))
  108. {
  109. if(gLogCallback)
  110. gLogCallback(gLogCallbackPtr, *logcode, msg.data(), al::sizei(msg));
  111. else if(gLogState == LogState::FirstRun)
  112. gLogState = LogState::Disable;
  113. }
  114. }
  115. }