diff --git a/libtransmission/log.cc b/libtransmission/log.cc index c3a53351b..54c4b7531 100644 --- a/libtransmission/log.cc +++ b/libtransmission/log.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -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, 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{}; + 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; }