LCOV - code coverage report
Current view: top level - foo/src - logger.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 186 238 78.2 %
Date: 2026-04-01 09:29:43 Functions: 37 48 77.1 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2026 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include <cstdio>
      19             : #include <cstring>
      20             : #include <cerrno>
      21             : #include <ctime>
      22             : 
      23             : #include "client/jami_signal.h"
      24             : 
      25             : #include <fmt/core.h>
      26             : #include <fmt/format.h>
      27             : #include <fmt/compile.h>
      28             : 
      29             : #ifdef _MSC_VER
      30             : #include <sys_time.h>
      31             : #else
      32             : #include <sys/time.h>
      33             : #endif
      34             : 
      35             : #include <atomic>
      36             : #include <condition_variable>
      37             : #include <fstream>
      38             : #include <string>
      39             : #include <mutex>
      40             : #include <thread>
      41             : #include <list>
      42             : 
      43             : #include "logger.h"
      44             : 
      45             : #include <stdio.h>
      46             : #ifdef __linux__
      47             : #include <unistd.h>
      48             : #include <syslog.h>
      49             : #include <sys/syscall.h>
      50             : #endif // __linux__
      51             : 
      52             : #ifdef __ANDROID__
      53             : #ifndef APP_NAME
      54             : #define APP_NAME "libjami"
      55             : #endif /* APP_NAME */
      56             : #endif
      57             : 
      58             : #define END_COLOR "\033[0m"
      59             : 
      60             : #ifndef _WIN32
      61             : #define RED    "\033[22;31m"
      62             : #define YELLOW "\033[01;33m"
      63             : #define CYAN   "\033[22;36m"
      64             : #else
      65             : #define FOREGROUND_WHITE 0x000f
      66             : #define RED              FOREGROUND_RED + 0x0008
      67             : #define YELLOW           FOREGROUND_RED + FOREGROUND_GREEN + 0x0008
      68             : #define CYAN             FOREGROUND_BLUE + FOREGROUND_GREEN + 0x0008
      69             : #define LIGHT_GREEN      FOREGROUND_GREEN + 0x0008
      70             : #endif // _WIN32
      71             : 
      72             : #define LOGFILE "jami"
      73             : 
      74             : namespace jami {
      75             : 
      76             : static constexpr auto ENDL = '\n';
      77             : 
      78             : #ifndef __GLIBC__
      79             : static const char*
      80             : check_error(int result, char* buffer)
      81             : {
      82             :     switch (result) {
      83             :     case 0:
      84             :         return buffer;
      85             : 
      86             :     case ERANGE: /* should never happen */
      87             :         return "unknown (too big to display)";
      88             : 
      89             :     default:
      90             :         return "unknown (invalid error number)";
      91             :     }
      92             : }
      93             : 
      94             : static const char*
      95             : check_error(char* result, char*)
      96             : {
      97             :     return result;
      98             : }
      99             : #endif
     100             : 
     101             : void
     102           0 : strErr()
     103             : {
     104             : #ifdef __GLIBC__
     105           0 :     JAMI_ERR("%m");
     106             : #else
     107             :     char buf[1000];
     108             :     JAMI_ERR("%s", check_error(strerror_r(errno, buf, sizeof(buf)), buf));
     109             : #endif
     110           0 : }
     111             : 
     112             : // extract the last component of a pathname (extract a filename from its dirname)
     113             : static constexpr std::string_view
     114      553793 : stripDirName(std::string_view path)
     115             : {
     116      553793 :     if (!path.empty()) {
     117      553726 :         size_t pos = path.find_last_of("/\\");
     118      553874 :         if (pos != std::string_view::npos)
     119      196563 :             return path.substr(pos + 1);
     120             :     }
     121      357309 :     return path;
     122             : }
     123             : 
     124             : static std::string
     125      276600 : formatHeader(std::string_view file, unsigned line)
     126             : {
     127             : #ifdef __linux__
     128      276600 :     auto tid = syscall(__NR_gettid) & 0xffff;
     129             : #else
     130             :     auto tid = std::this_thread::get_id();
     131             : #endif // __linux__
     132             : 
     133             :     unsigned int secs, milli;
     134             :     struct timeval tv;
     135      277107 :     if (!gettimeofday(&tv, NULL)) {
     136      277096 :         secs = tv.tv_sec;
     137      277096 :         milli = tv.tv_usec / 1000; // suppose that milli < 1000
     138             :     } else {
     139           0 :         secs = time(NULL);
     140           0 :         milli = 0;
     141             :     }
     142             : 
     143      277096 :     if (!file.empty()) {
     144      831024 :         return fmt::format(FMT_COMPILE("[{: >3d}.{:0<3d}|{: >4}|{: <24s}:{: <4d}] "),
     145             :                            secs,
     146             :                            milli,
     147             :                            tid,
     148      553688 :                            stripDirName(file),
     149      276647 :                            line);
     150             :     } else {
     151           0 :         return fmt::format(FMT_COMPILE("[{: >3d}.{:0<3d}|{: >4}] "), secs, milli, tid);
     152             :     }
     153             : }
     154             : 
     155             : static std::string
     156       23270 : formatPrintfArgs(const char* format, va_list ap)
     157             : {
     158       23270 :     std::string ret;
     159             :     /* A good guess of what we might encounter. */
     160             :     static constexpr size_t default_buf_size = 80;
     161             : 
     162       23266 :     ret.resize(default_buf_size);
     163             : 
     164             :     /* Necessary if we don't have enough space in buf. */
     165             :     va_list cp;
     166       23255 :     va_copy(cp, ap);
     167             : 
     168       23255 :     int size = vsnprintf(ret.data(), ret.size(), format, ap);
     169             : 
     170             :     /* Not enough space?  Well try again. */
     171       23252 :     if ((size_t) size >= ret.size()) {
     172        1538 :         ret.resize(size + 1);
     173        1538 :         vsnprintf((char*) ret.data(), ret.size(), format, cp);
     174             :     }
     175             : 
     176       23273 :     ret.resize(size);
     177             : 
     178       23268 :     va_end(cp);
     179             : 
     180       46536 :     return ret;
     181           0 : }
     182             : 
     183             : struct Logger::Msg
     184             : {
     185             :     Msg() = delete;
     186             : 
     187      253630 :     Msg(int level, std::string_view file, unsigned line, bool linefeed, std::string_view tag, std::string&& message)
     188      253630 :         : file_(stripDirName(file))
     189      253675 :         , line_(line)
     190      253675 :         , tag_(tag)
     191      253675 :         , payload_(std::move(message))
     192      253285 :         , level_(level)
     193      253285 :         , linefeed_(linefeed)
     194      253285 :         , header_(formatHeader(file_, line_))
     195      253419 :     {}
     196             : 
     197       23263 :     Msg(int level, std::string_view file, unsigned line, bool linefeed, std::string_view tag, const char* fmt, va_list ap)
     198       23263 :         : file_(stripDirName(file))
     199       23270 :         , line_(line)
     200       23270 :         , tag_(tag)
     201       23270 :         , payload_(formatPrintfArgs(fmt, ap))
     202       23268 :         , level_(level)
     203       23268 :         , linefeed_(linefeed)
     204       23268 :         , header_(formatHeader(file_, line_))
     205       23259 :     {}
     206             : 
     207         850 :     Msg(Msg&& other) = default;
     208      276201 :     Msg& operator=(Msg&& other) = default;
     209             : 
     210             :     std::string_view file_;
     211             :     unsigned line_;
     212             :     std::string_view tag_;
     213             :     std::string payload_;
     214             :     int level_;
     215             :     bool linefeed_;
     216             :     std::string header_;
     217             : };
     218             : 
     219             : class Logger::Handler
     220             : {
     221             : public:
     222           0 :     virtual ~Handler() = default;
     223             : 
     224             :     virtual void consume(const Msg& msg) = 0;
     225             : 
     226         918 :     virtual void enable(bool en) { enabled_.store(en, std::memory_order_relaxed); }
     227     1385645 :     bool isEnable() { return enabled_.load(std::memory_order_relaxed); }
     228             : 
     229             : protected:
     230             :     std::atomic_bool enabled_ {false};
     231             : };
     232             : 
     233             : class ConsoleLog final : public Logger::Handler
     234             : {
     235             : public:
     236          40 :     ConsoleLog() = default;
     237             : 
     238             : #ifdef _WIN32
     239             :     void printLogImpl(const Logger::Msg& msg, bool with_color)
     240             :     {
     241             :         // If we are using Visual Studio, we can use OutputDebugString to print
     242             :         // to the "Output" window. Otherwise, we just use fputs to stderr.
     243             :         static std::function<void(const char* str)> fputsFunc = [](const char* str) {
     244             :             fputs(str, stderr);
     245             :         };
     246             :         static std::function<void(const char* str)> outputDebugStringFunc = [](const char* str) {
     247             :             OutputDebugStringA(str);
     248             :         };
     249             :         static std::function<void()> putcFunc = []() {
     250             :             putc(ENDL, stderr);
     251             :         };
     252             :         // These next two functions will be used to print the message and line ending.
     253             :         static auto printFunc = IsDebuggerPresent() ? outputDebugStringFunc : fputsFunc;
     254             :         static auto endlFunc = IsDebuggerPresent() ? []() { OutputDebugStringA("\n"); } : putcFunc;
     255             : 
     256             :         WORD saved_attributes;
     257             :         static HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
     258             :         auto& header = msg.header_;
     259             :         if (with_color) {
     260             :             static WORD color_header = CYAN;
     261             :             WORD color_prefix = LIGHT_GREEN;
     262             :             CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
     263             : 
     264             :             switch (msg.level_) {
     265             :             case LOG_ERR:
     266             :                 color_prefix = RED;
     267             :                 break;
     268             : 
     269             :             case LOG_WARNING:
     270             :                 color_prefix = YELLOW;
     271             :                 break;
     272             :             }
     273             : 
     274             :             GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
     275             :             saved_attributes = consoleInfo.wAttributes;
     276             :             SetConsoleTextAttribute(hConsole, color_header);
     277             : 
     278             :             printFunc(header.c_str());
     279             : 
     280             :             SetConsoleTextAttribute(hConsole, saved_attributes);
     281             :             SetConsoleTextAttribute(hConsole, color_prefix);
     282             :         } else {
     283             :             printFunc(header.c_str());
     284             :         }
     285             : 
     286             :         if (!msg.tag_.empty())
     287             :             printFunc(msg.tag_.data());
     288             :         printFunc(msg.payload_.c_str());
     289             : 
     290             :         if (msg.linefeed_) {
     291             :             endlFunc();
     292             :         }
     293             : 
     294             :         if (with_color) {
     295             :             SetConsoleTextAttribute(hConsole, saved_attributes);
     296             :         }
     297             :     }
     298             : #else
     299      277051 :     void printLogImpl(const Logger::Msg& msg, bool with_color)
     300             :     {
     301      277051 :         if (with_color) {
     302      277051 :             constexpr const char* color_header = CYAN;
     303      277051 :             const char* color_prefix = "";
     304             : 
     305      277051 :             switch (msg.level_) {
     306        6443 :             case LOG_ERR:
     307        6443 :                 color_prefix = RED;
     308        6443 :                 break;
     309             : 
     310       31978 :             case LOG_WARNING:
     311       31978 :                 color_prefix = YELLOW;
     312       31978 :                 break;
     313             :             }
     314             : 
     315      277051 :             fputs(color_header, stderr);
     316      277051 :             fwrite(msg.header_.c_str(), 1, msg.header_.size(), stderr);
     317      277051 :             fputs(END_COLOR, stderr);
     318      277051 :             fputs(color_prefix, stderr);
     319             :         } else {
     320           0 :             fwrite(msg.header_.c_str(), 1, msg.header_.size(), stderr);
     321             :         }
     322             : 
     323      277051 :         if (!msg.tag_.empty())
     324       46640 :             fwrite(msg.tag_.data(), 1, msg.tag_.size(), stderr);
     325      277051 :         fputs(msg.payload_.c_str(), stderr);
     326             : 
     327      277051 :         if (with_color) {
     328      277051 :             fputs(END_COLOR, stderr);
     329             :         }
     330      277051 :         if (msg.linefeed_) {
     331      277047 :             putc(ENDL, stderr);
     332             :         }
     333      277051 :     }
     334             : #endif /* _WIN32 */
     335             : 
     336             :     bool withColor_ = !(getenv("NO_COLOR") || getenv("NO_COLORS") || getenv("NO_COLOUR") || getenv("NO_COLOURS"));
     337             : 
     338      277051 :     void consume(const Logger::Msg& msg) override { printLogImpl(msg, withColor_); }
     339             : };
     340             : 
     341             : class SysLog final : public Logger::Handler
     342             : {
     343             : public:
     344          40 :     SysLog()
     345          40 :     {
     346             : #ifdef _WIN32
     347             :         ::openlog(LOGFILE, WINLOG_PID, WINLOG_MAIL);
     348             : #else
     349             : #ifndef __ANDROID__
     350          40 :         ::openlog(LOGFILE, LOG_NDELAY, LOG_USER);
     351             : #endif
     352             : #endif /* _WIN32 */
     353          40 :     }
     354             : 
     355           0 :     void consume(const Logger::Msg& msg) override
     356             :     {
     357             : #ifdef __ANDROID__
     358             :         __android_log_write(msg.level_, msg.file_.data(), msg.payload_.c_str());
     359             : #else
     360           0 :         ::syslog(msg.level_,
     361             :                  "%.*s%.*s",
     362           0 :                  (int) msg.tag_.size(),
     363             :                  msg.tag_.data(),
     364           0 :                  (int) msg.payload_.size(),
     365             :                  msg.payload_.data());
     366             : #endif
     367           0 :     }
     368             : };
     369             : 
     370             : class MonitorLog final : public Logger::Handler
     371             : {
     372             : public:
     373          40 :     MonitorLog() = default;
     374             : 
     375           0 :     void consume(const Logger::Msg& msg) override
     376             :     {
     377           0 :         auto message = msg.header_ + msg.payload_;
     378           0 :         emitSignal<libjami::ConfigurationSignal::MessageSend>(message);
     379           0 :     }
     380             : };
     381             : 
     382             : class FileLog final : public Logger::Handler
     383             : {
     384             : public:
     385          40 :     FileLog() = default;
     386             : 
     387         306 :     void setFile(const std::string& path)
     388             :     {
     389         306 :         std::lock_guard lk(mtx_);
     390         306 :         if (file_.is_open())
     391           0 :             file_.close();
     392             : 
     393         306 :         if (not path.empty()) {
     394           0 :             file_.open(path, std::ofstream::out | std::ofstream::app);
     395           0 :             if (file_)
     396           0 :                 enable(true);
     397             :             else
     398           0 :                 enable(false);
     399             :         } else {
     400         306 :             enable(false);
     401             :         }
     402         306 :     }
     403             : 
     404           0 :     void consume(const Logger::Msg& msg) override
     405             :     {
     406           0 :         std::lock_guard lk(mtx_);
     407           0 :         if (file_.is_open()) {
     408           0 :             file_ << msg.header_ << msg.tag_ << msg.payload_;
     409           0 :             if (msg.linefeed_)
     410           0 :                 file_ << ENDL;
     411           0 :             file_.flush();
     412             :         }
     413           0 :     }
     414             : 
     415             : private:
     416             :     std::mutex mtx_;
     417             :     std::ofstream file_;
     418             : };
     419             : 
     420             : class LogDispatcher final
     421             : {
     422             : public:
     423      554662 :     static LogDispatcher& instance()
     424             :     {
     425      554662 :         static LogDispatcher* self = new LogDispatcher();
     426      554645 :         return *self;
     427             :     }
     428             : 
     429      276601 :     void log(Logger::Msg&& msg)
     430             :     {
     431             :         {
     432      276601 :             std::lock_guard lk(mtx_);
     433      277122 :             if (!running_)
     434          71 :                 return;
     435      277051 :             if (!recycleQueue_.empty()) {
     436      276201 :                 msgQueue_.splice(msgQueue_.end(), recycleQueue_, recycleQueue_.begin());
     437      276201 :                 msgQueue_.back() = std::move(msg);
     438             :             } else {
     439         850 :                 msgQueue_.emplace_back(std::move(msg));
     440             :             }
     441      277122 :         }
     442      277051 :         cv_.notify_one();
     443             :     }
     444             : 
     445         306 :     void stop()
     446             :     {
     447             :         {
     448         306 :             std::lock_guard lk(mtx_);
     449         306 :             if (!running_)
     450           0 :                 return;
     451         306 :             running_ = false;
     452         306 :         }
     453         306 :         cv_.notify_all();
     454         306 :         if (thread_.joinable())
     455         306 :             thread_.join();
     456         306 :         recycleQueue_.splice(recycleQueue_.end(), std::move(msgQueue_));
     457             :     }
     458             : 
     459             :     ConsoleLog consoleLog;
     460             :     SysLog sysLog;
     461             :     MonitorLog monitorLog;
     462             :     FileLog fileLog;
     463             : 
     464         306 :     void enableFileLog(const std::string& path)
     465             :     {
     466         306 :         fileLog.setFile(path);
     467         306 :         checkStatus();
     468         306 :     }
     469             : 
     470         306 :     void enableConsoleLog(bool en)
     471             :     {
     472         306 :         consoleLog.enable(en);
     473         306 :         checkStatus();
     474             : #ifdef _WIN32
     475             :         static WORD original_attributes;
     476             :         if (en) {
     477             :             if (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) {
     478             :                 FILE *fpstdout = stdout, *fpstderr = stderr;
     479             :                 freopen_s(&fpstdout, "CONOUT$", "w", stdout);
     480             :                 freopen_s(&fpstderr, "CONOUT$", "w", stderr);
     481             :                 // Save the original state of the console window(in case AttachConsole worked).
     482             :                 CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
     483             :                 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo);
     484             :                 original_attributes = consoleInfo.wAttributes;
     485             :                 SetConsoleCP(CP_UTF8);
     486             :                 SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
     487             :             }
     488             :         } else {
     489             :             // Restore the original state of the console window in case we attached.
     490             :             SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), original_attributes);
     491             :             FreeConsole();
     492             :         }
     493             : #endif
     494         306 :     }
     495             : 
     496         306 :     void enableSysLog(bool en)
     497             :     {
     498         306 :         sysLog.enable(en);
     499         306 :         checkStatus();
     500         306 :     }
     501             : 
     502           0 :     void enableMonitorLog(bool en)
     503             :     {
     504           0 :         monitorLog.enable(en);
     505           0 :         checkStatus();
     506           0 :     }
     507             : 
     508      277913 :     bool isEnabled()
     509             :     {
     510      277913 :         return consoleLog.isEnable() || sysLog.isEnable() || monitorLog.isEnable() || fileLog.isEnable();
     511             :     }
     512             : 
     513             : private:
     514          40 :     LogDispatcher() = default;
     515             : 
     516         918 :     void checkStatus()
     517             :     {
     518         918 :         bool en = isEnabled();
     519         918 :         std::thread t;
     520             :         {
     521         918 :             std::lock_guard lk(mtx_);
     522         918 :             if (en && !running_) {
     523         306 :                 running_ = true;
     524         306 :                 thread_ = std::thread(&LogDispatcher::loop, this);
     525         612 :             } else if (!en && running_) {
     526           0 :                 running_ = false;
     527           0 :                 t = std::move(thread_);
     528             :             }
     529         918 :         }
     530         918 :         if (t.joinable()) {
     531           0 :             cv_.notify_all();
     532           0 :             t.join();
     533           0 :             std::lock_guard lk(mtx_);
     534           0 :             recycleQueue_.splice(recycleQueue_.end(), std::move(msgQueue_));
     535           0 :         }
     536         918 :     }
     537             : 
     538         306 :     void loop()
     539             :     {
     540         306 :         std::unique_lock lk(mtx_);
     541      229858 :         while (running_) {
     542      618385 :             cv_.wait(lk, [this] { return not msgQueue_.empty() or not running_; });
     543      229552 :             auto local = std::move(msgQueue_);
     544      229552 :             lk.unlock();
     545      506603 :             for (auto& msg : local) {
     546      277051 :                 if (sysLog.isEnable())
     547           0 :                     sysLog.consume(msg);
     548      277051 :                 if (monitorLog.isEnable())
     549           0 :                     monitorLog.consume(msg);
     550      277051 :                 if (consoleLog.isEnable())
     551      277051 :                     consoleLog.consume(msg);
     552      277051 :                 if (fileLog.isEnable())
     553           0 :                     fileLog.consume(msg);
     554             : 
     555      277051 :                 msg.payload_ = {};
     556      277051 :                 msg.header_ = {};
     557             :             }
     558      229552 :             lk.lock();
     559      229552 :             if (recycleQueue_.size() < 128)
     560      229523 :                 recycleQueue_.splice(recycleQueue_.end(), local);
     561      229552 :         }
     562         306 :     }
     563             : 
     564             :     std::mutex mtx_;
     565             :     std::condition_variable cv_;
     566             :     std::list<Logger::Msg> msgQueue_;
     567             :     std::list<Logger::Msg> recycleQueue_;
     568             :     bool running_ {false};
     569             :     std::thread thread_;
     570             : };
     571             : 
     572             : void
     573         306 : Logger::setConsoleLog(bool en)
     574             : {
     575         306 :     LogDispatcher::instance().enableConsoleLog(en);
     576         306 : }
     577             : 
     578             : void
     579         306 : Logger::setSysLog(bool en)
     580             : {
     581         306 :     LogDispatcher::instance().enableSysLog(en);
     582         306 : }
     583             : 
     584             : void
     585           0 : Logger::setMonitorLog(bool en)
     586             : {
     587           0 :     LogDispatcher::instance().enableMonitorLog(en);
     588           0 : }
     589             : 
     590             : void
     591           0 : Logger::setFileLog(const std::string& path)
     592             : {
     593           0 :     LogDispatcher::instance().enableFileLog(path);
     594           0 : }
     595             : 
     596             : LIBJAMI_PUBLIC void
     597       23269 : Logger::log(int level, const char* file, unsigned line, bool linefeed, const char* fmt, ...)
     598             : {
     599             :     va_list ap;
     600       23269 :     va_start(ap, fmt);
     601       23269 :     vlog(level, file, line, linefeed, fmt, ap);
     602       23276 :     va_end(ap);
     603       23276 : }
     604             : 
     605             : static std::atomic_bool debugEnabled_ {false};
     606             : 
     607             : void
     608         306 : Logger::setDebugMode(bool enable)
     609             : {
     610         306 :     debugEnabled_.store(enable, std::memory_order_relaxed);
     611         306 : }
     612             : 
     613             : bool
     614       77823 : Logger::debugEnabled()
     615             : {
     616       77823 :     return debugEnabled_.load(std::memory_order_relaxed);
     617             : }
     618             : 
     619             : void
     620       23270 : Logger::vlog(int level, const char* file, unsigned line, bool linefeed, const char* fmt, va_list ap)
     621             : {
     622       23270 :     if (level < LOG_WARNING and not debugEnabled_.load(std::memory_order_relaxed)) {
     623           1 :         return;
     624             :     }
     625             : 
     626       23270 :     if (!LogDispatcher::instance().isEnabled()) {
     627           1 :         return;
     628             :     }
     629             : 
     630             :     /* Timestamp is generated here. */
     631       23268 :     Msg msg(level, file, line, linefeed, {}, fmt, ap);
     632       23260 :     LogDispatcher::instance().log(std::move(msg));
     633       23276 : }
     634             : 
     635             : void
     636      253739 : Logger::write(int level, std::string_view file, unsigned line, bool linefeed, std::string_view tag, std::string&& message)
     637             : {
     638      253739 :     if (!LogDispatcher::instance().isEnabled()) {
     639           2 :         return;
     640             :     }
     641             :     /* Timestamp is generated here. */
     642      253660 :     Msg msg(level, file, line, linefeed, tag, std::move(message));
     643      253408 :     LogDispatcher::instance().log(std::move(msg));
     644      253833 : }
     645             : 
     646             : void
     647         306 : Logger::fini()
     648             : {
     649             :     // Force close on file and join thread
     650         306 :     LogDispatcher::instance().enableFileLog({});
     651         306 :     LogDispatcher::instance().stop();
     652             : 
     653             : #ifdef _WIN32
     654             :     Logger::setConsoleLog(false);
     655             : #endif /* _WIN32 */
     656         306 : }
     657             : 
     658             : } // namespace jami

Generated by: LCOV version 1.14