AZCoreLogSink.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/Memory/SystemAllocator.h>
  10. #include <AzCore/Math/Crc.h>
  11. #include <AzCore/Debug/TraceMessageBus.h>
  12. #include <AzCore/std/containers/unordered_map.h>
  13. #include <AzCore/Console/IConsole.h>
  14. #include <CryAssert.h>
  15. namespace AZ::Debug
  16. {
  17. AZ_CVAR_EXTERNED(int, bg_traceLogLevel);
  18. }
  19. /**
  20. * Hook Trace bus so we can funnel AZ asserts, warnings, etc to CryEngine.
  21. *
  22. * Note: This is currently owned by CrySystem, because CrySystem owns
  23. * the logging mechanism for which it is relevant.
  24. */
  25. class AZCoreLogSink
  26. : public AZ::Debug::TraceMessageBus::Handler
  27. {
  28. public:
  29. ~AZCoreLogSink()
  30. {
  31. Disconnect();
  32. }
  33. inline static void Connect(bool suppressSystemOutput)
  34. {
  35. GetInstance().m_ignoredAsserts = new IgnoredAssertMap();
  36. GetInstance().m_suppressSystemOutput = suppressSystemOutput;
  37. GetInstance().BusConnect();
  38. }
  39. inline static void Disconnect()
  40. {
  41. GetInstance().BusDisconnect();
  42. delete GetInstance().m_ignoredAsserts;
  43. GetInstance().m_ignoredAsserts = nullptr;
  44. }
  45. static AZCoreLogSink& GetInstance()
  46. {
  47. static AZCoreLogSink s_sink;
  48. return s_sink;
  49. }
  50. static bool IsCryLogReady()
  51. {
  52. bool ready = gEnv && gEnv->pSystem && gEnv->pLog;
  53. #ifdef _RELEASE
  54. static bool hasSetCVar = false;
  55. if(!hasSetCVar && ready)
  56. {
  57. // AZ logging only has a concept of 3 levels (error, warning, info) but cry logging has 4 levels (..., messaging). If info level is set, we'll turn on messaging as well
  58. int logLevel = AZ::Debug::bg_traceLogLevel == static_cast<int>(AZ::Debug::LogLevel::Info) ? 4 : AZ::Debug::bg_traceLogLevel;
  59. gEnv->pConsole->GetCVar("log_WriteToFileVerbosity")->Set(logLevel);
  60. hasSetCVar = true;
  61. }
  62. #endif
  63. return ready;
  64. }
  65. bool OnPreAssert(const char* fileName, int line, const char* func, const char* message) override
  66. {
  67. AZ_UNUSED(fileName);
  68. AZ_UNUSED(line);
  69. AZ_UNUSED(func);
  70. AZ_UNUSED(message);
  71. return false; // allow AZCore to do its default behavior. This usually results in an application shutdown.
  72. }
  73. bool OnPreError(const char* window, const char* fileName, int line, const char* func, const char* message) override
  74. {
  75. AZ_UNUSED(fileName);
  76. AZ_UNUSED(line);
  77. AZ_UNUSED(func);
  78. if (!IsCryLogReady())
  79. {
  80. return false; // allow AZCore to do its default behavior.
  81. }
  82. AZStd::string_view windowView = window;
  83. if (!windowView.empty())
  84. {
  85. gEnv->pLog->LogError("(%s) - %s", window, message);
  86. }
  87. else
  88. {
  89. gEnv->pLog->LogError("%s", message);
  90. }
  91. return m_suppressSystemOutput;
  92. }
  93. bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override
  94. {
  95. AZ_UNUSED(fileName);
  96. AZ_UNUSED(line);
  97. AZ_UNUSED(func);
  98. if (!IsCryLogReady())
  99. {
  100. return false; // allow AZCore to do its default behavior.
  101. }
  102. AZStd::string_view windowView = window;
  103. if (!windowView.empty())
  104. {
  105. CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "(%s) - %s", window, message);
  106. }
  107. else
  108. {
  109. CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "%s", message);
  110. }
  111. return m_suppressSystemOutput;
  112. }
  113. bool OnOutput(const char* window, const char* message) override
  114. {
  115. if (!IsCryLogReady())
  116. {
  117. return false; // allow AZCore to do its default behavior.
  118. }
  119. AZStd::string_view windowView = window;
  120. // Only print out the window if it is not equal to the NoWindow or the DefaultSystemWindow value
  121. if (windowView == AZ::Debug::Trace::GetNoWindow() || windowView == AZ::Debug::Trace::GetDefaultSystemWindow())
  122. {
  123. [[maybe_unused]] auto WriteToStream = [message = AZStd::string_view(message)]
  124. (AZ::IO::GenericStream& stream)
  125. {
  126. constexpr AZStd::string_view newline = "\n";
  127. stream.Write(message.size(), message.data());
  128. // performs does not format the output and no newline will automatically be added
  129. // Therefore an explicit invocation for a new line is supplied
  130. stream.Write(newline.size(), newline.data());
  131. };
  132. CryOutputToCallback(ILog::eAlways, WriteToStream);
  133. }
  134. else
  135. {
  136. [[maybe_unused]] auto WriteToStream = [window = AZStd::string_view(window), message = AZStd::string_view(message)]
  137. (AZ::IO::GenericStream& stream)
  138. {
  139. constexpr AZStd::string_view windowMessageSeparator = " - ";
  140. constexpr AZStd::string_view newline = "\n";
  141. constexpr AZStd::string_view leftParenthesis = "(";
  142. constexpr AZStd::string_view rightParenthesis = ")";
  143. stream.Write(leftParenthesis.size(), leftParenthesis.data());
  144. stream.Write(window.size(), window.data());
  145. stream.Write(rightParenthesis.size(), rightParenthesis.data());
  146. stream.Write(windowMessageSeparator.size(), windowMessageSeparator.data());
  147. stream.Write(message.size(), message.data());
  148. stream.Write(newline.size(), newline.data());
  149. };
  150. CryOutputToCallback(ILog::eMessage, WriteToStream);
  151. }
  152. return m_suppressSystemOutput;
  153. }
  154. private:
  155. using IgnoredAssertMap = AZStd::unordered_map<AZ::Crc32, bool, AZStd::hash<AZ::Crc32>, AZStd::equal_to<AZ::Crc32>, AZ::OSStdAllocator>;
  156. IgnoredAssertMap* m_ignoredAsserts;
  157. bool m_suppressSystemOutput = true;
  158. };