~aleteoryx/muditaos

8d96f2d3543c2bea59610e6b65edc842a6b2d42b — Lefucjusz 2 years ago ace3f5e
[MOS-993] Logger bug fixes and optimizations

Fixes and optimizations in logger:

* fixed possible buffer overflow when logging
logs over line buffer size;
* reduced max log line length to 2048;
* moved pubsetbuf before file opening;
* log file stream buffer created once
in logger ctor;
* updatet UTs;
* additional minor cleanup.
M module-cellular/modem/mux/CellularMux.cpp => module-cellular/modem/mux/CellularMux.cpp +3 -6
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CellularMux.h"


@@ 554,7 554,6 @@ void CellularMux::processData(bsp::cellular::CellularDMAResultStruct &result)
            LOG_DEBUG("DMA buffer full");
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivedAfterFull:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivedAndIdle:
            inst->processData(result);
            break;


@@ 562,9 561,7 @@ void CellularMux::processData(bsp::cellular::CellularDMAResultStruct &result)
            LOG_DEBUG("DMA uninitialized");
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivingNotStarted:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::TransmittingNotStarted:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::CMUXFrameError:
            LOG_DEBUG("CellularResult Error: %s", c_str(result.resultCode));
            inst->processError(result);


@@ 792,7 789,7 @@ void CellularMux::closeChannels()
bool CellularMux::searchATCommandResponse(const std::vector<std::string> &response,
                                          const std::string &str,
                                          size_t numberOfExpectedTokens,
                                          logger_level level)
                                          LoggerLevel level)
{
    const size_t numberOfTokens = response.size();
    if (searchForString(response, str) && (numberOfExpectedTokens == 0 || numberOfTokens == numberOfExpectedTokens)) {


@@ 838,7 835,7 @@ const std::unique_ptr<ATParser> &CellularMux::getParser() const
    return parser;
}

bool CellularMux::checkATCommandPrompt(const std::vector<std::string> &response, logger_level level)
bool CellularMux::checkATCommandPrompt(const std::vector<std::string> &response, LoggerLevel level)
{
    return searchATCommandResponse(response, ">", 0, level);
}

M module-cellular/modem/mux/CellularMux.h => module-cellular/modem/mux/CellularMux.h +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 323,7 323,7 @@ class CellularMux
    bool searchATCommandResponse(const std::vector<std::string> &response,
                                 const std::string &str,
                                 size_t numberOfExpectedTokens,
                                 logger_level level);
                                 LoggerLevel level);
    bool searchForString(const std::vector<std::string> &response, const std::string &str);

    /// @brief It is serching the resposne for ">" string


