3131#include < spdlog/spdlog.h>
3232#include " compress_rotate_file_sink.h"
3333#include " logger.h"
34-
3534namespace UC ::Logger {
36-
35+ constexpr uint64_t LIMIT_THRESHOLD_MS = 60000 ;
36+ constexpr uint32_t RATE_LIMIT_MAX_LOGS_PER_WINDOW = 3 ;
37+ constexpr uint32_t kRateLimitCountBits = 2 ;
38+ constexpr uint64_t kRateLimitCountMask = (1u << kRateLimitCountBits ) - 1u ;
39+ constexpr size_t kHashMixMagic = 0x9e3779b97f4a7c15ULL ;
40+ constexpr size_t kHashShiftLeft = 12 ;
41+ constexpr size_t kHashShiftRight = 4 ;
3742static spdlog::level::level_enum SpdLevels[] = {spdlog::level::debug, spdlog::level::info,
3843 spdlog::level::warn, spdlog::level::err,
3944 spdlog::level::critical};
@@ -45,6 +50,81 @@ void Logger::Log(Level&& lv, SourceLocation&& loc, std::string&& msg)
4550 this ->logger_ ->log (spdlog::source_loc{loc.file , loc.line , loc.func }, level, std::move (msg));
4651}
4752
53+ inline uint64_t GetCurrentTimeMs ()
54+ {
55+ auto now = std::chrono::steady_clock::now ();
56+ auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
57+ return ms.time_since_epoch ().count ();
58+ }
59+
60+ bool Logger::FilterCallSite (const char * file, int line)
61+ {
62+ uint64_t now = GetCurrentTimeMs ();
63+ const std::string_view fv (file);
64+ std::hash<std::string_view> h;
65+ size_t x = h (fv);
66+ x ^= static_cast <size_t >(line) + kHashMixMagic + (x << kHashShiftLeft ) + (x >> kHashShiftRight );
67+ const uint64_t full_hash = static_cast <uint64_t >(x);
68+ const size_t slot_idx = static_cast <size_t >(full_hash % HASH_SLOT_NUM);
69+ // key_tag=0 is reserved for empty; so shift by +1.
70+ const uint64_t key_tag = full_hash + 1u ;
71+
72+ auto & slot = hash_slots_[slot_idx];
73+ std::atomic<uint64_t >* rate_state = nullptr ;
74+
75+ // 1) Lookup: find an existing chain entry with the same key.
76+ for (size_t i = 0 ; i < HASH_CHAIN_LEN; ++i) {
77+ uint64_t stored = slot.chain_entries [i].key_hash .load (std::memory_order_relaxed);
78+ if (stored == key_tag) {
79+ rate_state = &slot.chain_entries [i].rate_limit_state ;
80+ break ;
81+ }
82+ }
83+
84+ // 2) Insert: if key not found, try to claim an empty entry.
85+ if (rate_state == nullptr ) {
86+ for (size_t i = 0 ; i < HASH_CHAIN_LEN; ++i) {
87+ uint64_t expected_empty = 0 ;
88+ if (slot.chain_entries [i].key_hash .compare_exchange_strong (expected_empty, key_tag,
89+ std::memory_order_relaxed,
90+ std::memory_order_relaxed)) {
91+ rate_state = &slot.chain_entries [i].rate_limit_state ;
92+ break ;
93+ }
94+ }
95+ }
96+
97+ // 3) Evict: if the chain is full, overwrite a deterministic entry.
98+ if (rate_state == nullptr ) {
99+ const size_t evict_idx = static_cast <size_t >(key_tag % HASH_CHAIN_LEN);
100+ rate_state = &slot.chain_entries [evict_idx].rate_limit_state ;
101+ slot.chain_entries [evict_idx].key_hash .store (key_tag, std::memory_order_relaxed);
102+ slot.chain_entries [evict_idx].rate_limit_state .store (0 , std::memory_order_relaxed);
103+ }
104+
105+ uint64_t s = rate_state->load (std::memory_order_relaxed);
106+ const uint64_t window_start = s >> kRateLimitCountBits ;
107+ const uint32_t count = static_cast <uint32_t >(s & kRateLimitCountMask );
108+
109+ if (s == 0 || now - window_start > LIMIT_THRESHOLD_MS) {
110+ const uint64_t desired = (now << kRateLimitCountBits ) | 1u ;
111+ if (rate_state->compare_exchange_strong (s, desired, std::memory_order_relaxed,
112+ std::memory_order_relaxed)) {
113+ return true ;
114+ }
115+ return false ;
116+ }
117+
118+ if (count >= RATE_LIMIT_MAX_LOGS_PER_WINDOW) { return false ; }
119+ const uint64_t desired =
120+ (window_start << kRateLimitCountBits ) | static_cast <uint64_t >(count + 1u );
121+ if (rate_state->compare_exchange_strong (s, desired, std::memory_order_relaxed,
122+ std::memory_order_relaxed)) {
123+ return true ;
124+ }
125+ return false ;
126+ }
127+
48128std::shared_ptr<spdlog::logger> Logger::Make ()
49129{
50130 if (this ->logger_ ) { return this ->logger_ ; }
0 commit comments