feat: stop logging repetitive warnings (#2756)

Fixes #295
This commit is contained in:
Charles Kerr 2022-03-11 16:06:58 -06:00 committed by GitHub
parent d2f8e1d4e0
commit 749bb591fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 132 additions and 99 deletions

View File

@ -6,6 +6,7 @@
#include <cerrno>
#include <cstdarg>
#include <cstdio>
#include <map>
#include <mutex>
#include <event2/buffer.h>
@ -135,6 +136,101 @@ char* tr_logGetTimeStr(char* buf, size_t buflen)
****
***/
void logAddImpl(
[[maybe_unused]] char const* file,
[[maybe_unused]] int line,
tr_log_level level,
[[maybe_unused]] std::string_view name,
std::string_view msg)
{
if (std::empty(msg))
{
return;
}
auto const lock = std::lock_guard(message_mutex_);
#ifdef _WIN32
OutputDebugStringA(tr_strvJoin(msg, "\r\n").c_str());
#elif defined(__ANDROID__)
int prio;
switch (level)
{
case TR_LOG_CRITICAL:
prio = ANDROID_LOG_FATAL;
break;
case TR_LOG_ERROR:
prio = ANDROID_LOG_ERROR;
break;
case TR_LOG_WARN:
prio = ANDROID_LOG_WARN;
break;
case TR_LOG_INFO:
prio = ANDROID_LOG_INFO;
break;
case TR_LOG_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
case TR_LOG_TRACE:
prio = ANDROID_LOG_VERBOSE;
}
#ifdef NDEBUG
__android_log_print(prio, "transmission", "%" TR_PRIsv, TR_PRIsv_ARG(msg));
#else
__android_log_print(prio, "transmission", "[%s:%d] %" TR_PRIsv, file, line, TR_PRIsv_ARG(msg));
#endif
#else
if (tr_logGetQueueEnabled())
{
auto* const newmsg = tr_new0(tr_log_message, 1);
newmsg->level = level;
newmsg->when = tr_time();
newmsg->message = tr_strvDup(msg);
newmsg->file = file;
newmsg->line = line;
newmsg->name = tr_strvDup(name);
*myQueueTail = newmsg;
myQueueTail = &newmsg->next;
++myQueueLength;
if (myQueueLength > TR_LOG_MAX_QUEUE_LENGTH)
{
tr_log_message* old = myQueue;
myQueue = old->next;
old->next = nullptr;
tr_logFreeQueue(old);
--myQueueLength;
TR_ASSERT(myQueueLength == TR_LOG_MAX_QUEUE_LENGTH);
}
}
else
{
char timestr[64];
tr_sys_file_t fp = tr_logGetFile();
if (fp == TR_BAD_SYS_FILE)
{
fp = tr_sys_file_get_std(TR_STD_SYS_FILE_ERR, nullptr);
}
tr_logGetTimeStr(timestr, sizeof(timestr));
auto const out = !std::empty(name) ? tr_strvJoin("["sv, timestr, "] "sv, name, ": "sv, msg) :
tr_strvJoin("["sv, timestr, "] "sv, msg);
tr_sys_file_write_line(fp, out, nullptr);
tr_sys_file_flush(fp, nullptr);
}
#endif
}
void tr_logAddMessage(
[[maybe_unused]] char const* file,
[[maybe_unused]] int line,
@ -143,117 +239,54 @@ void tr_logAddMessage(
char const* fmt,
...)
{
// message logging shouldn't affect errno
int const err = errno;
// skip unwanted messages
if (!tr_logLevelIsActive(level))
{
return;
}
int const err = errno; /* message logging shouldn't affect errno */
char buf[1024];
va_list ap;
auto const lock = std::lock_guard(message_mutex_);
/* build the text message */
*buf = '\0';
va_start(ap, fmt);
int const buf_len = evutil_vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (buf_len < 0)
{
errno = err;
return;
}
#ifdef _WIN32
auto const lock = std::lock_guard(message_mutex_);
if ((size_t)buf_len < sizeof(buf) - 3)
// don't log the same warning ad infinitum.
// it's not useful after some point.
bool last_one = false;
if (level == TR_LOG_CRITICAL || level == TR_LOG_ERROR || level == TR_LOG_WARN)
{
buf[buf_len + 0] = '\r';
buf[buf_len + 1] = '\n';
buf[buf_len + 2] = '\0';
OutputDebugStringA(buf);
buf[buf_len + 0] = '\0';
}
else
{
OutputDebugStringA(buf);
}
static auto constexpr MaxRepeat = size_t{ 30 };
static auto counts = new std::map<std::pair<char const*, int>, size_t>{};
#elif defined(__ANDROID__)
int prio;
switch (level)
{
case TR_LOG_ERROR:
prio = ANDROID_LOG_ERROR;
break;
case TR_LOG_INFO:
prio = ANDROID_LOG_INFO;
break;
case TR_LOG_DEBUG:
prio = ANDROID_LOG_DEBUG;
break;
default:
prio = ANDROID_LOG_VERBOSE;
}
#ifdef NDEBUG
__android_log_print(prio, "transmission", "%s", buf);
#else
__android_log_print(prio, "transmission", "[%s:%d] %s", file, line, buf);
#endif
#else
if (!tr_str_is_empty(buf))
{
if (tr_logGetQueueEnabled())
auto& count = (*counts)[std::make_pair(file, line)];
++count;
last_one = count == MaxRepeat;
if (count > MaxRepeat)
{
auto* const newmsg = tr_new0(tr_log_message, 1);
newmsg->level = level;
newmsg->when = tr_time();
newmsg->message = tr_strndup(buf, buf_len);
newmsg->file = file;
newmsg->line = line;
newmsg->name = tr_strvDup(name);
*myQueueTail = newmsg;
myQueueTail = &newmsg->next;
++myQueueLength;
if (myQueueLength > TR_LOG_MAX_QUEUE_LENGTH)
{
tr_log_message* old = myQueue;
myQueue = old->next;
old->next = nullptr;
tr_logFreeQueue(old);
--myQueueLength;
TR_ASSERT(myQueueLength == TR_LOG_MAX_QUEUE_LENGTH);
}
}
else
{
char timestr[64];
tr_sys_file_t fp = tr_logGetFile();
if (fp == TR_BAD_SYS_FILE)
{
fp = tr_sys_file_get_std(TR_STD_SYS_FILE_ERR, nullptr);
}
tr_logGetTimeStr(timestr, sizeof(timestr));
auto const out = !std::empty(name) ? tr_strvJoin("["sv, timestr, "] "sv, name, ": "sv, buf) :
tr_strvJoin("["sv, timestr, "] "sv, buf);
tr_sys_file_write_line(fp, out, nullptr);
tr_sys_file_flush(fp, nullptr);
errno = err;
return;
}
}
#endif
// build the message
auto buf = std::array<char, 2048>{};
va_list ap;
va_start(ap, fmt);
int const buf_len = evutil_vsnprintf(std::data(buf), std::size(buf), fmt, ap);
va_end(ap);
if (buf_len <= 0)
{
errno = err;
return;
}
// log the messages
logAddImpl(file, line, level, name, std::data(buf));
if (last_one)
{
logAddImpl(file, line, level, "", _("Too many messages like this! I won't log this message anymore this session."));
}
errno = err;
}