@@ 333,7 333,7 @@ class CellularMux
    /// @param response - tokenized resposne
    /// @param level - determine how the errors are logged
    /// @return true - str string is found, false - otherwise
    bool checkATCommandPrompt(const std::vector<std::string> &response, logger_level level = LOGERROR);
    bool checkATCommandPrompt(const std::vector<std::string> &response, LoggerLevel level = LOGERROR);

    void selectAntenna(bsp::cellular::antenna antenna);
    [[nodiscard]] bsp::cellular::antenna getAntenna();

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +118 -91
@@ 23,29 23,29 @@ namespace Log
        return stream;
    }

    Logger::Logger() : buffer{}, rotator{".log"}
    Logger::Logger() : rotator{".log"}, streamBuffer{std::make_unique<char[]>(streamBufferSize)}
    {
        filtered = {
            {"ApplicationManager", logger_level::LOGINFO},
            {"CellularMux", logger_level::LOGINFO},
            {"ApplicationManager", LoggerLevel::LOGINFO},
            {"CellularMux", LoggerLevel::LOGINFO},
#if (!LOG_SENSITIVE_DATA_ENABLED)
            {"ServiceCellular", logger_level::LOGINFO},
            {"ServiceCellular", LoggerLevel::LOGINFO},
#endif
            {"ServiceAntenna", logger_level::LOGERROR},
            {"ServiceAudio", logger_level::LOGINFO},
            {"ServiceBluetooth", logger_level::LOGINFO},
            {"ServiceBluetooth_w1", logger_level::LOGINFO},
            {"ServiceFota", logger_level::LOGINFO},
            {"ServiceEink", logger_level::LOGINFO},
            {"ServiceDB", logger_level::LOGINFO},
            {CRIT_STR, logger_level::LOGTRACE},
            {IRQ_STR, logger_level::LOGTRACE},
            {"FileIndexer", logger_level::LOGINFO},
            {"EventManager", logger_level::LOGINFO}
            {"ServiceAntenna", LoggerLevel::LOGERROR},
            {"ServiceAudio", LoggerLevel::LOGINFO},
            {"ServiceBluetooth", LoggerLevel::LOGINFO},
            {"ServiceBluetooth_w1", LoggerLevel::LOGINFO},
            {"ServiceFota", LoggerLevel::LOGINFO},
            {"ServiceEink", LoggerLevel::LOGINFO},
            {"ServiceDB", LoggerLevel::LOGINFO},
            {CRIT_STR, LoggerLevel::LOGTRACE},
            {IRQ_STR, LoggerLevel::LOGTRACE},
            {"FileIndexer", LoggerLevel::LOGINFO},
            {"EventManager", LoggerLevel::LOGINFO}
        };

        std::list<sys::WorkerQueueInfo> queueInfo{
            {LoggerWorker::SignalQueueName, LoggerWorker::SignalSize, LoggerWorker::SignalQueueLenght}};
        const std::list<sys::WorkerQueueInfo> queueInfo{
            {LoggerWorker::SignalQueueName, LoggerWorker::SignalSize, LoggerWorker::SignalQueueLength}};
        worker = std::make_unique<LoggerWorker>(Log::workerName);
        worker->init(queueInfo);
        worker->run();


@@ 65,38 65,38 @@ namespace Log

    void Logger::destroyInstance()
    {

        delete _logger;
        _logger = nullptr;
    }

    Logger &Logger::get()
    {
        static auto *logger = new Logger;
        _logger             = logger;
        return *logger;
        if (_logger == nullptr) {
            _logger = new Logger;
        }
        return *_logger;
    }

    auto Logger::getLogLevel(const std::string &name) -> logger_level
    LoggerLevel Logger::getLogLevel(const std::string &name)
    {
        return filtered[name];
    }

    auto Logger::getLogs() -> std::string
    std::string Logger::getLogs()
    {
        LockGuard lock(mutex);

        std::string logs;
        while (!buffer.getFlushBuffer()->isEmpty()) {
            const auto [result, msg] = buffer.getFlushBuffer()->get();
            if (result) {
                logs += msg;
            const auto msg = buffer.getFlushBuffer()->get();
            if (msg.has_value()) {
                logs += msg.value();
            }
        }
        return logs;
    }

    void Logger::init(Application app, size_t fileSize)
    void Logger::init(Application app, std::size_t fileSize)
    {
        application = std::move(app);
        maxFileSize = fileSize;


@@ 117,70 117,80 @@ namespace Log
        writeLogsTimer.start();
    }

    auto Logger::log(Device device, const char *fmt, va_list args) -> int
    int Logger::log(Device device, const char *fmt, va_list args)
    {
        LockGuard lock(mutex);
        loggerBufferCurrentPos = 0;
        lineBufferCurrentPos = 0;
        return writeLog(device, fmt, args);
    }

    auto Logger::log(
        logger_level level, const char *file, int line, const char *function, const char *fmt, va_list args) -> int
    int Logger::log(LoggerLevel level, const char *file, int line, const char *function, const char *fmt, va_list args)
    {
        if (!filterLogs(level)) {
            return -1;
        }
        LockGuard lock(mutex);
        loggerBufferCurrentPos = 0;
        lineBufferCurrentPos = 0;
        addLogHeader(level, file, line, function);
        return writeLog(Device::DEFAULT, fmt, args);
    }

    auto Logger::writeLog(Device device, const char *fmt, va_list args) -> int
    [[nodiscard]] std::size_t Logger::lineBufferSizeLeft() const noexcept
    {
        const auto sizeLeft = LINE_BUFFER_SIZE - lineBufferCurrentPos;
        assert(sizeLeft > 0);
        return sizeLeft;
    }

    int Logger::writeLog(Device device, const char *fmt, va_list args)
    {
        const auto sizeLeft = loggerBufferSizeLeft();
        const auto result   = vsnprintf(&loggerBuffer[loggerBufferCurrentPos], sizeLeft, fmt, args);
        if (0 <= result) {
            const auto numOfBytesAddedToBuffer = static_cast<size_t>(result);
            loggerBufferCurrentPos += (numOfBytesAddedToBuffer < sizeLeft) ? numOfBytesAddedToBuffer : (sizeLeft - 1);
            loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos], loggerBufferSizeLeft(), "\n");

            logToDevice(Device::DEFAULT, loggerBuffer, loggerBufferCurrentPos);
            buffer.getCurrentBuffer()->put(std::string(loggerBuffer, loggerBufferCurrentPos));
        constexpr auto lineTerminationString = "\n";
        constexpr auto lineTerminationLength = 2; // '\n' + null-terminator

        const auto bufferSizeLeft = lineBufferSizeLeft();
        const auto bytesParsed    = vsnprintf(&lineBuffer[lineBufferCurrentPos], bufferSizeLeft, fmt, args);
        if (bytesParsed >= 0) {
            /* Leave space for line termination */
            lineBufferCurrentPos +=
                std::min(bufferSizeLeft - lineTerminationLength, static_cast<std::size_t>(bytesParsed));
            /* Terminate the line */
            lineBufferCurrentPos +=
                snprintf(&lineBuffer[lineBufferCurrentPos], lineBufferSizeLeft(), lineTerminationString);
            /* Write the line to device and to the buffer */
            logToDevice(Device::DEFAULT, lineBuffer, lineBufferCurrentPos);
            buffer.getCurrentBuffer()->put(std::string(lineBuffer, lineBufferCurrentPos));
            checkBufferState();
            return loggerBufferCurrentPos;
            return lineBufferCurrentPos;
        }
        return -1;
    }

    auto Logger::logAssert(const char *fmt, va_list args) -> int
    int Logger::logAssert(const char *fmt, va_list args)
    {
        LockGuard lock(mutex);

        logToDevice(fmt, args);

        return loggerBufferCurrentPos;
        return lineBufferCurrentPos;
    }

    /// @param logPath: file path to store the log
    /// @return: < 0 - error occured during log flush
    /// @return:   0 - log flush did not happen
    /// @return:   1 - log flush successflul
    auto Logger::dumpToFile(std::filesystem::path logPath, bool isLoggerRunning) -> int
    int Logger::dumpToFile(const std::filesystem::path &logPath, LoggerState loggerState)
    {
        std::error_code errorCode;
        auto firstDump = !std::filesystem::exists(logPath, errorCode);

        if (errorCode) {
            if (isLoggerRunning) {
                LOG_ERROR("Failed to check if file %s exists, error: %d", logPath.c_str(), errorCode.value());
            if (loggerState == LoggerState::RUNNING) {
                LOG_ERROR("Failed to check if file '%s' exists, error: %d!", logPath.c_str(), errorCode.value());
            }
            return -EIO;
        }

        if (const bool maxSizeExceeded = !firstDump && std::filesystem::file_size(logPath) > maxFileSize;
        if (const auto maxSizeExceeded = (!firstDump && (std::filesystem::file_size(logPath) > maxFileSize));
            maxSizeExceeded) {
            if (isLoggerRunning) {
            if (loggerState == LoggerState::RUNNING) {
                LOG_DEBUG("Max log file size exceeded. Rotating log files...");
            }



@@ 191,56 201,62 @@ namespace Log
            firstDump = true;
        }

        int status = 1;
        {
            const auto &logs = getLogs();

            LockGuard lock(logFileMutex);
            std::ofstream logFile(logPath, std::fstream::out | std::fstream::app);
            if (!logFile.good()) {
                status = -EIO;
            }
            std::ofstream logFile;

            constexpr size_t streamBufferSize = 64 * 1024;
            auto streamBuffer                 = std::make_unique<char[]>(streamBufferSize);
            /* In some implementations pubsetbuf has to be called before opening a stream to be effective */
            logFile.rdbuf()->pubsetbuf(streamBuffer.get(), streamBufferSize);

            logFile.open(logPath, std::fstream::out | std::fstream::app);
            if (!logFile.good()) {
                if (loggerState == LoggerState::RUNNING) {
                    LOG_ERROR("Failed to open log file '%s'!", logPath.c_str());
                }
                return -EIO;
            }

            if (firstDump) {
                addFileHeader(logFile);
            }
            logFile.write(logs.data(), logs.size());
            if (logFile.bad()) {
                status = -EIO;
                if (loggerState == LoggerState::RUNNING) {
                    LOG_ERROR("Failed to flush logs to file '%s'!", logPath.c_str());
                }
                return -EIO;
            }
        }

        if (isLoggerRunning) {
            LOG_DEBUG("Flush ended with status: %d", status);
        if (loggerState == LoggerState::RUNNING) {
            LOG_DEBUG("Flush to file '%s' ended successfully!", logPath.c_str());
        }

        return status;
        return statusSuccess;
    }

    auto Logger::diagnosticDump() -> int
    int Logger::diagnosticDump()
    {
        buffer.nextBuffer();
        worker->notify(LoggerWorker::Signal::DumpDiagnostic);
        writeLogsTimer.restart(writeLogsToFileInterval);
        return 1;
        return statusSuccess;
    }

    auto Logger::flushLogs() -> int
    int Logger::flushLogs()
    {
        LOG_INFO("Shutdown dump logs");
        worker->close();
        writeLogsTimer.stop();
        buffer.nextBuffer();
        return dumpToFile(purefs::dir::getLogsPath() / LOG_FILE_NAME, false);
        return dumpToFile(purefs::dir::getLogsPath() / LOG_FILE_NAME, LoggerState::STOPPED);
    }

    void Logger::checkBufferState()
    {
        auto size = buffer.getCurrentBuffer()->getSize();
        const auto size = buffer.getCurrentBuffer()->getSize();

        if (size >= buffer.getCircularBufferSize()) {
            worker->notify(LoggerWorker::Signal::DumpFilledBuffer);


@@ 256,35 272,46 @@ namespace Log

    const char *getTaskDesc()
    {
        return xTaskGetCurrentTaskHandle() == nullptr ? Log::Logger::CRIT_STR
               : xPortIsInsideInterrupt()             ? Log::Logger::IRQ_STR
                                                      : pcTaskGetName(xTaskGetCurrentTaskHandle());
        if (xTaskGetCurrentTaskHandle() == nullptr) {
            return Log::Logger::CRIT_STR;
        }
        if (xPortIsInsideInterrupt() == pdTRUE) {
            return Log::Logger::IRQ_STR;
        }
        return pcTaskGetName(xTaskGetCurrentTaskHandle());
    }

    bool Logger::filterLogs(logger_level level)
    bool Logger::filterLogs(LoggerLevel level)
    {
        return getLogLevel(getTaskDesc()) <= level;
    }

    void Logger::addLogHeader(logger_level level, const char *file, int line, const char *function)
    void Logger::addLogHeader(LoggerLevel level, const char *file, int line, const char *function)
    {
        loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos],
                                           LOGGER_BUFFER_SIZE - loggerBufferCurrentPos,
                                           "%" PRIu32 " ms ",
                                           cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()));

        loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos],
                                           LOGGER_BUFFER_SIZE - loggerBufferCurrentPos,
                                           "%s%-5s %s[%s] %s%s:%s:%d:%s ",
                                           logColors->levelColors[level].data(),
                                           levelNames[level],
                                           logColors->serviceNameColor.data(),
                                           getTaskDesc(),
                                           logColors->callerInfoColor.data(),
                                           file,
                                           function,
                                           line,
                                           logColors->resetColor.data());
    }
        auto bufferSizeLeft = lineBufferSizeLeft();
        auto bytesParsed    = snprintf(&lineBuffer[lineBufferCurrentPos],
                                    bufferSizeLeft,
                                    "%" PRIu32 " ms ",
                                    cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()));
        if (bytesParsed >= 0) {
            lineBufferCurrentPos += std::min(bufferSizeLeft, static_cast<std::size_t>(bytesParsed));
        }

        bufferSizeLeft = lineBufferSizeLeft();
        bytesParsed    = snprintf(&lineBuffer[lineBufferCurrentPos],
                               bufferSizeLeft,
                               "%s%-5s %s[%s] %s%s:%s:%d:%s ",
                               logColors->levelColors[level].data(),
                               levelNames[level],
                               logColors->serviceNameColor.data(),
                               getTaskDesc(),
                               logColors->callerInfoColor.data(),
                               file,
                               function,
                               line,
                               logColors->resetColor.data());
        if (bytesParsed >= 0) {
            lineBufferCurrentPos += std::min(bufferSizeLeft, static_cast<std::size_t>(bytesParsed));
        }
    }
} // namespace Log

