MXS-1908 Turn LM_MESSAGE_STATS into a class
This commit is contained in:
@ -254,14 +254,6 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct lm_message_stats
|
|
||||||
{
|
|
||||||
SPINLOCK lock;
|
|
||||||
uint64_t first_ms; /** The time when the error was logged the first time in this window. */
|
|
||||||
uint64_t last_ms; /** The time when the error was logged the last time. */
|
|
||||||
size_t count; /** How many times the error has been reported within this window. */
|
|
||||||
} LM_MESSAGE_STATS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current time.
|
* Returns the current time.
|
||||||
*
|
*
|
||||||
@ -275,6 +267,101 @@ static uint64_t time_monotonic_ms()
|
|||||||
return now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
return now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef enum message_suppression
|
||||||
|
{
|
||||||
|
MESSAGE_NOT_SUPPRESSED, // Message is not suppressed.
|
||||||
|
MESSAGE_SUPPRESSED, // Message is suppressed for the first time (for this round)
|
||||||
|
MESSAGE_STILL_SUPPRESSED // Message is still suppressed (for this round)
|
||||||
|
} message_suppression_t;
|
||||||
|
|
||||||
|
class MessageStatsValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageStatsValue()
|
||||||
|
: m_lock(SPINLOCK_INIT)
|
||||||
|
, m_first_ms(time_monotonic_ms())
|
||||||
|
, m_last_ms(0)
|
||||||
|
, m_count(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
message_suppression_t update_suppression(const MXS_LOG_THROTTLING& t)
|
||||||
|
{
|
||||||
|
message_suppression_t rv = MESSAGE_NOT_SUPPRESSED;
|
||||||
|
|
||||||
|
uint64_t now_ms = time_monotonic_ms();
|
||||||
|
|
||||||
|
spinlock_acquire(&m_lock);
|
||||||
|
|
||||||
|
++m_count;
|
||||||
|
|
||||||
|
// Less that t.window_ms milliseconds since the message was logged
|
||||||
|
// the last time. May have to be throttled.
|
||||||
|
if (m_count < t.count)
|
||||||
|
{
|
||||||
|
// t.count times has not been reached, still ok to log.
|
||||||
|
}
|
||||||
|
else if (m_count == t.count)
|
||||||
|
{
|
||||||
|
// t.count times has been reached. Was it within the window?
|
||||||
|
if (now_ms - m_first_ms < t.window_ms)
|
||||||
|
{
|
||||||
|
// Within the window, suppress the message.
|
||||||
|
rv = MESSAGE_SUPPRESSED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not within the window, reset the situation.
|
||||||
|
|
||||||
|
// The flooding situation is analyzed window by window.
|
||||||
|
// That means that if there in each of two consequtive
|
||||||
|
// windows are not enough messages for throttling to take
|
||||||
|
// effect, but there would be if the window was placed at a
|
||||||
|
// slightly different position (e.g. starting in the middle
|
||||||
|
// of the first and ending in the middle of the second) it
|
||||||
|
// will go undetected and no throttling will be made.
|
||||||
|
// However, if that's the case, it was a spike so the
|
||||||
|
// flooding will stop anyway.
|
||||||
|
|
||||||
|
m_first_ms = now_ms;
|
||||||
|
m_count = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// In suppression mode.
|
||||||
|
if (now_ms - m_first_ms < (t.window_ms + t.suppress_ms))
|
||||||
|
{
|
||||||
|
// Still in the suppression window.
|
||||||
|
rv = MESSAGE_STILL_SUPPRESSED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We have exited the suppression window, reset the situation.
|
||||||
|
m_first_ms = now_ms;
|
||||||
|
m_count = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_ms = now_ms;
|
||||||
|
|
||||||
|
spinlock_release(&m_lock);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SPINLOCK m_lock;
|
||||||
|
uint64_t m_first_ms; /** The time when the error was logged the first time in this window. */
|
||||||
|
uint64_t m_last_ms; /** The time when the error was logged the last time. */
|
||||||
|
size_t m_count; /** How many times the error has been reported within this window. */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -317,6 +404,7 @@ class MessageStats
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef MessageStatsKey Key;
|
typedef MessageStatsKey Key;
|
||||||
|
typedef MessageStatsValue Value;
|
||||||
|
|
||||||
MessageStats(const MessageStats&) = delete;
|
MessageStats(const MessageStats&) = delete;
|
||||||
MessageStats& operator=(const MessageStats&) = delete;
|
MessageStats& operator=(const MessageStats&) = delete;
|
||||||
@ -325,7 +413,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LM_MESSAGE_STATS& get(const Key& key)
|
Value& get(const Key& key)
|
||||||
{
|
{
|
||||||
mxs::SpinLockGuard guard(m_lock);
|
mxs::SpinLockGuard guard(m_lock);
|
||||||
|
|
||||||
@ -333,13 +421,9 @@ public:
|
|||||||
|
|
||||||
if (i == m_registry.end())
|
if (i == m_registry.end())
|
||||||
{
|
{
|
||||||
LM_MESSAGE_STATS stats;
|
Value value;
|
||||||
spinlock_init(&stats.lock);
|
|
||||||
stats.first_ms = time_monotonic_ms();
|
|
||||||
stats.last_ms = 0;
|
|
||||||
stats.count = 0;
|
|
||||||
|
|
||||||
i = m_registry.insert(std::make_pair(key, stats)).first;
|
i = m_registry.insert(std::make_pair(key, value)).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i->second;
|
return i->second;
|
||||||
@ -347,7 +431,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
mxs::SpinLock m_lock;
|
mxs::SpinLock m_lock;
|
||||||
std::tr1::unordered_map<Key, LM_MESSAGE_STATS> m_registry;
|
std::tr1::unordered_map<Key, Value> m_registry;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2777,13 +2861,6 @@ static enum log_flush priority_to_flush(int priority)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum message_suppression
|
|
||||||
{
|
|
||||||
MESSAGE_NOT_SUPPRESSED, // Message is not suppressed.
|
|
||||||
MESSAGE_SUPPRESSED, // Message is suppressed for the first time (for this round)
|
|
||||||
MESSAGE_STILL_SUPPRESSED // Message is still suppressed (for this round)
|
|
||||||
} message_suppression_t;
|
|
||||||
|
|
||||||
static message_suppression_t message_status(const char* file, int line)
|
static message_suppression_t message_status(const char* file, int line)
|
||||||
{
|
{
|
||||||
message_suppression_t rv = MESSAGE_NOT_SUPPRESSED;
|
message_suppression_t rv = MESSAGE_NOT_SUPPRESSED;
|
||||||
@ -2796,65 +2873,9 @@ static message_suppression_t message_status(const char* file, int line)
|
|||||||
if ((t.count != 0) && (t.window_ms != 0) && (t.suppress_ms != 0))
|
if ((t.count != 0) && (t.window_ms != 0) && (t.suppress_ms != 0))
|
||||||
{
|
{
|
||||||
MessageStats::Key key(file, line);
|
MessageStats::Key key(file, line);
|
||||||
LM_MESSAGE_STATS& value = message_stats->get(key);
|
MessageStats::Value& value = message_stats->get(key);
|
||||||
|
|
||||||
uint64_t now_ms = time_monotonic_ms();
|
rv = value.update_suppression(t);
|
||||||
|
|
||||||
spinlock_acquire(&value.lock);
|
|
||||||
|
|
||||||
++value.count;
|
|
||||||
|
|
||||||
// Less that t.window_ms milliseconds since the message was logged
|
|
||||||
// the last time. May have to be throttled.
|
|
||||||
if (value.count < t.count)
|
|
||||||
{
|
|
||||||
// t.count times has not been reached, still ok to log.
|
|
||||||
}
|
|
||||||
else if (value.count == t.count)
|
|
||||||
{
|
|
||||||
// t.count times has been reached. Was it within the window?
|
|
||||||
if (now_ms - value.first_ms < t.window_ms)
|
|
||||||
{
|
|
||||||
// Within the window, suppress the message.
|
|
||||||
rv = MESSAGE_SUPPRESSED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Not within the window, reset the situation.
|
|
||||||
|
|
||||||
// The flooding situation is analyzed window by window.
|
|
||||||
// That means that if there in each of two consequtive
|
|
||||||
// windows are not enough messages for throttling to take
|
|
||||||
// effect, but there would be if the window was placed at a
|
|
||||||
// slightly different position (e.g. starting in the middle
|
|
||||||
// of the first and ending in the middle of the second) it
|
|
||||||
// will go undetected and no throttling will be made.
|
|
||||||
// However, if that's the case, it was a spike so the
|
|
||||||
// flooding will stop anyway.
|
|
||||||
|
|
||||||
value.first_ms = now_ms;
|
|
||||||
value.count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// In suppression mode.
|
|
||||||
if (now_ms - value.first_ms < (t.window_ms + t.suppress_ms))
|
|
||||||
{
|
|
||||||
// Still in the suppression window.
|
|
||||||
rv = MESSAGE_STILL_SUPPRESSED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We have exited the suppression window, reset the situation.
|
|
||||||
value.first_ms = now_ms;
|
|
||||||
value.count = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value.last_ms = now_ms;
|
|
||||||
|
|
||||||
spinlock_release(&value.lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
|
|||||||
Reference in New Issue
Block a user