Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -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 =
1 change: 1 addition & 0 deletions Common++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(
header/Logger.h
header/LRUList.h
header/MacAddress.h
header/ObjectPool.h
header/OUILookup.h
header/PcapPlusPlusVersion.h
header/PointerVector.h
Expand Down
1 change: 0 additions & 1 deletion Common++/header/IpAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -876,5 +876,4 @@ namespace pcpp
oss << network.toString();
return oss;
}

} // namespace pcpp
272 changes: 221 additions & 51 deletions Common++/header/Logger.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#pragma once

#include <cstdio>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint>
#include <memory>
#include <array>
#include <mutex>
#include <ostream>
#include <sstream>
#include "DeprecationUtils.h"
#include "ObjectPool.h"

#ifndef LOG_MODULE
# define LOG_MODULE UndefinedLogModule
Expand All @@ -17,35 +21,29 @@
# 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 PCPP_LOG_* macro below the PCPP_ACTIVE_LOG_LEVEL will be removed at compile time.
// Uses the PCPP_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 // !PCPP_ACTIVE_LOG_LEVEL

/// @namespace pcpp
/// @brief The main namespace for the PcapPlusPlus lib
namespace pcpp
{
/// Cross-platform and thread-safe version of strerror
/// @param errnum Value of errno
/// @return String representation of the error number
std::string getErrorString(int errnum);

/// An enum representing all PcapPlusPlus modules
enum LogModule : uint8_t
Expand Down Expand Up @@ -75,6 +73,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++)
Expand Down Expand Up @@ -109,14 +108,100 @@ namespace pcpp
PcapLogModuleDpdkDevice, ///< DpdkDevice module (Pcap++)
PcapLogModuleKniDevice, ///< KniDevice module (Pcap++)
PcapLogModuleXdpDevice, ///< XdpDevice module (Pcap++)
NetworkUtils, ///< NetworkUtils module (Pcap++)
PcapLogModuleNetworkUtils, ///< Network Utils module (Pcap++)
NumOfLogModules
};

/// Cross-platform and thread-safe version of strerror
/// @param errnum Value of errno
/// @return String representation of the error number
std::string getErrorString(int errnum);
/// @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<std::underlying_type<LogLevel>::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 = {}) : m_Source(source), m_Level(level)
{}

/// @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;
m_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 <class T> inline LogContext& operator<<(T const& value)
{
m_Stream << value;
return *this;
}

private:
std::ostringstream m_Stream;
LogSource m_Source;
LogLevel m_Level = LogLevel::Info;
};
} // namespace internal

/// @class Logger
/// PcapPlusPlus logger manager.
Expand All @@ -138,14 +223,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 : uint8_t
{
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
/// Log printer callback. Used for printing the logs in a custom way.
Expand All @@ -154,7 +239,10 @@ 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
using LogPrinter = void (*)(LogLevel, const std::string&, const std::string&, const std::string&, const int);
/// @remarks The printer callback should support being called from multiple threads simultaneously.
using LogPrinter =
std::add_pointer<void(LogLevel logLevel, const std::string& logMessage, const std::string& file,
const std::string& method, const int line)>::type;

/// A static method for converting the log level enum to a string.
/// @param[in] logLevel A log level enum
Expand Down Expand Up @@ -182,7 +270,16 @@ namespace pcpp
/// @return True if this module log level is "debug". False otherwise
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;
}

/// Set all PcapPlusPlus modules to a certain log level
Expand All @@ -209,8 +306,9 @@ namespace pcpp
}

/// @return Get the last error message
std::string getLastError()
std::string getLastError() const
{
std::lock_guard<std::mutex> lock(m_LastErrorMtx);
return m_LastError;
}

Expand All @@ -233,17 +331,32 @@ namespace pcpp
return m_LogsEnabled;
}

template <class T> Logger& operator<<(const T& msg)
/// @brief Controls if the logger should use a pool of LogContext objects.
///
/// If enabled is set to false, preallocate and maxPoolSize are ignored.
/// @param enabled True to enable context pooling, false to disable.
/// @param preallocate The number of LogContext objects to preallocate in the pool.
/// @param maxPoolSize The maximum number of LogContext objects to keep in the pool.
/// @remarks Disabling the pooling clears the pool.
void useContextPooling(bool enabled, std::size_t preallocate = 2, std::size_t maxPoolSize = 10)
{
(*m_LogStream) << msg;
return *this;
}
m_UseContextPooling = enabled;

static std::ostringstream* internalCreateLogStream();
if (m_UseContextPooling)
{
m_LogContextPool.setMaxSize(maxPoolSize);

/// An internal method to print log messages. Shouldn't be used externally.
void internalPrintLogMessage(std::ostringstream* logStream, Logger::LogLevel logLevel, const char* file,
const char* method, int line);
if (preallocate > 0)
{
m_LogContextPool.preallocate(preallocate);
}
}
else
{
// Clear the pool if we're disabling pooling.
m_LogContextPool.clear();
}
}

/// Get access to Logger singleton
/// @todo: make this singleton thread-safe/
Expand All @@ -254,17 +367,74 @@ namespace pcpp
return instance;
}

/// @brief Creates a new LogContext with Info level and no source.
/// @return A new LogContext.
std::unique_ptr<internal::LogContext> 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<internal::LogContext> 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<internal::LogContext> message);

private:
bool m_LogsEnabled;
Logger::LogLevel m_LogModulesArray[NumOfLogModules]{};
std::array<LogLevel, NumOfLogModules> m_LogModulesArray;
LogPrinter m_LogPrinter;

mutable std::mutex m_LastErrorMtx;
std::string m_LastError;
std::ostringstream* m_LogStream{};

bool m_UseContextPooling = true;
// Keep a maximum of 10 LogContext objects in the pool.
internal::DynamicObjectPool<internal::LogContext> m_LogContextPool{ 10, 2 };

// private c'tor - this class is a singleton
Logger();

static void defaultLogPrinter(LogLevel logLevel, const std::string& logMessage, const std::string& file,
const std::string& method, int line);
};

} // namespace pcpp

#define PCPP_LOG(level, message) \
do \
{ \
auto& logger = pcpp::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
Loading
Loading