diff --git a/src/workerd/util/sentry-test.c++ b/src/workerd/util/sentry-test.c++ index 34ad9bcdbef..90a6348206b 100644 --- a/src/workerd/util/sentry-test.c++ +++ b/src/workerd/util/sentry-test.c++ @@ -5,6 +5,7 @@ #include "sentry.h" #include +#include namespace { @@ -26,4 +27,11 @@ KJ_TEST("Sentry tags are applied only when logging") { expectSentryTag("NOSENTRY"_kj); } +KJ_TEST("periodic logging is thread-safe") { + auto log = []() { LOG_PERIODICALLY(INFO, "concurrent periodic log"); }; + + kj::Thread first(log); + kj::Thread second(log); +} + } // namespace diff --git a/src/workerd/util/sentry.h b/src/workerd/util/sentry.h index 851df86e777..74902b10497 100644 --- a/src/workerd/util/sentry.h +++ b/src/workerd/util/sentry.h @@ -12,6 +12,7 @@ #include #include +#include #include namespace workerd { @@ -69,6 +70,23 @@ inline bool isInterestingException(const kj::Exception& e) { e.getType() != kj::Exception::Type::OVERLOADED; } +struct LogPeriodically { + bool shouldLog() { + const auto now = kj::systemCoarseMonotonicClock().now(); + const auto nowNanos = (now - kj::origin()) / kj::NANOSECONDS; + const auto lastLogged = lastLoggedNanos.load(std::memory_order_relaxed); + if (KJ_LIKELY(nowNanos - lastLogged < 1 * kj::HOURS / kj::NANOSECONDS)) { + return false; + } + + lastLoggedNanos.store(nowNanos, std::memory_order_relaxed); + return true; + } + + private: + std::atomic lastLoggedNanos{-(1 * kj::HOURS / kj::NANOSECONDS)}; +}; + #define LOG_NOSENTRY(severity, ...) KJ_LOG(severity, "NOSENTRY " __VA_ARGS__); #define LOG_IF_INTERESTING(exception, severity, ...) \ @@ -98,11 +116,8 @@ inline bool isInterestingException(const kj::Exception& e) { // be prohibitive. #define LOG_PERIODICALLY(severity, ...) \ do { \ - static kj::TimePoint KJ_UNIQUE_NAME(lastLogged) = kj::origin() - 1 * kj::HOURS; \ - const auto KJ_UNIQUE_NAME(now) = kj::systemCoarseMonotonicClock().now(); \ - const auto KJ_UNIQUE_NAME(elapsed) = KJ_UNIQUE_NAME(now) - KJ_UNIQUE_NAME(lastLogged); \ - if (KJ_UNLIKELY(KJ_UNIQUE_NAME(elapsed) >= 1 * kj::HOURS)) { \ - KJ_UNIQUE_NAME(lastLogged) = KJ_UNIQUE_NAME(now); \ + static ::workerd::LogPeriodically KJ_UNIQUE_NAME(logPeriodically); \ + if (KJ_UNLIKELY(KJ_UNIQUE_NAME(logPeriodically).shouldLog())) { \ KJ_LOG(severity, __VA_ARGS__); \ } \ } while (0)