diff --git a/.codespellrc b/.codespellrc index c7019c766a..7506fd03df 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] -skip = *.dat,typos-config.toml,.git,.venv,./ci,./Dist,./mk,./Tests/ExamplesTest/expected_output,./Tests/ExamplesTest/pcap_examples,./Tests/Packet++Test/PacketExamples,./Tests/Pcap++Test/PcapExamples,./3rdParty,./Examples/PcapSearch/dirent-for-Visual-Studio +skip = *.dat,typos-config.toml,.git,.venv,venv,./out,./ci,./Dist,./mk,./Tests/ExamplesTest/expected_output,./Tests/ExamplesTest/pcap_examples,./Tests/Packet++Test/PacketExamples,./Tests/Pcap++Test/PcapExamples,./3rdParty,./Examples/PcapSearch/dirent-for-Visual-Studio ignore-words = codespell-ignore-list.txt count = diff --git a/Common++/CMakeLists.txt b/Common++/CMakeLists.txt index 5b75bf9f77..01cdbd671e 100644 --- a/Common++/CMakeLists.txt +++ b/Common++/CMakeLists.txt @@ -20,6 +20,7 @@ set(public_headers header/Logger.h header/LRUList.h header/MacAddress.h + header/ObjectPool.h header/OUILookup.h header/PcapPlusPlusVersion.h header/PointerVector.h diff --git a/Common++/header/IpAddress.h b/Common++/header/IpAddress.h index 243dae0ef9..44d0f98735 100644 --- a/Common++/header/IpAddress.h +++ b/Common++/header/IpAddress.h @@ -1082,40 +1082,40 @@ namespace pcpp std::unique_ptr m_IPv4Network; std::unique_ptr m_IPv6Network; }; -} // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Address& ipv4Address) -{ - os << ipv4Address.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Address& ipv4Address) + { + os << ipv4Address.toString(); + return os; + } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Address& ipv6Address) -{ - os << ipv6Address.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Address& ipv6Address) + { + os << ipv6Address.toString(); + return os; + } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPAddress& ipAddress) -{ - os << ipAddress.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPAddress& ipAddress) + { + os << ipAddress.toString(); + return os; + } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Network& network) -{ - os << network.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv4Network& network) + { + os << network.toString(); + return os; + } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Network& network) -{ - os << network.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPv6Network& network) + { + os << network.toString(); + return os; + } -inline std::ostream& operator<<(std::ostream& os, const pcpp::IPNetwork& network) -{ - os << network.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::IPNetwork& network) + { + os << network.toString(); + return os; + } +} // namespace pcpp diff --git a/Common++/header/Logger.h b/Common++/header/Logger.h index fafe6a9e37..c206cf76d9 100644 --- a/Common++/header/Logger.h +++ b/Common++/header/Logger.h @@ -1,10 +1,13 @@ #pragma once #include +#include #include #include #include #include +#include "DeprecationUtils.h" +#include "ObjectPool.h" #ifndef LOG_MODULE # define LOG_MODULE UndefinedLogModule @@ -17,30 +20,20 @@ # define PCAPPP_FILENAME __FILE__ #endif -#define PCPP_LOG(level, message) \ - do \ - { \ - std::ostringstream* sstream = pcpp::Logger::getInstance().internalCreateLogStream(); \ - (*sstream) << message; \ - pcpp::Logger::getInstance().internalPrintLogMessage(sstream, level, PCAPPP_FILENAME, __FUNCTION__, __LINE__); \ - } while (0) - -#define PCPP_LOG_DEBUG(message) \ - do \ - { \ - if (pcpp::Logger::getInstance().logsEnabled() && pcpp::Logger::getInstance().isDebugEnabled(LOG_MODULE)) \ - { \ - PCPP_LOG(pcpp::Logger::Debug, message); \ - } \ - } while (0) +/// @file -#define PCPP_LOG_ERROR(message) \ - do \ - { \ - PCPP_LOG(pcpp::Logger::Error, message); \ - } while (0) +// Compile time log levels. +// Allows for conditional removal of unwanted log calls at compile time. +#define PCPP_LOG_LEVEL_OFF 0 +#define PCPP_LOG_LEVEL_ERROR 1 +#define PCPP_LOG_LEVEL_INFO 2 +#define PCPP_LOG_LEVEL_DEBUG 3 -/// @file +// All log messages built via a PCAP_LOG_* macro below the PCAP_ACTIVE_LOG_LEVEL will be removed at compile time. +// Uses the PCAP_ACTIVE_LOG_LEVEL if it is defined, otherwise defaults to PCAP_LOG_LEVEL_DEBUG +#ifndef PCPP_ACTIVE_LOG_LEVEL +# define PCPP_ACTIVE_LOG_LEVEL PCPP_LOG_LEVEL_DEBUG +#endif // !PCAP_ACTIVE_LOG_LEVEL /** * \namespace pcpp @@ -79,6 +72,7 @@ namespace pcpp PacketLogModuleGreLayer, ///< GreLayer module (Packet++) PacketLogModuleSSLLayer, ///< SSLLayer module (Packet++) PacketLogModuleSllLayer, ///< SllLayer module (Packet++) + PacketLogModuleSll2Layer, ///< Sll2Layer module (Packet++) PacketLogModuleNflogLayer, ///< NflogLayer module (Packet++) PacketLogModuleDhcpLayer, ///< DhcpLayer module (Packet++) PacketLogModuleDhcpV6Layer, ///< DhcpV6Layer module (Packet++) @@ -113,10 +107,124 @@ namespace pcpp PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++) PcapLogModuleKniDevice, ///< KniDevice module (Pcap++) PcapLogModuleXdpDevice, ///< XdpDevice module (Pcap++) - NetworkUtils, ///< NetworkUtils module (Pcap++) + PcapLogModuleNetworkUtils, ///< Network Utils module (Pcap++) NumOfLogModules }; + /** + * @struct LogSource + * Represents the source of a log message. + * Contains information about the source file, function, line number, and the log module. + */ + struct LogSource + { + /** + * Default constructor for LogSource. + */ + constexpr LogSource() = default; + + /** + * Constructor for LogSource with only the log module. + * @param logModule The log module. + */ + explicit constexpr LogSource(LogModule logModule) : logModule(logModule) + {} + + /** + * Constructor for LogSource with all parameters. + * @param logModule The log module. + * @param file The source file. + * @param function The source function. + * @param line The line number. + */ + constexpr LogSource(LogModule logModule, const char* file, const char* function, int line) + : file(file), function(function), line(line), logModule(logModule) + {} + + const char* file = nullptr; /**< The source file. */ + const char* function = nullptr; /**< The source function. */ + int line = 0; /**< The line number. */ + LogModule logModule = UndefinedLogModule; /**< The log module. */ + }; + + /** + * An enum representing the log level. Currently 4 log levels are supported: Off, Error, Info and Debug. Info is the + * default log level + */ + enum class LogLevel + { + Off = PCPP_LOG_LEVEL_OFF, ///< No log messages are emitted. + Error = PCPP_LOG_LEVEL_ERROR, ///< Error level logs are emitted. + Info = PCPP_LOG_LEVEL_INFO, ///< Info level logs and above are emitted. + Debug = PCPP_LOG_LEVEL_DEBUG ///< Debug level logs and above are emitted. + }; + + inline std::ostream& operator<<(std::ostream& s, LogLevel v) + { + return s << static_cast::type>(v); + } + + // Forward declaration + class Logger; + + namespace internal + { + /** + * @class LogContext + * @brief A context encapsulating the details of a single log message to be passed to the Logger. + */ + class LogContext + { + public: + friend class pcpp::Logger; + + /** + * @brief Creates a context with an empty message with Info level and no source. + */ + LogContext() = default; + /** + * @brief Creates a context with an empty message with the given level and source. + * @param level The log level for this message. + * @param source The log source. + */ + explicit LogContext(LogLevel level, LogSource const& source = {}) : level(level), m_Source(source) + {} + + /** + * @brief Initializes the context with an empty message and the given level and source. + * @param level The log level for this message. + * @param source The log source. + */ + void init(LogLevel level, LogSource const& source) + { + m_Source = source; + this->level = level; + m_Stream.clear(); + m_Stream.str({}); + } + + /** + * @brief Appends to the message. + * @param value The value to append. + * @return A reference to this context. + */ + template inline LogContext& operator<<(T const& value) + { + m_Stream << value; + return *this; + } + + /** + * @brief The log level at which the message will be emitted. + */ + LogLevel level = LogLevel::Info; + + private: + LogSource m_Source; + std::ostringstream m_Stream; + }; + } // namespace internal + /** * @class Logger * PcapPlusPlus logger manager. @@ -139,16 +247,14 @@ namespace pcpp class Logger { public: - /** - * An enum representing the log level. Currently 3 log levels are supported: Error, Info and Debug. Info is the - * default log level - */ - enum LogLevel - { - Error, ///< Error log level - Info, ///< Info log level - Debug ///< Debug log level - }; + // Deprecated, Use the LogLevel in the pcpp namespace instead. + using LogLevel = pcpp::LogLevel; + PCPP_DEPRECATED("Use the LogLevel in the pcpp namespace instead.") + static const LogLevel Error = LogLevel::Error; + PCPP_DEPRECATED("Use the LogLevel in the pcpp namespace instead.") + static const LogLevel Info = LogLevel::Info; + PCPP_DEPRECATED("Use the LogLevel in the pcpp namespace instead.") + static const LogLevel Debug = LogLevel::Debug; /** * @typedef LogPrinter @@ -158,6 +264,7 @@ namespace pcpp * @param[in] file The source file in PcapPlusPlus code the log message is coming from * @param[in] method The method in PcapPlusPlus code the log message is coming from * @param[in] line The line in PcapPlusPlus code the log message is coming from + * @remarks The printer callback should support being called from multiple threads simultaneously. */ typedef void (*LogPrinter)(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); @@ -196,7 +303,18 @@ namespace pcpp */ bool isDebugEnabled(LogModule module) const { - return m_LogModulesArray[module] == Debug; + return m_LogModulesArray[module] == LogLevel::Debug; + } + + /** + * @brief Check whether a log level should be emitted by the logger. + * @param level The level of the log message. + * @param module PcapPlusPlus module + * @return True if the message should be emitted. False otherwise. + */ + bool shouldLog(LogLevel level, LogModule module) const + { + return level != LogLevel::Off && m_LogModulesArray[module] >= level; } /** @@ -259,19 +377,29 @@ namespace pcpp return m_LogsEnabled; } - template Logger& operator<<(const T& msg) - { - (*m_LogStream) << msg; - return *this; - } - - std::ostringstream* internalCreateLogStream(); - /** - * An internal method to print log messages. Shouldn't be used externally. + * @brief Controls if the logger should use a pool of LogContext objects. + * @param enabled True to enable context pooling, false to disable. + * @param preallocate The number of LogContext objects to preallocate in the pool. + * @remarks Disabling the pooling clears the pool. */ - void internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file, - const char* method, int line); + void useContextPooling(bool enabled, std::size_t preallocate = 2) + { + m_UseContextPooling = enabled; + + if (m_UseContextPooling) + { + if (preallocate > 0) + { + m_LogContextPool.preallocate(preallocate); + } + } + else + { + // Clear the pool if we're disabling pooling. + m_LogContextPool.clear(); + } + } /** * Get access to Logger singleton @@ -284,12 +412,105 @@ namespace pcpp return instance; } + /** + * @brief Creates a new LogContext with Info level and no source. + * @return A new LogContext. + */ + std::unique_ptr createLogContext(); + /** + * @brief Creates a new LogContext with the given level and source. + * @param level The log level for this message. + * @param source The log source. + * @return A new LogContext. + */ + std::unique_ptr createLogContext(LogLevel level, LogSource const& source = {}); + + /** + * @brief Directly emits a log message bypassing all level checks. + * @param source The log source. + * @param level The log level for this message. This is only used for the log printer. + * @param message The log message. + */ + void emit(LogSource const& source, LogLevel level, std::string const& message); + /** + * @brief Directly emits a log message bypassing all level checks. + * @param message The log message. + */ + void emit(std::unique_ptr message); + + /** + * @brief Logs a message with the given source, level, and message. + * @param message The log message. + */ + void log(std::unique_ptr message); + + /** + * @brief Logs an object with the given source, level. + * + * The object is converted to a string via the std::ostream << operator. + * @tparam T The type of object to be logged. + * @param source The log source. + * @param level The log level for this message. + * @param message The object to be logged. + */ + template void log(LogSource const& source, LogLevel level, T const& message) + { + if (shouldLog(level, source.logModule)) + { + auto ctx = createLogContext(); + (*ctx) << message; + emit(std::move(ctx)); + } + }; + + /** + * @brief Logs an object with the given source at the Error level. + * + * The object is converted to a string via the std::ostream << operator. + * @tparam T The type of object to be logged. + * @param source The log source. + * @param message The object to be logged. + */ + template void logError(LogSource const& source, T const& message) + { + log(source, LogLevel::Error, message); + }; + + /** + * @brief Logs an object with the given source at the Info level. + * + * The object is converted to a string via the std::ostream << operator. + * @tparam T The type of object to be logged. + * @param source The log source. + * @param message The object to be logged. + */ + template void logInfo(LogSource const& source, T const& message) + { + log(source, LogLevel::Info, message); + }; + + /** + * @brief Logs an object with the given source at the Debug level. + * + * The object is converted to a string via the std::ostream << operator. + * @tparam T The type of object to be logged. + * @param source The log source. + * @param message The object to be logged. + */ + template void logDebug(LogSource const& source, T const& message) + { + log(source, LogLevel::Debug, message); + }; + private: bool m_LogsEnabled; - Logger::LogLevel m_LogModulesArray[NumOfLogModules]; + LogLevel m_LogModulesArray[NumOfLogModules]; LogPrinter m_LogPrinter; std::string m_LastError; - std::ostringstream* m_LogStream; + + bool m_UseContextPooling = true; + // Keep a maximum of 10 LogContext objects in the pool. + ObjectPool m_LogContextPool{ 10, 2 }; // private c'tor - this class is a singleton Logger(); @@ -297,4 +518,54 @@ namespace pcpp static void defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, const std::string& method, const int line); }; + + // Specialization for string to skip the stringstream + template <> inline void Logger::log(LogSource const& source, LogLevel level, std::string const& message) + { + if (shouldLog(level, source.logModule)) + { + emit(source, level, message); + } + }; + + // Specialization for const char* to skip the stringstream + template <> inline void Logger::log(LogSource const& source, LogLevel level, const char* const& message) + { + if (shouldLog(level, source.logModule)) + { + emit(source, level, message); + } + }; + } // namespace pcpp + +#define PCPP_LOG(level, message) \ + do \ + { \ + auto& logger = Logger::getInstance(); \ + if (logger.shouldLog(level, LOG_MODULE)) \ + { \ + auto ctx = \ + logger.createLogContext(level, pcpp::LogSource(LOG_MODULE, PCAPPP_FILENAME, __FUNCTION__, __LINE__)); \ + (*ctx) << message; \ + logger.emit(std::move(ctx)); \ + } \ + } while (0) + +#if PCPP_ACTIVE_LOG_LEVEL >= PCPP_LOG_LEVEL_DEBUG +# define PCPP_LOG_DEBUG(message) PCPP_LOG(pcpp::LogLevel::Debug, message) +#else +# define PCPP_LOG_DEBUG(message) (void)0 +#endif + +#if PCPP_ACTIVE_LOG_LEVEL >= PCPP_LOG_LEVEL_INFO +# define PCPP_LOG_INFO(message) PCPP_LOG(pcpp::LogLevel::Info, message) +#else +# define PCPP_LOG_INFO(message) (void)0 +#endif + +#if PCPP_ACTIVE_LOG_LEVEL >= PCPP_LOG_LEVEL_ERROR +# define PCPP_LOG_ERROR(message) PCPP_LOG(pcpp::LogLevel::Error, message) +#else +# define PCPP_LOG_ERROR(message) (void)0 +#endif diff --git a/Common++/header/MacAddress.h b/Common++/header/MacAddress.h index 29333aa02d..8b59f7d53c 100644 --- a/Common++/header/MacAddress.h +++ b/Common++/header/MacAddress.h @@ -173,10 +173,10 @@ namespace pcpp private: uint8_t m_Address[6] = { 0 }; }; -} // namespace pcpp -inline std::ostream& operator<<(std::ostream& os, const pcpp::MacAddress& macAddress) -{ - os << macAddress.toString(); - return os; -} + inline std::ostream& operator<<(std::ostream& os, const pcpp::MacAddress& macAddress) + { + os << macAddress.toString(); + return os; + } +} // namespace pcpp diff --git a/Common++/header/ObjectPool.h b/Common++/header/ObjectPool.h new file mode 100644 index 0000000000..ec73f1e820 --- /dev/null +++ b/Common++/header/ObjectPool.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include + +namespace pcpp +{ + /** + * @brief A generic object pool implementation. + * + * This class provides a generic object pool that can be used to efficiently manage and reuse objects of any type. + * Objects can be acquired from the pool using the `acquireObject` method, and released back to the pool using the + * `releaseObject` method. If the pool is empty when acquiring an object, a new object will be created. If the pool + * is full when releasing an object, the object will be deleted. + * + * @tparam T The type of objects managed by the pool. Must be default constructable. + */ + template ::value, bool>::type = true> + class ObjectPool + { + public: + constexpr static std::size_t DEFAULT_POOL_SIZE = 100; + constexpr static std::size_t INFINITE_POOL_SIZE = 0; + + /** + * A constructor for this class that creates a pool of objects + * @param[in] maxPoolSize The maximum number of objects in the pool + * @param[in] preallocate The number of objects to preallocate in the pool + */ + explicit ObjectPool(std::size_t maxPoolSize = DEFAULT_POOL_SIZE, std::size_t preallocate = 0) + : m_maxPoolSize(maxPoolSize) + { + this->preallocate(preallocate); + } + + // These don't strictly need to be deleted, but don't need to be implemented for now either. + ObjectPool(const ObjectPool&) = delete; + ObjectPool(ObjectPool&&) = delete; + ObjectPool& operator=(const ObjectPool&) = delete; + ObjectPool& operator=(ObjectPool&&) = delete; + + /** + * A destructor for this class that deletes all objects in the pool + */ + ~ObjectPool() + { + clear(); + } + + /** + * @brief Acquires a unique pointer to an object from the pool. + * + * This method acquires a unique pointer to an object from the pool. + * If the pool is empty, a new object will be created. + * + * @return A unique pointer to an object from the pool. + */ + std::unique_ptr acquireObject() + { + return std::unique_ptr(acquireObjectRaw()); + } + + /** + * @brief Acquires a raw pointer to an object from the pool. + * + * This method acquires a raw pointer to an object from the pool. + * If the pool is empty, a new object will be created. + * + * @return A raw pointer to an object from the pool. + */ + T* acquireObjectRaw() + { + std::unique_lock lock(m_mutex); + + if (m_pool.empty()) + { + // We don't need the lock anymore, so release it. + lock.unlock(); + return new T(); + } + + T* obj = m_pool.top(); + m_pool.pop(); + return obj; + } + + /** + * @brief Releases a unique pointer to an object back to the pool. + * + * This method releases a unique pointer to an object back to the pool. + * If the pool is full, the object will be deleted. + * + * @param[in] obj The unique pointer to the object to release. + */ + void releaseObject(std::unique_ptr obj) + { + releaseObjectRaw(obj.release()); + } + + /** + * @brief Releases a raw pointer to an object back to the pool. + * + * This method releases a raw pointer to an object back to the pool. + * If the pool is full, the object will be deleted. + * + * @param[in] obj The raw pointer to the object to release. + */ + void releaseObjectRaw(T* obj) + { + std::unique_lock lock(m_mutex); + + if (m_maxPoolSize == INFINITE_POOL_SIZE || m_pool.size() < m_maxPoolSize) + { + m_pool.push(obj); + } + else + { + // We don't need the lock anymore, so release it. + lock.unlock(); + delete obj; + } + } + + /** + * @brief Pre-allocates up to a minimum number of objects in the pool. + * @param count The number of objects to pre-allocate. + */ + void preallocate(std::size_t count) + { + std::unique_lock lock(m_mutex); + for (std::size_t i = m_pool.size(); i < count; i++) + { + m_pool.push(new T()); + } + } + + /** + * @brief Deallocates and releases all objects currently held by the pool. + */ + void clear() + { + std::unique_lock lock(m_mutex); + while (!m_pool.empty()) + { + delete m_pool.top(); + m_pool.pop(); + } + } + + private: + std::size_t m_maxPoolSize; /**< The maximum number of objects in the pool */ + std::mutex m_mutex; /**< Mutex for thread safety */ + std::stack m_pool; /**< The pool of objects */ + }; +} // namespace pcpp diff --git a/Common++/src/IpAddress.cpp b/Common++/src/IpAddress.cpp index dd1c1a3d5f..781a053188 100644 --- a/Common++/src/IpAddress.cpp +++ b/Common++/src/IpAddress.cpp @@ -66,6 +66,7 @@ namespace pcpp } catch (const std::invalid_argument& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR(e.what()); return false; } @@ -126,6 +127,7 @@ namespace pcpp } catch (const std::invalid_argument& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR(e.what()); return false; } diff --git a/Common++/src/IpUtils.cpp b/Common++/src/IpUtils.cpp index 3c812d5bfe..b4b21689a8 100644 --- a/Common++/src/IpUtils.cpp +++ b/Common++/src/IpUtils.cpp @@ -37,6 +37,7 @@ namespace pcpp } catch (const std::invalid_argument& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_DEBUG is disabled PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); return nullptr; } @@ -61,6 +62,7 @@ namespace pcpp } catch (const std::invalid_argument& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_DEBUG is disabled PCPP_LOG_DEBUG("Extraction failed: " << e.what() << " Returning nullptr."); return nullptr; } diff --git a/Common++/src/Logger.cpp b/Common++/src/Logger.cpp index 7608911b6b..4468f37a13 100644 --- a/Common++/src/Logger.cpp +++ b/Common++/src/Logger.cpp @@ -1,56 +1,90 @@ -#include #include "Logger.h" +#include +#include + namespace pcpp { - Logger::Logger() : m_LogsEnabled(true), m_LogPrinter(&defaultLogPrinter) { m_LastError.reserve(200); for (int i = 0; i < NumOfLogModules; i++) - m_LogModulesArray[i] = Info; + m_LogModulesArray[i] = LogLevel::Info; } std::string Logger::logLevelAsString(LogLevel logLevel) { switch (logLevel) { - case Logger::Error: + case LogLevel::Off: + return "OFF"; + case LogLevel::Error: return "ERROR"; - case Logger::Info: + case LogLevel::Info: return "INFO"; - default: + case LogLevel::Debug: return "DEBUG"; + default: + return "UNKNOWN"; } } - void Logger::defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, - const std::string& method, const int line) + std::unique_ptr Logger::createLogContext() { - std::ostringstream sstream; - sstream << file << ": " << method << ":" << line; - std::cerr << std::left << "[" << std::setw(5) << Logger::logLevelAsString(logLevel) << ": " << std::setw(45) - << sstream.str() << "] " << logMessage << std::endl; + return createLogContext(LogLevel::Info, {}); // call the other createLogContext method + } + std::unique_ptr Logger::createLogContext(LogLevel level, LogSource const& source) + { + if (m_UseContextPooling) + { + auto ctx = m_LogContextPool.acquireObject(); + ctx->init(level, source); + return ctx; + } + return std::unique_ptr(new internal::LogContext(level, source)); } - std::ostringstream* Logger::internalCreateLogStream() + void Logger::emit(std::unique_ptr message) { - return new std::ostringstream(); + emit(message->m_Source, message->level, message->m_Stream.str()); + // Pushes the message back to the pool if pooling is enabled. Otherwise, the message is deleted. + if (m_UseContextPooling) + { + m_LogContextPool.releaseObject(std::move(message)); + } } - void Logger::internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file, - const char* method, int line) + void Logger::emit(LogSource const& source, LogLevel logLevel, std::string const& message) { - std::string logMessage = logStream->str(); - delete logStream; - if (logLevel == Logger::Error) + if (logLevel == LogLevel::Error) { - m_LastError = logMessage; + m_LastError = message; } if (m_LogsEnabled) { - m_LogPrinter(logLevel, logMessage, file, method, line); + m_LogPrinter(logLevel, message, source.file, source.function, source.line); + } + } + + void Logger::log(std::unique_ptr message) + { + if (shouldLog(message->level, message->m_Source.logModule)) + { + emit(std::move(message)); } } + void Logger::defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file, + const std::string& method, const int line) + { + // This mutex is used to prevent multiple threads from writing to the console at the same time. + static std::mutex logMutex; + + std::ostringstream sstream; + sstream << file << ": " << method << ":" << line; + + std::unique_lock lock(logMutex); + std::cerr << std::left << "[" << std::setw(5) << Logger::logLevelAsString(logLevel) << ": " << std::setw(45) + << sstream.str() << "] " << logMessage << std::endl; + } } // namespace pcpp diff --git a/Pcap++/src/NetworkUtils.cpp b/Pcap++/src/NetworkUtils.cpp index 6e595adfc8..f030d62cb1 100644 --- a/Pcap++/src/NetworkUtils.cpp +++ b/Pcap++/src/NetworkUtils.cpp @@ -1,4 +1,4 @@ -#define LOG_MODULE NetworkUtils +#define LOG_MODULE PcapLogModuleNetworkUtils #include #include diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 6eef2111d2..c30b3c47e0 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -1044,6 +1044,7 @@ namespace pcpp } catch (const std::exception& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR("Error retrieving default gateway address: " << e.what()); } break; diff --git a/Pcap++/src/PcapLiveDeviceList.cpp b/Pcap++/src/PcapLiveDeviceList.cpp index 9fb46b4ce4..a68e65a406 100644 --- a/Pcap++/src/PcapLiveDeviceList.cpp +++ b/Pcap++/src/PcapLiveDeviceList.cpp @@ -51,6 +51,7 @@ namespace pcpp } catch (const std::exception& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR(e.what()); } diff --git a/Pcap++/src/PcapRemoteDeviceList.cpp b/Pcap++/src/PcapRemoteDeviceList.cpp index b725f3b817..5eea5fe0e5 100644 --- a/Pcap++/src/PcapRemoteDeviceList.cpp +++ b/Pcap++/src/PcapRemoteDeviceList.cpp @@ -96,6 +96,7 @@ namespace pcpp } catch (const std::exception& e) { + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR(e.what()); return nullptr; } @@ -120,6 +121,7 @@ namespace pcpp { delete device; } + (void)e; // Suppress the unreferenced local variable warning when PCPP_LOG_ERROR is disabled PCPP_LOG_ERROR("Error creating remote devices: " << e.what()); return nullptr; } diff --git a/Tests/Packet++Test/main.cpp b/Tests/Packet++Test/main.cpp index 4163780110..aec896dae7 100644 --- a/Tests/Packet++Test/main.cpp +++ b/Tests/Packet++Test/main.cpp @@ -85,6 +85,7 @@ int main(int argc, char* argv[]) #endif // The logger singleton looks like a memory leak. Invoke it before starting the memory check + // Context pooling can cause issues if the logger's built-in context pool allocates new LogContext instances. pcpp::Logger::getInstance(); // cppcheck-suppress knownConditionTrueFalse diff --git a/Tests/Pcap++Test/Tests/LoggerTests.cpp b/Tests/Pcap++Test/Tests/LoggerTests.cpp index 00f06d332a..e40b235ea8 100644 --- a/Tests/Pcap++Test/Tests/LoggerTests.cpp +++ b/Tests/Pcap++Test/Tests/LoggerTests.cpp @@ -136,7 +136,7 @@ class LoggerCleaner ~LoggerCleaner() { pcpp::Logger::getInstance().enableLogs(); - pcpp::Logger::getInstance().setAllModulesToLogLevel(pcpp::Logger::Info); + pcpp::Logger::getInstance().setAllModulesToLogLevel(pcpp::LogLevel::Info); pcpp::Logger::getInstance().resetLogPrinter(); std::cout.clear(); LogPrinter::clean(); diff --git a/Tests/Pcap++Test/main.cpp b/Tests/Pcap++Test/main.cpp index 784a9e0d6f..7445c3fe7f 100644 --- a/Tests/Pcap++Test/main.cpp +++ b/Tests/Pcap++Test/main.cpp @@ -144,6 +144,7 @@ int main(int argc, char* argv[]) #endif // The logger singleton looks like a memory leak. Invoke it before starting the memory check + // Context pooling can cause issues if the logger's built-in context pool allocates new LogContext instances. pcpp::Logger::getInstance(); // cppcheck-suppress knownConditionTrueFalse