M module-utils/log/Logger.hpp => module-utils/log/Logger.hpp +31 -29
@@ 24,6 24,12 @@ namespace Log
        SEGGER_RTT
    };

    enum class LoggerState
    {
        STOPPED,
        RUNNING
    };

    struct Application
    {
        std::string name;


@@ 36,19 42,18 @@ namespace Log
    class Logger
    {
      public:
        void enableColors(bool enable);
        [[nodiscard]] static Logger &get();
        void enableColors(bool enable);
        static void destroyInstance();
        auto getLogs() -> std::string;
        void init(Application app, size_t fileSize = MAX_LOG_FILE_SIZE);
        std::string getLogs();
        void init(Application app, std::size_t fileSize = MAX_LOG_FILE_SIZE);
        void createTimer(sys::Service *parent);
        auto log(Device device, const char *fmt, va_list args) -> int;
        auto log(logger_level level, const char *file, int line, const char *function, const char *fmt, va_list args)
            -> int;
        auto logAssert(const char *fmt, va_list args) -> int;
        auto dumpToFile(std::filesystem::path logPath, bool isLoggerRunning = true) -> int;
        auto diagnosticDump() -> int;
        auto flushLogs() -> int;
        int log(Device device, const char *fmt, va_list args);
        int log(LoggerLevel level, const char *file, int line, const char *function, const char *fmt, va_list args);
        int logAssert(const char *fmt, va_list args);
        int dumpToFile(const std::filesystem::path &logPath, LoggerState loggerState = LoggerState::RUNNING);
        int diagnosticDump();
        int flushLogs();

        static constexpr auto CRIT_STR = "CRIT";
        static constexpr auto IRQ_STR  = "IRQ";


@@ 56,37 61,29 @@ namespace Log
      private:
        Logger();

        void addLogHeader(logger_level level,
                          const char *file     = nullptr,
                          int line             = -1,
                          const char *function = nullptr);
        [[nodiscard]] bool filterLogs(logger_level level);
        void addLogHeader(LoggerLevel level, const char *file = nullptr, int line = -1, const char *function = nullptr);
        [[nodiscard]] bool filterLogs(LoggerLevel level);
        /// Filter out not interesting logs via thread Name
        /// its' using fact that:
        /// - TRACE is level 0, for unedfined lookups it will be alvways trace
        /// - it will be one time init for apps which doesn't tell what level they should have
        /// - for others it will be o1 lookup so it's fine
        [[nodiscard]] auto getLogLevel(const std::string &name) -> logger_level;
        [[nodiscard]] LoggerLevel getLogLevel(const std::string &name);
        void logToDevice(const char *fmt, va_list args);
        void logToDevice(Device device, std::string_view logMsg, size_t length);
        auto writeLog(Device device, const char *fmt, va_list args) -> int;
        [[nodiscard]] size_t loggerBufferSizeLeft() const noexcept
        {
            const auto sizeLeft = LOGGER_BUFFER_SIZE - loggerBufferCurrentPos;
            assert(sizeLeft > 0);
            return sizeLeft;
        }
        void logToDevice(Device device, const char *logMsg, std::size_t length);
        int writeLog(Device device, const char *fmt, va_list args);
        std::size_t lineBufferSizeLeft() const noexcept;

        void addFileHeader(std::ofstream &file) const;
        void checkBufferState();

        cpp_freertos::MutexStandard mutex;
        cpp_freertos::MutexStandard logFileMutex;
        logger_level level{LOGTRACE};
        LoggerLevel loggerLevel{LOGTRACE};
        const LogColors *logColors            = &logColorsOff;
        char loggerBuffer[LOGGER_BUFFER_SIZE] = {0};
        size_t loggerBufferCurrentPos         = 0;
        size_t maxFileSize                    = MAX_LOG_FILE_SIZE;
        char lineBuffer[LINE_BUFFER_SIZE]     = {0};
        std::size_t lineBufferCurrentPos      = 0;
        std::size_t maxFileSize               = MAX_LOG_FILE_SIZE;

        Application application;



@@ 97,9 94,14 @@ namespace Log
        sys::TimerHandle writeLogsTimer;

        static const char *levelNames[];
        std::map<std::string, logger_level> filtered;
        std::map<std::string, LoggerLevel> filtered;
        static Logger *_logger;
        std::unique_ptr<LoggerWorker> worker;

        static constexpr int statusSuccess = 1;

        static constexpr std::size_t streamBufferSize = 64 * 1024;
        std::unique_ptr<char[]> streamBuffer;
    };

    const char *getTaskDesc();

M module-utils/log/LoggerBuffer.cpp => module-utils/log/LoggerBuffer.cpp +14 -10
@@ 1,19 1,19 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "LoggerBuffer.hpp"

std::pair<bool, std::string> LoggerBuffer::get()
std::optional<std::string> LoggerBuffer::get()
{
    auto [result, logMsg] = StringCircularBuffer::get();
    if (!result) {
        return {result, logMsg};
    auto logMsg = StringCircularBuffer::get();
    if (!logMsg.has_value()) {
        return std::nullopt;
    }
    if (numOfLostBytes > 0) {
        logMsg         = std::to_string(numOfLostBytes) + " " + lostBytesMessage + "\n" + logMsg;
        logMsg         = std::to_string(numOfLostBytes) + " " + lostBytesMessage + "\n" + logMsg.value();
        numOfLostBytes = 0;
    }
    return {true, logMsg};
    return logMsg;
}

void LoggerBuffer::put(const std::string &logMsg)


@@ 30,8 30,12 @@ void LoggerBuffer::put(std::string &&logMsg)

void LoggerBuffer::updateNumOfLostBytes()
{
    if (StringCircularBuffer::isFull()) {
        auto [_, lostMsg] = StringCircularBuffer::get();
        numOfLostBytes += lostMsg.length();
    if (!StringCircularBuffer::isFull()) {
        return;
    }

    const auto lostMsg = StringCircularBuffer::get();
    if (lostMsg.has_value()) {
        numOfLostBytes += lostMsg.value().length();
    }
}

M module-utils/log/LoggerBuffer.hpp => module-utils/log/LoggerBuffer.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 11,11 11,11 @@ class LoggerBuffer : public StringCircularBuffer
    explicit LoggerBuffer(size_t size) : StringCircularBuffer(size)
    {}

    [[nodiscard]] std::pair<bool, std::string> get();
    [[nodiscard]] std::optional<std::string> get();
    void put(const std::string &logMsg);
    void put(std::string &&logMsg);

    static constexpr auto lostBytesMessage = "bytes was lost.";
    static constexpr auto lostBytesMessage = "bytes were lost.";

  private:
    void updateNumOfLostBytes();

M module-utils/log/LoggerWorker.cpp => module-utils/log/LoggerWorker.cpp +2 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "LoggerWorker.hpp"


@@ 9,7 9,7 @@

namespace Log
{
    LoggerWorker::LoggerWorker(const std::string name) : Worker(name, priority)
    LoggerWorker::LoggerWorker(const std::string &name) : Worker(name, priority)
    {}

    void LoggerWorker::notify(Signal command)

M module-utils/log/LoggerWorker.hpp => module-utils/log/LoggerWorker.hpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 21,9 21,9 @@ namespace Log

        static constexpr auto SignalQueueName   = "LoggerSignal";
        static constexpr auto SignalSize        = sizeof(Signal);
        static constexpr auto SignalQueueLenght = 1;
        static constexpr auto SignalQueueLength = 1;

        explicit LoggerWorker(const std::string name);
        explicit LoggerWorker(const std::string &name);
        void notify(Signal command);
        bool handleMessage(std::uint32_t queueID) override;
        void handleCommand(Signal command);

M module-utils/log/StringCircularBuffer.cpp => module-utils/log/StringCircularBuffer.cpp +6 -5
@@ 1,20 1,21 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "StringCircularBuffer.hpp"

std::pair<bool, std::string> StringCircularBuffer::get()
std::optional<std::string> StringCircularBuffer::get()
{
    if (isEmpty()) {
        return {false, ""};
        return std::nullopt;
    }

    const std::string val = buffer[tail];
    const auto val = buffer[tail];

    full                  = false;
    tail                  = (tail + 1) % capacity;
    --size;

    return {true, val};
    return val;
}

void StringCircularBuffer::put(const std::string &item)

M module-utils/log/StringCircularBuffer.hpp => module-utils/log/StringCircularBuffer.hpp +3 -2
@@ 1,10 1,11 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <memory>
#include <string>
#include <optional>

class StringCircularBuffer
{


@@ 19,7 20,7 @@ class StringCircularBuffer
    {
        return size == 0;
    }
    [[nodiscard]] virtual std::pair<bool, std::string> get();
    [[nodiscard]] virtual std::optional<std::string> get();
    [[nodiscard]] size_t getSize() const noexcept
    {
        return size;

M module-utils/log/api/log/log.hpp => module-utils/log/api/log/log.hpp +4 -4
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*


@@ 53,14 53,14 @@ extern "C"
        LOGWARN,
        LOGERROR,
        LOGFATAL
    } logger_level;
    } LoggerLevel;

#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

    /**
     * Forward declarations
     */
    int log_Log(logger_level level, const char *file, int line, const char *function, const char *fmt, ...)
    int log_Log(LoggerLevel level, const char *file, int line, const char *function, const char *fmt, ...)
        __attribute__((format(printf, 5, 6)));
#ifdef LOG_IGNORE_ALL
    int log_ignore(logger_level level, const char *file, int line, const char *function, const char *fmt, ...)


@@ 103,7 103,7 @@ extern "C"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
static const size_t LOGGER_BUFFER_SIZE = 8192;
static const size_t LINE_BUFFER_SIZE   = 2048;
static const char *LOG_FILE_NAME       = "MuditaOS.log";
static const int MAX_LOG_FILES_COUNT   = 3;
static const size_t MAX_LOG_FILE_SIZE  = 1024 * 1024 * 15; // 15 MB

M module-utils/log/board/linux/log_linux.cpp => module-utils/log/board/linux/log_linux.cpp +2 -4
@@ 1,10 1,8 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>
#include <Logger.hpp>
#include <iostream>
#include <string_view>
#include <ticks.hpp>

namespace Log


@@ 14,7 12,7 @@ namespace Log
        assert(false && "Not implemented");
    }

    void Logger::logToDevice(Device, std::string_view logMsg, size_t)
    void Logger::logToDevice(Device, const char *logMsg, size_t)
    {
        std::cout << logMsg;
    }

M module-utils/log/board/rt1051/log_rt1051.cpp => module-utils/log/board/rt1051/log_rt1051.cpp +31 -19
@@ 1,8 1,6 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <board.h>
#include <critical.hpp>
#include <macros.h>
#include <log/log.hpp>
#include <Logger.hpp>


@@ 13,31 11,45 @@ namespace Log
{
    void Logger::logToDevice(const char *fmt, va_list args)
    {
        loggerBufferCurrentPos = 0;
        loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos],
                                           loggerBufferSizeLeft(),
                                           "%lu ms ",
                                           cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()));
        lineBufferCurrentPos = 0;

        loggerBufferCurrentPos += snprintf(&loggerBuffer[loggerBufferCurrentPos],
                                           loggerBufferSizeLeft(),
                                           "%-5s [%-10s] \x1b[31mASSERTION ",
                                           levelNames[LOGFATAL],
                                           getTaskDesc());
        auto bufferSizeLeft = lineBufferSizeLeft();
        auto bytesParsed    = snprintf(&lineBuffer[lineBufferCurrentPos],
                                    bufferSizeLeft,
                                    "%lu ms ",
                                    cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks()));
        if (bytesParsed >= 0) {
            lineBufferCurrentPos += std::min(bufferSizeLeft, static_cast<std::size_t>(bytesParsed));
        }

        bufferSizeLeft = lineBufferSizeLeft();
        bytesParsed    = snprintf(&lineBuffer[lineBufferCurrentPos],
                               bufferSizeLeft,
                               "%-5s [%-10s] \x1b[31mASSERTION ",
                               levelNames[LOGFATAL],
                               getTaskDesc());
        if (bytesParsed >= 0) {
            lineBufferCurrentPos += std::min(bufferSizeLeft, static_cast<std::size_t>(bytesParsed));
        }

        bufferSizeLeft = lineBufferSizeLeft();
        bytesParsed    = vsnprintf(&lineBuffer[lineBufferCurrentPos], bufferSizeLeft, fmt, args);
        if (bytesParsed >= 0) {
            lineBufferCurrentPos += std::min(bufferSizeLeft, static_cast<std::size_t>(bytesParsed));
        }

        loggerBufferCurrentPos += vsnprintf(&loggerBuffer[loggerBufferCurrentPos], loggerBufferSizeLeft(), fmt, args);
        logToDevice(Device::DEFAULT, loggerBuffer, loggerBufferCurrentPos);
        buffer.getCurrentBuffer()->put(std::string(loggerBuffer, loggerBufferCurrentPos));
        logToDevice(Device::DEFAULT, lineBuffer, lineBufferCurrentPos);
        buffer.getCurrentBuffer()->put(std::string(lineBuffer, lineBufferCurrentPos));
    }

    void Logger::logToDevice(Device device, std::string_view logMsg, size_t length)
    void Logger::logToDevice(Device device, const char *logMsg, std::size_t length)
    {
        switch (device) {
        case Device::DEFAULT:
            log_WriteToDevice(reinterpret_cast<const uint8_t *>(logMsg.data()), length);
            log_WriteToDevice(reinterpret_cast<const uint8_t *>(logMsg), length);
            break;
        case Device::SEGGER_RTT:
            SEGGER_RTT_Write(0, reinterpret_cast<const void *>(logMsg.data()), length);
            SEGGER_RTT_Write(0, logMsg, length);
            break;
        default:
            break;

M module-utils/log/log.cpp => module-utils/log/log.cpp +3 -3
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <log/log.hpp>


@@ 23,7 23,7 @@ int log_Printf(const char *fmt, ...)
    return result;
}

int log_ignore(logger_level level, const char *file, int line, const char *function, const char *fmt, ...)
int log_ignore(LoggerLevel level, const char *file, int line, const char *function, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);


@@ 31,7 31,7 @@ int log_ignore(logger_level level, const char *file, int line, const char *funct
    return 0;
}

int log_Log(logger_level level, const char *file, int line, const char *function, const char *fmt, ...)
int log_Log(LoggerLevel level, const char *file, int line, const char *function, const char *fmt, ...)
{
    va_list args;


M module-utils/log/tests/test_LoggerBuffer.cpp => module-utils/log/tests/test_LoggerBuffer.cpp +16 -16
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 38,39 38,39 @@ TEST_CASE("LoggerBuffer tests")

    auto putMsgFunc = [&](const auto &msg) { buffer.put(msg); };
    auto getMsgFunc = [&](const auto &originalMsg) {
        const auto [result, msg] = buffer.get();
        REQUIRE(result);
        REQUIRE(msg == originalMsg);
        const auto msg = buffer.get();
        REQUIRE(msg.has_value());
        REQUIRE(msg.value() == originalMsg);
    };
    auto putAllMsgsFunc = [&](const vector<string> &msgs) { for_each(msgs.begin(), msgs.end(), putMsgFunc); };
    auto getAllMsgsFunc = [&](const vector<string> &msgs) { for_each(msgs.begin(), msgs.end(), getMsgFunc); };
    auto checkLostBytes = [&](size_t numOfBytes, const string &originalMsg) {
        const auto [result, msg] = buffer.get();
        REQUIRE(result);
        REQUIRE(msg.find(originalMsg) != string::npos);
        REQUIRE(msg.find(to_string(numOfBytes)) != string::npos);
        REQUIRE(msg.find(LoggerBuffer::lostBytesMessage) != string::npos);
        const auto msg = buffer.get();
        REQUIRE(msg.has_value());
        REQUIRE(msg.value().find(originalMsg) != string::npos);
        REQUIRE(msg.value().find(to_string(numOfBytes)) != string::npos);
        REQUIRE(msg.value().find(LoggerBuffer::lostBytesMessage) != string::npos);
    };

    SECTION("calling get on empty buffer should return false")
    {
        const auto [result, _] = buffer.get();
        REQUIRE(!result);
        const auto msg = buffer.get();
        REQUIRE(!msg.has_value());
        TestBuffer(buffer, capacity, 0);
    }

    SECTION("after putting one msg in buffer, get should return this msg")
    {
        const string originalMsg = randomStringGenerator.getRandomString();
        const auto originalMsg = randomStringGenerator.getRandomString();
        buffer.put(originalMsg);
        TestBuffer(buffer, capacity, 1);
        const auto [result, msg] = buffer.get();
        REQUIRE(result);
        REQUIRE(msg == originalMsg);
        const auto msg = buffer.get();
        REQUIRE(msg.has_value());
        REQUIRE(msg.value() == originalMsg);
        TestBuffer(buffer, capacity, 0);
    }

    SECTION("after filling whole buffer with msgs, caliling get repeatedly should return all these msgs back")
    SECTION("after filling whole buffer with msgs, calling get repeatedly should return all these msgs back")
    {
        const auto msgs = randomStringGenerator.createRandomStringVector(capacity);
        putAllMsgsFunc(msgs);

M module-utils/log/tests/test_log.cpp => module-utils/log/tests/test_log.cpp +9 -10
@@ 1,4 1,4 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>


@@ 10,17 10,16 @@ using namespace std;

TEST_CASE("Log tests")
{
    int value                     = -423;
    const int value                   = -423;
    const char *carray            = "carray";
    string str                    = "string";
    double double_value           = 6.5323;
    unsigned int unsigned_value   = 7821;
    constexpr auto big_array_size = LOGGER_BUFFER_SIZE + 2;
    char big_array[big_array_size];
    memset(big_array, 'X', big_array_size);
    big_array[big_array_size - 1] = '\0';
    const string str                  = "string";
    const double double_value         = 6.5323;
    const unsigned int unsigned_value = 7821;
    char big_array[LINE_BUFFER_SIZE + 2];
    memset(big_array, 'X', sizeof(big_array));
    big_array[sizeof(big_array) - 1] = '\0';

    int loggerBufferSize = static_cast<int>(LOGGER_BUFFER_SIZE);
    const auto loggerBufferSize = static_cast<int>(LINE_BUFFER_SIZE);
    int result           = LOG_TRACE("value: %d", value);
    REQUIRE(0 < result);
    REQUIRE(result <= loggerBufferSize);

M test/mock-logs.cpp => test/mock-logs.cpp +1 -1
@@ 4,7 4,7 @@
#include <log/log.hpp>
#include <cstdarg>
__attribute__((weak)) int log_Log(
    logger_level level, const char *file, int line, const char *function, const char *fmt, ...)
    LoggerLevel level, const char *file, int line, const char *function, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);