Skip to content
Draft
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: 2 additions & 0 deletions libsolutil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ set(sources
Keccak256.h
LazyInit.h
LEB128.h
logging.cpp
logging.h
Numeric.cpp
Numeric.h
picosha2.h
Expand Down
89 changes: 89 additions & 0 deletions libsolutil/logging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <libsolutil/logging.h>

#include <map>
#include <optional>
#include <vector>

using namespace solidity;

struct Registry::Impl {
std::map<std::string, std::unique_ptr<Logger>, std::less<>> loggers;
// Preset levels: applied to new loggers on creation (most specific match wins)
std::vector<std::pair<std::string, LogLevel>> presets;
std::FILE* output = stderr;

LogLevel resolve_level(std::string const& name) const {
LogLevel result = LogLevel::info;
std::size_t best_match = 0;
bool matched = false;
for (auto const& [prefix, level] : presets) {
if (prefix.empty() || name == prefix || name.starts_with(prefix + ".")) {
if (!matched || prefix.size() >= best_match) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coding style error

best_match = prefix.size();
result = level;
matched = true;
}
}
}
return result;
}
};

Registry::Registry() : m_impl(std::make_unique<Impl>()) {}
Registry::~Registry() = default;

Registry& Registry::instance() {
static Registry reg;
return reg;
}

Logger& Registry::get(std::string const& name) {
auto it = m_impl->loggers.find(name);
if (it != m_impl->loggers.end())
return *it->second;

auto level = m_impl->resolve_level(name);
auto logger = std::make_unique<Logger>(name, level, m_impl->output);
auto& ref = *logger;
m_impl->loggers.emplace(name, std::move(logger));
return ref;
}

void Registry::set_level(std::string_view prefix, LogLevel level) {
// Store as preset for future loggers
std::string key(prefix);
bool found = false;
for (auto& [p, l] : m_impl->presets) {
if (p == key) { l = level; found = true; break; }
}
if (!found)
m_impl->presets.emplace_back(key, level);

// Apply to existing loggers
for (auto& [name, logger] : m_impl->loggers) {
if (key.empty() || name == prefix || name.starts_with(key + "."))
logger->set_level(level);
}
}

void Registry::set_output(std::FILE* output) {
m_impl->output = output;
}

void Registry::clear() {
m_impl->loggers.clear();
}

namespace solidity {

std::optional<LogLevel> parseLogLevel(std::string_view s) {
if (s == "trace") return LogLevel::trace;
if (s == "debug") return LogLevel::debug;
if (s == "info") return LogLevel::info;
if (s == "warn") return LogLevel::warn;
if (s == "error") return LogLevel::error;
if (s == "off") return LogLevel::off;
return std::nullopt;
}

}
102 changes: 102 additions & 0 deletions libsolutil/logging.h
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's lot of stuff here that does not follow our style. Is it copied from somewhere?

  • Braces not on a new line
  • Abbreviated or even single-letter variable names
  • No _ prefix in function parameters
  • Whole enums squished into a single line
  • Function names with underscores rather than camel case
  • The file name starts with a lowercase letter (logging.cpp)
  • Tons of auto even where using the type directly is straightforward
  • No SPDX comment (or copyright boilerplace, though TBH I'd be glad to get rid of it at some point)

Copy link
Copy Markdown
Member

@clonker clonker Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think it is productive to drive-by review this PR at that level especially considering in the state it is in right now? while it is being in draft and clearly not in a finalized state?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but SSA CFG-related PRs tend to get merged very quickly, sometimes before I manage to even take a look at them so I learned that I have to act fast and leave a comment if something catches my eye. This bit wasn't my focus here, but it stood out and it wasn't very clear to me how close to completion this PR really was as I only skimmed through parts of it and it has no description (is it a normal initial version or just a messy final version?) so I decided it's better to comment on it regardless. Note that I intentionally wrapped it into a single comment rather than flood you with comments on every individual thing.

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#pragma once

#include <libsolutil/Exceptions.h>

#include <fmt/format.h>

#include <cstdio>
#include <memory>
#include <optional>
#include <string>
#include <string_view>

namespace solidity
{

enum class LogLevel : std::uint8_t { trace = 0, debug = 1, info = 2, warn = 3, error = 4, off = 5 };

/// Guard macro: skips argument evaluation when the logger's level is too low.
/// SOL_LOG(logger, debug, "x={} y={}", expensive1(), expensive2());
#define SOL_LOG(logger, lvl, ...) \
do { if ((logger).should_log(::solidity::LogLevel::lvl)) [[unlikely]] (logger).lvl(__VA_ARGS__); } while (0)

class Logger {
public:
explicit Logger(std::string name, LogLevel level = LogLevel::info, std::FILE* output = stdout)
: m_name(std::move(name)),
m_level(static_cast<std::uint8_t>(level)),
m_output(output) {}

[[nodiscard]] bool should_log(LogLevel level) const noexcept {
return static_cast<std::uint8_t>(level) >= m_level;
}

void set_level(LogLevel level) noexcept {
m_level = static_cast<std::uint8_t>(level);
}

[[nodiscard]] LogLevel level() const noexcept {
return static_cast<LogLevel>(m_level);
}

[[nodiscard]] std::string const& name() const noexcept { return m_name; }

void set_output(std::FILE* output) noexcept { m_output = output; }

template <typename... Args>
void log(LogLevel const level, fmt::format_string<Args...> fmt, Args&&... args) const {
if (!should_log(level))
return;
fmt::print(m_output, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
SOL_NOINLINE void trace(fmt::format_string<Args...> fmt, Args&&... args) const {
log(LogLevel::trace, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
SOL_NOINLINE void debug(fmt::format_string<Args...> fmt, Args&&... args) const {
log(LogLevel::debug, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
SOL_NOINLINE void info(fmt::format_string<Args...> fmt, Args&&... args) const {
log(LogLevel::info, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
SOL_NOINLINE void warn(fmt::format_string<Args...> fmt, Args&&... args) const {
log(LogLevel::warn, fmt, std::forward<Args>(args)...);
}

template <typename... Args>
SOL_NOINLINE void error(fmt::format_string<Args...> fmt, Args&&... args) const {
log(LogLevel::error, fmt, std::forward<Args>(args)...);
}

private:
std::string m_name;
std::uint8_t m_level;
std::FILE* m_output;
};

class Registry {
public:
static Registry& instance();

Logger& get(std::string const& name);
void set_level(std::string_view prefix, LogLevel level);
void set_output(std::FILE* output);
void clear();

private:
Registry();
~Registry();
struct Impl;
std::unique_ptr<Impl> m_impl;
};

std::optional<LogLevel> parseLogLevel(std::string_view s);

}
Loading