diff options
| author | Stefan Boberg <[email protected]> | 2023-05-09 14:39:01 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-09 14:39:01 +0200 |
| commit | f384c90daca485255c19dfda90939a76a121c530 (patch) | |
| tree | 84473505995798108c4678694d946eba9d130b61 /src/zencore | |
| parent | add ip and username to sentry reports if allowed in settings (#276) (diff) | |
| download | zen-f384c90daca485255c19dfda90939a76a121c530.tar.xz zen-f384c90daca485255c19dfda90939a76a121c530.zip | |
implemented thread-local activity tracking
includes support for on-demand formatting of scope in error messages
Diffstat (limited to 'src/zencore')
| -rw-r--r-- | src/zencore/include/zencore/logging.h | 83 | ||||
| -rw-r--r-- | src/zencore/logging.cpp | 111 |
2 files changed, 194 insertions, 0 deletions
diff --git a/src/zencore/include/zencore/logging.h b/src/zencore/include/zencore/logging.h index 5cbe034cf..92db1b35b 100644 --- a/src/zencore/include/zencore/logging.h +++ b/src/zencore/include/zencore/logging.h @@ -134,3 +134,86 @@ LogIsErrorLevel(int level) using namespace std::literals; \ ConsoleLog().info(fmtstr##sv, ##__VA_ARGS__); \ } while (false) + +////////////////////////////////////////////////////////////////////////// + +namespace zen { + +class StringBuilderBase; + +/** Thread-local diagnostics scope stack + + Intended to be used in logging and such to provide context on demand. + A subclass should do as little work as possible at construction time + and ideally only do something when the Emit function is called. + */ +class ScopedActivityBase +{ +public: + ScopedActivityBase(); + virtual ~ScopedActivityBase(); + virtual void Emit(StringBuilderBase& Target) = 0; + + inline ScopedActivityBase* GetNext() { return m_NextScope; } + +private: + ScopedActivityBase* m_NextScope; +}; + +class ScopedActivityString : public ScopedActivityBase +{ +public: + inline ScopedActivityString(const std::string_view& Text) : m_Text{Text} {} + virtual ~ScopedActivityString(); + virtual void Emit(StringBuilderBase& Target) override; + +private: + const std::string_view m_Text; +}; + +template<typename Lambda> +class ScopedLazyActivity : public ScopedActivityBase +{ +public: + ScopedLazyActivity(Lambda&& InFunc) : m_CapturedFunc(std::move(InFunc)) {} + ~ScopedLazyActivity() = default; + + virtual void Emit(StringBuilderBase& Target) override { m_CapturedFunc(Target); } + +private: + const Lambda m_CapturedFunc; +}; + +std::string_view EmitScopesForLogging(StringBuilderBase& OutString); + +#define ZEN_LOG_SCOPE(...) ScopedLazyActivity Activity$##__LINE__([&](StringBuilderBase& Out) { Out << fmt::format(__VA_ARGS__); }) + +#define ZEN_SCOPED_ERROR(fmtstr, ...) \ + do \ + { \ + ExtendableStringBuilder<256> ScopeString; \ + const std::string_view Scopes = EmitScopesForLogging(ScopeString); \ + ZEN_LOG_WITH_LOCATION(Log(), \ + spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \ + spdlog::level::err, \ + fmtstr "{}"sv, \ + ##__VA_ARGS__, \ + Scopes); \ + } while (false) + +#define ZEN_SCOPED_CRITICAL(fmtstr, ...) \ + do \ + { \ + ExtendableStringBuilder<256> ScopeString; \ + const std::string_view Scopes = EmitScopesForLogging(ScopeString); \ + ZEN_LOG_WITH_LOCATION(Log(), \ + spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \ + spdlog::level::critical, \ + fmtstr "{}"sv, \ + ##__VA_ARGS__, \ + Scopes); \ + } while (false) + +ScopedActivityBase* GetThreadActivity(); + +} // namespace zen diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp index a6423e2dc..13a8ce9ef 100644 --- a/src/zencore/logging.cpp +++ b/src/zencore/logging.cpp @@ -2,6 +2,9 @@ #include "zencore/logging.h" +#include <zencore/string.h> +#include <zencore/testing.h> + #include <spdlog/sinks/stdout_color_sinks.h> namespace zen { @@ -83,3 +86,111 @@ ShutdownLogging() } } // namespace zen::logging + +namespace zen { + +thread_local ScopedActivityBase* t_ScopeStack = nullptr; + +ScopedActivityBase* +GetThreadActivity() +{ + return t_ScopeStack; +} + +ScopedActivityBase::ScopedActivityBase() : m_NextScope{t_ScopeStack} +{ + t_ScopeStack = this; +} + +ScopedActivityBase::~ScopedActivityBase() +{ + ZEN_ASSERT(t_ScopeStack == this); + t_ScopeStack = m_NextScope; +} + +std::string_view +EmitActivitiesForLogging(StringBuilderBase& OutString) +{ + OutString.Reset(); + + for (auto Bread = GetThreadActivity(); Bread; Bread = Bread->GetNext()) + { + OutString.Append("\n|>|>|> "); + Bread->Emit(OutString); + } + + return OutString.ToView(); +} + +////////////////////////////////////////////////////////////////////////// + +ScopedActivityString::~ScopedActivityString() +{ +} + +void +ScopedActivityString::Emit(StringBuilderBase& Target) +{ + Target.Append(m_Text); +} + +#if ZEN_WITH_TESTS + +void +logging_forcelink() +{ +} + +using namespace std::literals; + +TEST_CASE("simple.bread") +{ + ExtendableStringBuilder<256> Crumbs; + + auto EmitBreadcrumbs = [&] { + Crumbs.Reset(); + + for (auto Bread = GetThreadActivity(); Bread; Bread = Bread->GetNext()) + { + Bread->Emit(Crumbs); + } + + return Crumbs.ToView(); + }; + + SUBCASE("single") + { + ScopedActivityString _{"hello"}; + EmitBreadcrumbs(); + + CHECK_EQ(Crumbs.ToView(), "hello"sv); + } + + SUBCASE("multi") + { + ScopedActivityString $1{"hello"}; + ScopedActivityString $2{"world"}; + ScopedActivityString $3{"amaze"}; + + CHECK_EQ(EmitBreadcrumbs(), "amazeworldhello"sv); + } + + SUBCASE("multi_defer") + { + int n = 42; + ScopedActivityString $1{"hello"}; + ScopedActivityString $2{"world"}; + ScopedActivityString $3{"amaze"}; + ScopedLazyActivity $4{[&](StringBuilderBase& Out) { Out << "plant"; }}; + { + ScopedLazyActivity $5{[&](StringBuilderBase& Out) { Out << n; }}; + ZEN_LOG_SCOPE("{}:{}", "abc", "def"); + CHECK_EQ(EmitBreadcrumbs(), "abc:def42plantamazeworldhello"sv); + } + CHECK_EQ(EmitBreadcrumbs(), "amazeworldhello"sv); + } +} + +#endif + +} // namespace zen |