/* Copyright 2017 - 2026 R. Thomas
* Copyright 2017 - 2026 Quarkslab
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "LIEF/config.h"
#include "LIEF/logging.hpp"
#include "LIEF/platforms.hpp"
#include "logging.hpp"
#include "spdlog/spdlog.h"
#if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/bundled/args.h"
#else
#include "fmt/args.h"
#endif
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#if defined(__ANDROID__)
#include "spdlog/sinks/android_sink.h"
#endif
namespace LIEF::logging {
static std::mutex Mutex; // NOLINT
std::shared_ptr<spdlog::logger> create_basic_logger_mt(const std::string& name,
const std::string& path,
bool truncate = false) {
spdlog::filename_t fname(path.begin(), path.end());
return spdlog::basic_logger_mt(name, fname, truncate);
}
static std::shared_ptr<spdlog::logger>
default_logger([[maybe_unused]] const std::string& name = "LIEF",
[[maybe_unused]] const std::string& logcat_tag = "",
[[maybe_unused]] const std::string& filepath = "/tmp/lief.log",
[[maybe_unused]] bool truncate = true) {
std::scoped_lock lock(Mutex);
auto& registry = spdlog::details::registry::instance();
registry.drop(name);
std::shared_ptr<spdlog::logger> sink;
if constexpr (current_platform() == PLATFORMS::PLAT_ANDROID) {
#if defined(__ANDROID__)
sink =
spdlog::android_logger_mt(name, !logcat_tag.empty() ? logcat_tag : name);
#endif
} else if (current_platform() == PLATFORMS::PLAT_IOS) {
sink = create_basic_logger_mt(name, filepath, truncate);
} else {
sink = spdlog::stderr_color_mt(name);
}
sink->set_level(spdlog::level::warn);
sink->set_pattern("%v");
sink->flush_on(spdlog::level::warn);
return sink;
}
LEVEL Logger::get_level() {
spdlog::level::level_enum lvl = sink_->level();
switch (lvl) {
default:
case spdlog::level::level_enum::off: return LEVEL::OFF;
case spdlog::level::level_enum::trace: return LEVEL::TRACE;
case spdlog::level::level_enum::debug: return LEVEL::DEBUG;
case spdlog::level::level_enum::info: return LEVEL::INFO;
case spdlog::level::level_enum::warn: return LEVEL::WARN;
case spdlog::level::level_enum::err: return LEVEL::ERR;
case spdlog::level::level_enum::critical: return LEVEL::CRITICAL;
}
return LEVEL::TRACE;
}
Logger& Logger::instance(const char* name) {
static instances_t instances;
{
std::scoped_lock lock(Mutex);
if (auto it = instances.find(name); it != instances.end()) {
return *it->second;
}
}
std::unique_ptr<Logger> impl{new Logger(default_logger(/*name=*/name))};
std::scoped_lock lock(Mutex);
return *instances.insert({name, std::move(impl)}).first->second;
}
void Logger::reset() {
set_logger(default_logger());
}
Logger& Logger::set_log_path(const std::string& path) {
std::scoped_lock lock(Mutex);
auto& registry = spdlog::details::registry::instance();
registry.drop(DEFAULT_NAME);
auto logger = create_basic_logger_mt(DEFAULT_NAME, path, /*truncate=*/true);
set_logger(std::move(logger));
return *this;
}
void Logger::set_logger(std::shared_ptr<spdlog::logger> logger) {
sink_ = std::move(logger);
sink_->set_pattern("%v");
sink_->set_level(spdlog::level::warn);
sink_->flush_on(spdlog::level::warn);
}
const char* to_string(LEVEL e) {
switch (e) {
case LEVEL::OFF: return "OFF";
case LEVEL::TRACE: return "TRACE";
case LEVEL::DEBUG: return "DEBUG";
case LEVEL::INFO: return "INFO";
case LEVEL::ERR: return "ERROR";
case LEVEL::WARN: return "WARN";
case LEVEL::CRITICAL: return "CRITICAL";
default: return "UNDEFINED";
}
return "UNDEFINED";
}
void Logger::set_level(LEVEL level) {
if constexpr (!lief_logging_support) {
return;
}
switch (level) {
case LEVEL::OFF:
{
sink_->set_level(spdlog::level::off);
sink_->flush_on(spdlog::level::off);
break;
}
case LEVEL::TRACE:
{
sink_->set_level(spdlog::level::trace);
sink_->flush_on(spdlog::level::trace);
break;
}
case LEVEL::DEBUG:
{
sink_->set_level(spdlog::level::debug);
sink_->flush_on(spdlog::level::debug);
break;
}
case LEVEL::INFO:
{
sink_->set_level(spdlog::level::info);
sink_->flush_on(spdlog::level::info);
break;
}
default:
case LEVEL::WARN:
{
sink_->set_level(spdlog::level::warn);
sink_->flush_on(spdlog::level::warn);
break;
}
case LEVEL::ERR:
{
sink_->set_level(spdlog::level::err);
sink_->flush_on(spdlog::level::err);
break;
}
case LEVEL::CRITICAL:
{
sink_->set_level(spdlog::level::critical);
sink_->flush_on(spdlog::level::critical);
break;
}
}
}
// Public interface
void disable() {
Logger::instance().disable();
}
void enable() {
Logger::instance().enable();
}
void set_level(LEVEL level) {
Logger::instance().set_level(level);
}
void set_path(const std::string& path) {
Logger::instance().set_log_path(path);
}
void set_logger(std::shared_ptr<spdlog::logger> logger) {
Logger::instance().set_logger(std::move(logger));
}
void reset() {
Logger::instance().reset();
}
LEVEL get_level() {
return Logger::instance().get_level();
}
void log(LEVEL level, const std::string& msg) {
switch (level) {
case LEVEL::OFF: break;
case LEVEL::TRACE:
case LEVEL::DEBUG:
{
LIEF_DEBUG("{}", msg);
break;
}
case LEVEL::INFO:
{
LIEF_INFO("{}", msg);
break;
}
case LEVEL::WARN:
{
LIEF_WARN("{}", msg);
break;
}
case LEVEL::CRITICAL:
case LEVEL::ERR:
{
LIEF_ERR("{}", msg);
break;
}
}
}
void log(LEVEL level, const std::string& fmt,
const std::vector<std::string>& args) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
for (const std::string& arg : args) {
store.push_back(arg);
}
std::string result = fmt::vformat(fmt, store);
log(level, result);
}
namespace named {
LEVEL get_level(const char* name) {
return Logger::instance(name).get_level();
}
void disable(const char* name) {
Logger::instance(name).disable();
}
void enable(const char* name) {
Logger::instance(name).enable();
}
void set_level(const char* name, LEVEL level) {
Logger::instance(name).set_level(level);
}
void set_path(const char* name, const std::string& path) {
Logger::instance(name).set_log_path(path);
}
void log(const char* name, LEVEL level, const std::string& msg) {
switch (level) {
case LEVEL::OFF: return;
case LEVEL::TRACE:
case LEVEL::DEBUG: return Logger::instance(name).debug("{}", msg);
case LEVEL::INFO: return Logger::instance(name).info("{}", msg);
case LEVEL::WARN: return Logger::instance(name).warn("{}", msg);
case LEVEL::ERR: return Logger::instance(name).err("{}", msg);
case LEVEL::CRITICAL: return Logger::instance(name).critial("{}", msg);
}
}
void set_logger(const char* name, std::shared_ptr<spdlog::logger> logger) {
Logger::instance(name).set_logger(std::move(logger));
}
void reset(const char* name) {
Logger::instance(name).reset();
}
spdlog::logger& get_sink(const char* name) {
return Logger::instance(name).sink();
}
}
}