#pragma once
#include <cstddef>
#include <cstdint>
#include <string_view>
#include <fmt/core.h>
consteval const char *relative_name(const char *path)
{
const char *relative = path;
const char *after_last_slash = path;
bool sep_before = true;
#ifdef _WIN32
auto is_sep = [](char c) consteval { return c == '/' || c == '\\'; };
#else
auto is_sep = [](char c) consteval { return c == '/'; };
#endif
for (const char *p = path; *p != '\0'; ++p) {
if (sep_before && p[0] == 's' && p[1] == 'r' && p[2] == 'c' && is_sep(p[3])) {
relative = p + 4;
}
if (is_sep(*p)) {
sep_before = true;
after_last_slash = p + 1;
} else {
sep_before = false;
}
}
return relative != path ? relative : after_last_slash;
}
static_assert(std::string_view{relative_name("src/bar.cpp")} == std::string_view{"bar.cpp"});
static_assert(
std::string_view{relative_name("/foo/src/bar/src/bar.cpp")} == std::string_view{"bar.cpp"});
static_assert(std::string_view{relative_name(__FILE__)} == std::string_view{"log.hpp"});
static_assert(std::string_view{relative_name("/srcsrc/bar.cpp")} == std::string_view{"bar.cpp"});
#define DDWAF_LOG_HELPER(level, function, file, line, fmt_str, ...) \
{ \
if (ddwaf::logger::valid(level)) { \
try { \
constexpr const char *filename = relative_name(file); \
auto message = ddwaf::fmt::format(fmt_str, ##__VA_ARGS__); \
ddwaf::logger::log( \
level, function, filename, line, message.c_str(), message.size()); \
} catch (...) {} \
} \
}
#define DDWAF_LOG(level, fmt, ...) \
DDWAF_LOG_HELPER(level, __func__, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define DDWAF_TRACE(fmt, ...) DDWAF_LOG(ddwaf::log_level::trace, fmt, ##__VA_ARGS__)
#define DDWAF_DEBUG(fmt, ...) DDWAF_LOG(ddwaf::log_level::debug, fmt, ##__VA_ARGS__)
#define DDWAF_INFO(fmt, ...) DDWAF_LOG(ddwaf::log_level::info, fmt, ##__VA_ARGS__)
#define DDWAF_WARN(fmt, ...) DDWAF_LOG(ddwaf::log_level::warn, fmt, ##__VA_ARGS__)
#define DDWAF_ERROR(fmt, ...) DDWAF_LOG(ddwaf::log_level::error, fmt, ##__VA_ARGS__)
namespace ddwaf {
enum class log_level : uint32_t { trace, debug, info, warn, error, off };
inline std::string_view log_level_to_str(log_level level)
{
switch (level) {
case log_level::trace:
return "trace";
case log_level::debug:
return "debug";
case log_level::error:
return "error";
case log_level::warn:
return "warn";
case log_level::info:
return "info";
case log_level::off:
break;
}
return "off";
}
class logger {
public:
using log_cb_type = void (*)(log_level level, const char *function, const char *file,
unsigned line, const char *message, uint64_t message_len);
static void init(log_cb_type cb, log_level min_level);
static bool valid(log_level level) { return cb != nullptr && level >= min_level; }
static void log(log_level level, const char *function, const char *file, unsigned line,
const char *message, size_t length);
private:
static log_cb_type cb;
static log_level min_level;
};
}