use spdlog as logger backend

split verbosity and log level
use backtrace to replay logs on critical error
use backtrace to replay logs emitted before setting the log level, with the appropriate level
make libmamba compile time log level a cmake option
break circular dependencies
remove hard-coded config loading sequence
This commit is contained in:
Adrien DELSALLE 2021-11-05 08:57:40 +01:00
parent 0f3901ff16
commit 95e65b8114
No known key found for this signature in database
GPG Key ID: 639D9226C33B92BB
25 changed files with 398 additions and 248 deletions

View File

@ -51,6 +51,15 @@ option(BUILD_TESTS "Build libmamba C++ tests" OFF)
option(BUILD_SHARED "Build shared libmamba library" OFF)
option(BUILD_STATIC "Build static libmamba library" OFF)
option(BUILD_STATIC_DEPS "Build static libmamba library with static linkage to its dependencies" OFF)
set(BUILD_LOG_LEVEL "TRACE" CACHE STRING "Logger active level at compile time")
if (NOT ${BUILD_LOG_LEVEL} MATCHES "^(TRACE|DEBUG|INFO|WARN|ERROR|CRITICAL|OFF)$")
message(FATAL_ERROR "Invalid log level: ${BUILD_LOG_LEVEL}, should be one of { TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL, OFF }")
endif ()
if (BUILD_STATIC_DEPS)
add_definitions("-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${BUILD_LOG_LEVEL}")
endif ()
if (BUILD_STATIC_DEPS)
add_definitions(-DLIBMAMBA_STATIC_DEPS)

View File

@ -18,3 +18,4 @@ dependencies:
- yaml-cpp
- termcolor-cpp
- cli11
- spdlog

View File

@ -16,6 +16,8 @@
#include <yaml-cpp/yaml.h>
#include "spdlog/spdlog.h"
#include <functional>
@ -28,7 +30,6 @@
#define CONFIG_DEBUGGING \
if (Configuration::instance().at("print_config_only").value<bool>()) \
{ \
Configuration::instance().at("quiet").set_value(true); \
int dump_opts \
= MAMBA_SHOW_CONFIG_VALUES | MAMBA_SHOW_CONFIG_SRCS | MAMBA_SHOW_ALL_CONFIGS; \
std::cout << Configuration::instance().dump(dump_opts) << std::endl; \
@ -163,6 +164,35 @@ namespace YAML
return true;
}
};
template <>
struct convert<spdlog::level::level_enum>
{
static Node encode(const spdlog::level::level_enum& rhs)
{
using namespace spdlog::level;
return Node(to_string_view(rhs).data());
}
static bool decode(const Node& node, spdlog::level::level_enum& rhs)
{
using namespace spdlog::level;
auto name = node.as<std::string>();
auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
if (it != std::end(level_string_views))
{
rhs = static_cast<level_enum>(it - std::begin(level_string_views));
return true;
}
LOG_ERROR
<< "Invalid log level, should be in {'critical', 'error', 'warning', 'info', 'debug', 'trace', 'off'} but is '"
<< name << "'";
return false;
}
};
}
@ -348,6 +378,27 @@ namespace mamba
std::string m_value = "";
};
template <>
struct cli_config<spdlog::level::level_enum>
{
using value_type = spdlog::level::level_enum;
using storage_type = std::string;
cli_config(const storage_type& value)
: m_value(value){};
bool defined()
{
return !m_value.empty();
};
value_type value()
{
return YAML::Node(m_value).as<spdlog::level::level_enum>();
};
storage_type m_value = "";
};
bool has_config_name(const std::string& file);
bool is_config_file(const fs::path& path);
@ -501,11 +552,66 @@ namespace mamba
enum class RCConfigLevel
{
kSystemDir = 0,
kRootPrefix = 1,
kHomeDir = 2,
kHomeDir = 1,
kRootPrefix = 2,
kTargetPrefix = 3
};
} // mamba
namespace YAML
{
template <>
struct convert<mamba::RCConfigLevel>
{
static Node encode(const mamba::RCConfigLevel& rhs)
{
using namespace spdlog::level;
switch (rhs)
{
case mamba::RCConfigLevel::kHomeDir:
return Node("HomeDir");
break;
case mamba::RCConfigLevel::kRootPrefix:
return Node("RootPrefix");
break;
case mamba::RCConfigLevel::kSystemDir:
return Node("SystemDir");
break;
case mamba::RCConfigLevel::kTargetPrefix:
return Node("TargetPrefix");
break;
default:
break;
}
return Node();
}
static bool decode(const Node& node, mamba::RCConfigLevel& rhs)
{
if (!node.IsScalar())
return false;
auto str = node.as<std::string>();
if (str == "HomeDir")
rhs = mamba::RCConfigLevel::kHomeDir;
else if (str == "RootPrefix")
rhs = mamba::RCConfigLevel::kRootPrefix;
else if (str == "SystemDir")
rhs = mamba::RCConfigLevel::kSystemDir;
else if (str == "TargetPrefix")
rhs = mamba::RCConfigLevel::kTargetPrefix;
else
return false;
return true;
}
};
} // YAML
namespace mamba
{
template <class T>
class Configurable
{
@ -562,6 +668,8 @@ namespace mamba
bool configured() const;
bool env_var_active() const;
bool has_single_op_lifetime() const;
self_type& set_rc_value(const T& value, const std::string& source);
@ -784,6 +892,12 @@ namespace mamba
return rc_configured() || env_var_configured() || cli_configured() || api_configured();
};
template <class T>
bool Configurable<T>::env_var_active() const
{
return !Context::instance().no_env || (name() == "no_env");
};
template <class T>
bool Configurable<T>::has_single_op_lifetime() const
{
@ -898,14 +1012,12 @@ namespace mamba
auto Configurable<T>::set_env_var_names(const std::vector<std::string>& names) -> self_type&
{
if (names.empty())
{
m_env_var_names = { "MAMBA_" + to_upper(m_name) };
}
else
{
m_env_var_names = names;
}
m_needed_configs.insert("no_env");
if (name() != "no_env")
m_needed_configs.insert("no_env");
return *this;
}
@ -1421,7 +1533,7 @@ namespace mamba
}
catch (const std::bad_cast& e)
{
LOG_FATAL << "Bad cast of Configurable '" << name() << "'";
LOG_CRITICAL << "Bad cast of Configurable '" << name() << "'";
throw e;
}
};
@ -1800,6 +1912,11 @@ namespace mamba
bool hook_disabled = options & MAMBA_CONF_DISABLE_HOOK;
bool force_compute = options & MAMBA_CONF_FORCE_COMPUTE;
if (force_compute)
LOG_TRACE << "Update configurable '" << name() << "'";
else
LOG_TRACE << "Compute configurable '" << name() << "'";
if (!force_compute && (Configuration::instance().is_loading() && (m_compute_counter > 0)))
throw std::runtime_error("Multiple computation of '" + m_name
+ "' detected during loading sequence.");
@ -1820,7 +1937,7 @@ namespace mamba
m_values.insert({ "CLI", p_cli_config->value() });
}
if (env_var_configured() && !ctx.no_env && (level >= ConfigurationLevel::kEnvVar))
if (env_var_configured() && env_var_active() && (level >= ConfigurationLevel::kEnvVar))
{
for (const auto& env_var : m_env_var_names)
{
@ -1834,9 +1951,9 @@ namespace mamba
}
catch (const YAML::Exception& e)
{
LOG_ERROR << "Bad conversion of configurable '" << name()
<< "' from environment variable '" << env_var << "' with value '"
<< env_var_value << "'";
LOG_CRITICAL << "Bad conversion of configurable '" << name()
<< "' from environment variable '" << env_var
<< "' with value '" << env_var_value << "'";
throw e;
}
}

View File

@ -7,12 +7,14 @@
#ifndef MAMBA_CORE_CONTEXT_HPP
#define MAMBA_CORE_CONTEXT_HPP
#include "mamba/core/mamba_fs.hpp"
#include "spdlog/spdlog.h"
#include <map>
#include <string>
#include <vector>
#include "mamba/core/mamba_fs.hpp"
#define ROOT_ENV_NAME "base"
namespace mamba
@ -86,6 +88,7 @@ namespace mamba
kStrict
};
class Logger;
std::string env_name(const fs::path& prefix);
fs::path locate_prefix_by_name(const std::string& name);
@ -118,7 +121,6 @@ namespace mamba
#endif
};
bool use_index_cache = false;
std::size_t local_repodata_ttl = 1; // take from header
bool offline = false;
@ -129,6 +131,12 @@ namespace mamba
long max_parallel_downloads = 5;
int verbosity = 0;
void set_verbosity(int lvl);
spdlog::level::level_enum log_level = spdlog::level::level_enum::info;
std::string log_pattern = "%^%-8!l%$ %v";
std::size_t log_backtrace = 20;
std::shared_ptr<Logger> logger;
bool dev = false;
bool on_ci = false;
@ -180,8 +188,6 @@ namespace mamba
// Conda compat
bool add_pip_as_python_dependency = true;
void set_verbosity(int lvl);
std::string host_platform = MAMBA_PLATFORM;
std::string platform = MAMBA_PLATFORM;
std::vector<std::string> platforms();

View File

@ -7,6 +7,14 @@
#ifndef MAMBA_CORE_OUTPUT_HPP
#define MAMBA_CORE_OUTPUT_HPP
#include "context.hpp"
#include "progress_bar.hpp"
#include "nlohmann/json.hpp"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <chrono>
#include <iomanip>
#include <iostream>
@ -18,9 +26,6 @@
#include <utility>
#include <vector>
#include "context.hpp"
#include "nlohmann/json.hpp"
#include "progress_bar.hpp"
#define ENUM_FLAG_OPERATOR(T, X) \
inline T operator X(T lhs, T rhs) \
@ -213,7 +218,6 @@ namespace mamba
~ConsoleStream();
};
class Console
{
public:
@ -236,6 +240,13 @@ namespace mamba
static std::string hide_secrets(const std::string_view& str);
void json_print();
void json_write(const nlohmann::json& j);
void json_append(const std::string& value);
void json_append(const nlohmann::json& j);
void json_down(const std::string& key);
void json_up();
private:
Console();
~Console() = default;
@ -247,6 +258,10 @@ namespace mamba
std::mutex m_mutex;
std::unique_ptr<ProgressBarManager> p_progress_manager;
std::string json_hier;
unsigned int json_index;
nlohmann::json json_log;
friend class ProgressProxy;
};
@ -256,77 +271,45 @@ namespace mamba
Console::instance().print_progress(m_idx);
}
#undef TRACE
#undef DEBUG
#undef INFO
#undef WARNING
#undef ERROR
#undef FATAL
enum class LogSeverity
{
kTrace,
kDebug,
kInfo,
kWarning,
kError,
kFatal
};
class MessageLogger
{
public:
MessageLogger(const char* file, int line, LogSeverity severity);
MessageLogger(const char* file, int line, spdlog::level::level_enum level);
~MessageLogger();
std::stringstream& stream();
static LogSeverity& global_log_severity();
private:
std::string m_file;
int m_line;
LogSeverity m_severity;
spdlog::level::level_enum m_level;
std::stringstream m_stream;
};
class JsonLogger
class Logger : public spdlog::logger
{
public:
JsonLogger(const JsonLogger&) = delete;
JsonLogger& operator=(const JsonLogger&) = delete;
Logger(const std::string& pattern);
JsonLogger(JsonLogger&&) = delete;
JsonLogger& operator=(JsonLogger&&) = delete;
static JsonLogger& instance();
nlohmann::json json_log;
void json_write(const nlohmann::json& j);
void json_append(const std::string& value);
void json_append(const nlohmann::json& j);
void json_down(const std::string& key);
void json_up();
private:
JsonLogger();
~JsonLogger() = default;
std::string json_hier;
unsigned int json_index;
void dump_backtrace_no_guards();
};
} // namespace mamba
#undef ERROR
#undef WARNING
#undef FATAL
#undef LOG
#undef LOG_TRACE
#undef LOG_DEBUG
#undef LOG_INFO
#undef LOG_WARNING
#undef LOG_ERROR
#undef LOG_CRITICAL
#define LOG(severity) mamba::MessageLogger(__FILE__, __LINE__, severity).stream()
#define LOG_TRACE LOG(mamba::LogSeverity::kTrace)
#define LOG_DEBUG LOG(mamba::LogSeverity::kDebug)
#define LOG_INFO LOG(mamba::LogSeverity::kInfo)
#define LOG_WARNING LOG(mamba::LogSeverity::kWarning)
#define LOG_ERROR LOG(mamba::LogSeverity::kError)
#define LOG_FATAL LOG(mamba::LogSeverity::kFatal)
#define LOG_TRACE LOG(spdlog::level::trace)
#define LOG_DEBUG LOG(spdlog::level::debug)
#define LOG_INFO LOG(spdlog::level::info)
#define LOG_WARNING LOG(spdlog::level::warn)
#define LOG_ERROR LOG(spdlog::level::err)
#define LOG_CRITICAL LOG(spdlog::level::critical)
#endif // MAMBA_OUTPUT_HPP

View File

@ -8,6 +8,7 @@
#define MAMBA_CORE_PROGRESS_BAR_HPP
#include <string_view>
#include <mutex>
namespace mamba
{

View File

@ -18,6 +18,8 @@
#include <nlohmann/json.hpp>
#include "spdlog/spdlog.h"
#include <algorithm>
#include <stdexcept>
@ -231,31 +233,32 @@ namespace mamba
void post_root_prefix_rc_loading()
{
auto& config = Configuration::instance();
if (!Context::instance().no_rc)
rc_loading_hook(RCConfigLevel::kHomeDir);
{
rc_loading_hook(RCConfigLevel::kRootPrefix);
config.at("no_env").compute(MAMBA_CONF_FORCE_COMPUTE);
}
}
void post_target_prefix_rc_loading()
{
auto& config = Configuration::instance();
if (!Context::instance().no_rc)
{
rc_loading_hook(RCConfigLevel::kTargetPrefix);
config.at("no_env").compute(MAMBA_CONF_FORCE_COMPUTE);
config.at("always_yes").compute(MAMBA_CONF_FORCE_COMPUTE);
config.at("quiet").compute(MAMBA_CONF_FORCE_COMPUTE);
config.at("json").compute(MAMBA_CONF_FORCE_COMPUTE);
}
}
/*
void log_level_hook(LogLevel& lvl)
{
MessageLogger::global_log_level() = lvl;
}
*/
spdlog::level::level_enum log_level_fallback_hook()
{
if (Context::instance().json)
return spdlog::level::off;
else
return spdlog::level::info;
}
void verbose_hook(std::uint8_t& lvl)
{
auto& ctx = Context::instance();
@ -333,12 +336,6 @@ namespace mamba
}
}
void show_banner_hook(bool& show)
{
if (show)
Console::print(banner());
}
void rc_files_hook(std::vector<fs::path>& files)
{
auto& ctx = Context::instance();
@ -389,6 +386,7 @@ namespace mamba
throw std::runtime_error("Aborting.");
}
Configuration::instance().at("quiet").set_value(true);
Configuration::instance().at("json").set_value(false);
}
}
@ -402,6 +400,7 @@ namespace mamba
throw std::runtime_error("Aborting.");
}
Configuration::instance().at("quiet").set_value(true);
Configuration::instance().at("json").set_value(false);
}
}
@ -499,7 +498,7 @@ namespace mamba
insert(Configurable("root_prefix", &ctx.root_prefix)
.group("Basic")
.set_env_var_names()
.needs({ "verbose", "create_base", "rc_files" })
.needs({ "create_base", "rc_files" })
.description("Path to the root prefix")
.set_post_merge_hook(detail::root_prefix_hook)
.set_post_context_hook(detail::post_root_prefix_rc_loading));
@ -516,9 +515,7 @@ namespace mamba
"envs_dirs",
"env_name",
"spec_file_env_name",
"use_target_prefix_fallback",
"verbose",
"always_yes" })
"use_target_prefix_fallback" })
.set_single_op_lifetime()
.description("Path to the target prefix")
.set_post_merge_hook(detail::target_prefix_hook)
@ -888,19 +885,40 @@ namespace mamba
.group("Output, Prompt and Flow Control")
.set_env_var_names()
.description("Only display what would have been done"));
/*
insert(Configurable("log_level", &ctx.verbosity)
.group("Output, Prompt and Flow Control")
.set_env_var_names()
.description("Set the log level")
.long_description(unindent(R"(
Set the log level. Log level can be one of {'off', 'fatal',
'error', 'warning', 'info', 'debug', 'trace'}.)"))
.set_post_merge_hook(detail::log_level_hook));
*/
insert(Configurable("log_level", &ctx.log_level)
.group("Output, Prompt and Flow Control")
.set_rc_configurable()
.set_env_var_names()
.needs({ "json" })
.description("Set the log level")
.set_fallback_value_hook(detail::log_level_fallback_hook)
.long_description(unindent(R"(
Set globally the log level of all loggers. Log level can
be one of {'off', 'fatal', 'error', 'warning', 'info',
'debug', 'trace'}.)")));
insert(Configurable("log_backtrace", &ctx.log_backtrace)
.group("Output, Prompt and Flow Control")
.set_rc_configurable()
.set_env_var_names()
.description("Set the log backtrace size")
.long_description(unindent(R"(
Set the log backtrace size. It will replay the n last
logs if an error is thrown during the execution.)")));
insert(Configurable("log_pattern", &ctx.log_pattern)
.group("Output, Prompt and Flow Control")
.set_rc_configurable()
.set_env_var_names()
.description("Set the log pattern")
.long_description(unindent(R"(
Set the log pattern.)")));
insert(Configurable("json", &ctx.json)
.group("Output, Prompt and Flow Control")
.set_rc_configurable()
.needs({ "print_config_only", "print_context_only" })
.set_env_var_names()
.description("Report all output as json"));
@ -921,7 +939,6 @@ namespace mamba
insert(Configurable("show_banner", true)
.group("Output, Prompt and Flow Control")
.needs({ "quiet", "json" })
.set_post_merge_hook(detail::show_banner_hook)
.set_single_op_lifetime()
.description("Show the banner"));
@ -957,20 +974,19 @@ namespace mamba
.group("Output, Prompt and Flow Control")
.set_rc_configurable()
.set_env_var_names()
.needs({ "json" })
.implies({ "show_banner" })
.needs({ "json", "print_config_only", "print_context_only" })
.description("Set quiet mode (print less output)"));
insert(Configurable("verbose", std::uint8_t(0))
.group("Output, Prompt and Flow Control")
.set_post_merge_hook(detail::verbose_hook)
.description("Set higher verbosity")
.description("Set the verbosity")
.long_description(unindent(R"(
Set a higher log verbosity than the default one.
This configurable has a similar effect as 'log_level',
except it can only increase the log level. If you need
fine-grained control, prefer 'log_level'.
'verbose' and 'log_level' are exclusive.)")));
Set the verbosity of .
The verbosity represent the information
given to the user about the operation asked for.
This information is printed to stdout and should
not be considered as logs (see log_level).)")));
// Config
insert(Configurable("rc_files", std::vector<fs::path>({}))
@ -1084,6 +1100,15 @@ namespace mamba
void Configuration::load()
{
spdlog::set_level(spdlog::level::n_levels);
spdlog::flush_on(spdlog::level::n_levels);
// Hard-coded value assuming it's enough to store the logs emitted
// before setting the log level, flushing the backtrace and setting
// its new capacity
spdlog::enable_backtrace(500);
LOG_DEBUG << "Loading configuration";
clear_rc_sources();
clear_rc_values();
@ -1096,7 +1121,23 @@ namespace mamba
at(c).compute();
}
m_load_lock = false;
LOG_DEBUG << m_config.size() << " configurables computed";
CONFIG_DEBUGGING;
if (at("show_banner").value<bool>())
Console::print(banner());
auto& ctx = Context::instance();
spdlog::set_pattern(ctx.log_pattern);
spdlog::set_level(ctx.log_level);
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->flush(); });
spdlog::flush_on(spdlog::level::off);
Context::instance().logger->dump_backtrace_no_guards();
spdlog::enable_backtrace(ctx.log_backtrace);
}
bool Configuration::is_loading()
@ -1106,20 +1147,11 @@ namespace mamba
void Configuration::compute_loading_sequence()
{
// break circular dependencies
// target_prefix (env configurable) -> no_env (rc configurable) -> rc_files -> target_prefix
// target_prefix -> always_yes (rc configurable) -> rc_files -> target_prefix
// hack to eventually display the banner before log messages. Needs to recompute those
// rc configurable configs in rc_files_hook if any rc file is used
m_loading_sequence
= { "no_env", "always_yes", "debug", "print_context_only", "print_config_only",
"quiet", "json", "show_banner" };
m_loading_sequence.clear();
std::vector<std::string> locks;
for (auto& c : m_config_order)
{
add_to_loading_sequence(m_loading_sequence, c, locks);
}
}
void Configuration::add_to_loading_sequence(std::vector<std::string>& seq,
@ -1223,7 +1255,7 @@ namespace mamba
catch (const std::out_of_range& e)
{
LOG_ERROR << "Configurable '" << name << "' does not exists";
throw e;
throw std::runtime_error("ConfigurationError");
}
}
@ -1244,6 +1276,8 @@ namespace mamba
void Configuration::set_rc_values(std::vector<fs::path> possible_rc_paths,
const RCConfigLevel& level)
{
LOG_TRACE << "Get RC files configuration from locations up to "
<< YAML::Node(level).as<std::string>();
if (possible_rc_paths.empty())
possible_rc_paths = compute_default_rc_sources(level);
@ -1296,7 +1330,7 @@ namespace mamba
if (detail::is_config_file(l))
{
sources.push_back(l);
LOG_DEBUG << "Configuration found at '" << l.string() << "'";
LOG_TRACE << "Configuration found at '" << l.string() << "'";
}
else if (fs::is_directory(l))
{
@ -1305,7 +1339,7 @@ namespace mamba
if (detail::is_config_file(p))
{
sources.push_back(p);
LOG_DEBUG << "Configuration found at '" << p.string() << "'";
LOG_TRACE << "Configuration found at '" << p.string() << "'";
}
else
{
@ -1316,7 +1350,7 @@ namespace mamba
else
{
if (!l.empty())
LOG_DEBUG << "Configuration not found at '" << l.string() << "'";
LOG_TRACE << "Configuration not found at '" << l.string() << "'";
}
}

View File

@ -25,6 +25,8 @@
#include "termcolor/termcolor.hpp"
#include "spdlog/spdlog.h"
namespace mamba
{
static std::map<std::string, std::string> other_pkg_mgr_install_instructions
@ -517,7 +519,9 @@ namespace mamba
if (ctx.freeze_installed)
Console::print("Possible hints:\n - 'freeze_installed' is turned on\n");
throw std::runtime_error("Could not solve for environment specs");
Console::stream() << "The environment can't be solved, aborting the operation";
LOG_ERROR << "Could not solve for environment specs";
throw std::runtime_error("UnsatisfiableError");
}
MTransaction trans(solver, package_caches);
@ -588,11 +592,9 @@ namespace mamba
Console::print(join(
"", std::vector<std::string>({ "Empty environment created at prefix: ", prefix })));
JsonLogger::instance().json_write({ { "success", true } });
Console::instance().json_write({ { "success", true } });
if (Context::instance().json)
Console::instance().print(JsonLogger::instance().json_log.unflatten().dump(4),
true);
Console::instance().json_print();
}
void create_target_directory(const fs::path prefix)

View File

@ -98,14 +98,12 @@ namespace mamba
// TODO do we need to do something wtih `shell_prefix -> root_prefix?`?
if (ctx.json)
{
JsonLogger::instance().json_write(
Console::instance().json_write(
{ { "success", true },
{ "operation", "shell_hook" },
{ "context", { { "shell_type", shell_type } } },
{ "actions", { { "print", { activator->hook() } } } } });
if (Context::instance().json)
Console::instance().print(JsonLogger::instance().json_log.unflatten().dump(4),
true);
Console::instance().json_print();
}
else
{

View File

@ -7,7 +7,6 @@
#include <csignal>
#include "mamba/core/context.hpp"
#include "mamba/core/output.hpp"
#include "mamba/core/environment.hpp"
#include "mamba/core/thread_utils.hpp"
#include "mamba/core/util.hpp"
@ -26,6 +25,10 @@ namespace mamba
}
set_default_signal_handler();
std::shared_ptr<spdlog::logger> l = std::make_shared<Logger>(log_pattern);
spdlog::set_default_logger(l);
logger = std::dynamic_pointer_cast<Logger>(l);
}
Context& Context::instance()
@ -36,19 +39,6 @@ namespace mamba
void Context::set_verbosity(int lvl)
{
MessageLogger::global_log_severity() = mamba::LogSeverity::kWarning;
if (lvl == 1)
{
MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
}
else if (lvl == 2)
{
MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
}
else if (lvl > 2)
{
MessageLogger::global_log_severity() = mamba::LogSeverity::kTrace;
}
this->verbosity = lvl;
}

View File

@ -421,10 +421,6 @@ namespace mamba
|| Context::instance().no_progress_bars;
}
/*****************
* MessageLogger *
*****************/
std::string strip_file_prefix(const std::string& file)
{
#ifdef _WIN32
@ -436,61 +432,46 @@ namespace mamba
return pos != std::string::npos ? file.substr(pos + 1, std::string::npos) : file;
}
MessageLogger::MessageLogger(const char* file, int line, LogSeverity severity)
/*****************
* MessageLogger *
*****************/
MessageLogger::MessageLogger(const char* file, int line, spdlog::level::level_enum level)
: m_file(strip_file_prefix(file))
, m_line(line)
, m_severity(severity)
, m_level(level)
, m_stream()
{
#ifdef MAMBA_DEVELOPMENT
m_stream << m_file << ":" << m_line << " ";
#endif
}
MessageLogger::~MessageLogger()
{
if (m_severity < global_log_severity())
{
return;
}
auto str = Console::hide_secrets(m_stream.str());
switch (m_severity)
switch (m_level)
{
case LogSeverity::kFatal:
std::cerr << termcolor::on_red << "FATAL " << termcolor::reset
<< prepend(str, "", std::string(8, ' ').c_str()) << std::endl;
case spdlog::level::critical:
SPDLOG_CRITICAL(prepend(str, "", std::string(4, ' ').c_str()));
if (Context::instance().log_level != spdlog::level::off)
spdlog::dump_backtrace();
break;
case LogSeverity::kError:
std::cerr << termcolor::red << "ERROR " << termcolor::reset
<< prepend(str, "", std::string(8, ' ').c_str()) << std::endl;
case spdlog::level::err:
SPDLOG_ERROR(prepend(str, "", std::string(4, ' ').c_str()));
break;
case LogSeverity::kWarning:
std::cerr << termcolor::yellow << "WARNING " << termcolor::reset
<< prepend(str, "", std::string(8, ' ').c_str()) << std::endl;
case spdlog::level::warn:
SPDLOG_WARN(prepend(str, "", std::string(4, ' ').c_str()));
break;
case LogSeverity::kInfo:
std::cerr << "INFO " << prepend(str, "", std::string(8, ' ').c_str())
<< std::endl;
case spdlog::level::info:
SPDLOG_INFO(prepend(str, "", std::string(4, ' ').c_str()));
break;
case LogSeverity::kDebug:
std::cerr << termcolor::cyan << "DEBUG " << termcolor::reset
<< prepend(str, "", std::string(8, ' ').c_str()) << std::endl;
case spdlog::level::debug:
SPDLOG_DEBUG(prepend(str, "", std::string(4, ' ').c_str()));
break;
case LogSeverity::kTrace:
std::cerr << termcolor::blue << "TRACE " << termcolor::reset
<< prepend(str, "", std::string(8, ' ').c_str()) << std::endl;
case spdlog::level::trace:
SPDLOG_TRACE(prepend(str, "", std::string(4, ' ').c_str()));
break;
default:
std::cerr << "UNKOWN " << prepend(str, "", std::string(8, ' ').c_str())
<< std::endl;
break;
}
if (m_severity == LogSeverity::kFatal)
{
std::abort();
}
}
std::stringstream& MessageLogger::stream()
@ -498,29 +479,35 @@ namespace mamba
return m_stream;
}
LogSeverity& MessageLogger::global_log_severity()
Logger::Logger(const std::string& pattern)
: spdlog::logger(std::string("mamba"),
std::make_shared<spdlog::sinks::stderr_color_sink_mt>())
{
static LogSeverity sev = LogSeverity::kWarning;
return sev;
// set_pattern("%^[%L %Y-%m-%d %T:%e]%$ %v");
set_pattern(pattern);
}
/***************
* JsonLogger *
***************/
JsonLogger::JsonLogger()
void Logger::dump_backtrace_no_guards()
{
using spdlog::details::log_msg;
if (tracer_.enabled())
{
tracer_.foreach_pop([this](const log_msg& msg) {
if (this->should_log(msg.level))
this->sink_it_(msg);
});
}
}
JsonLogger& JsonLogger::instance()
void Console::json_print()
{
static JsonLogger j;
return j;
if (Context::instance().json)
print(json_log.unflatten().dump(4), true);
}
// write all the key/value pairs of a JSON object into the current entry, which
// is then a JSON object
void JsonLogger::json_write(const nlohmann::json& j)
void Console::json_write(const nlohmann::json& j)
{
if (Context::instance().json)
{
@ -531,7 +518,7 @@ namespace mamba
}
// append a value to the current entry, which is then a list
void JsonLogger::json_append(const std::string& value)
void Console::json_append(const std::string& value)
{
if (Context::instance().json)
{
@ -541,7 +528,7 @@ namespace mamba
}
// append a JSON object to the current entry, which is then a list
void JsonLogger::json_append(const nlohmann::json& j)
void Console::json_append(const nlohmann::json& j)
{
if (Context::instance().json)
{
@ -553,7 +540,7 @@ namespace mamba
}
// go down in the hierarchy in the "key" entry, create it if it doesn't exist
void JsonLogger::json_down(const std::string& key)
void Console::json_down(const std::string& key)
{
if (Context::instance().json)
{
@ -563,7 +550,7 @@ namespace mamba
}
// go up in the hierarchy
void JsonLogger::json_up()
void Console::json_up()
{
if (Context::instance().json)
json_hier.erase(json_hier.rfind('/'));

View File

@ -39,8 +39,8 @@ namespace mamba
{
for (const auto& pkg : packages)
{
LOG_INFO << "Adding virtual package: " << pkg.name << "=" << pkg.version << "="
<< pkg.build_string;
LOG_DEBUG << "Adding virtual package: " << pkg.name << "=" << pkg.version << "="
<< pkg.build_string;
m_package_records.insert({ pkg.name, std::move(pkg) });
}
}

View File

@ -187,7 +187,7 @@ namespace mamba
else
{
// Todo remove double parsing?
LOG_INFO << "Adding job: " << ms.conda_build_form() << std::endl;
LOG_INFO << "Adding job: " << ms.conda_build_form();
Id inst_id = pool_conda_matchspec(reinterpret_cast<Pool*>(m_pool),
ms.conda_build_form().c_str());
queue_push2(&m_jobs, job_flag | SOLVER_SOLVABLE_PROVIDES, inst_id);
@ -348,9 +348,9 @@ namespace mamba
solver_solve(m_solver, &m_jobs);
m_is_solved = true;
LOG_INFO << "Problem count: " << solver_problem_count(m_solver) << std::endl;
LOG_INFO << "Problem count: " << solver_problem_count(m_solver);
success = solver_problem_count(m_solver) == 0;
JsonLogger::instance().json_write({ { "success", success } });
Console::instance().json_write({ { "success", success } });
return success;
}

View File

@ -416,7 +416,7 @@ namespace mamba
selection_solvables((Pool*) pool, &job, &q);
bool remove_success = q.count >= specs_to_remove.size();
JsonLogger::instance().json_write({ { "success", remove_success } });
Console::instance().json_write({ { "success", remove_success } });
Id pkg_id;
Solvable* solvable;
@ -441,8 +441,8 @@ namespace mamba
// if no action required, don't even start logging them
if (!empty())
{
JsonLogger::instance().json_down("actions");
JsonLogger::instance().json_write({ { "PREFIX", Context::instance().target_prefix } });
Console::instance().json_down("actions");
Console::instance().json_write({ { "PREFIX", Context::instance().target_prefix } });
}
queue_free(&q);
queue_free(&decision);
@ -530,8 +530,8 @@ namespace mamba
// if no action required, don't even start logging them
if (!empty())
{
JsonLogger::instance().json_down("actions");
JsonLogger::instance().json_write({ { "PREFIX", Context::instance().target_prefix } });
Console::instance().json_down("actions");
Console::instance().json_write({ { "PREFIX", Context::instance().target_prefix } });
}
}
@ -675,23 +675,22 @@ namespace mamba
// JSON output
// back to the top level if any action was required
if (!empty())
JsonLogger::instance().json_up();
JsonLogger::instance().json_write(
Console::instance().json_up();
Console::instance().json_write(
{ { "dry_run", ctx.dry_run }, { "prefix", ctx.target_prefix } });
if (empty())
JsonLogger::instance().json_write(
Console::instance().json_write(
{ { "message", "All requested packages already installed" } });
// finally, print the JSON
if (ctx.json)
Console::instance().print(JsonLogger::instance().json_log.unflatten().dump(4), true);
Console::instance().json_print();
if (ctx.dry_run)
{
Console::stream() << "Dry run. Not executing transaction.";
Console::stream() << "Dry run. Not executing the transaction.";
return true;
}
Console::stream() << "\n\nTransaction starting";
Console::stream() << "\nTransaction starting";
m_transaction_context = TransactionContext(prefix.path(), find_python_version());
History::UserRequest ur = History::UserRequest::prefilled();
@ -854,12 +853,12 @@ namespace mamba
auto add_json = [](const auto& jlist, const char* s) {
if (!jlist.empty())
{
JsonLogger::instance().json_down(s);
Console::instance().json_down(s);
for (nlohmann::json j : jlist)
{
JsonLogger::instance().json_append(j);
Console::instance().json_append(j);
}
JsonLogger::instance().json_up();
Console::instance().json_up();
}
};

View File

@ -192,6 +192,7 @@ namespace mamba
std::string windows_version()
{
LOG_DEBUG << "Loading Windows virtual package";
if (!env::get("CONDA_OVERRIDE_WIN").empty())
{
return env::get("CONDA_OVERRIDE_WIN");
@ -226,9 +227,11 @@ namespace mamba
full_version = rmatch[3];
auto version_els = split(full_version, ".");
norm_version = concat(version_els[0], ".", version_els[1], ".", version_els[2]);
LOG_DEBUG << "Windows version found: " << norm_version;
}
else
{
LOG_DEBUG << "Windows version not found";
norm_version = "0.0.0";
}
return norm_version;
@ -236,6 +239,7 @@ namespace mamba
std::string macos_version()
{
LOG_DEBUG << "Loading macos virtual package";
if (!env::get("CONDA_OVERRIDE_OSX").empty())
{
return env::get("CONDA_OVERRIDE_OSX");
@ -262,11 +266,15 @@ namespace mamba
<< ec.message();
return "";
}
return std::string(strip(out));
auto version = std::string(strip(out));
LOG_DEBUG << "macos version found: " << version;
return version;
}
std::string linux_version()
{
LOG_DEBUG << "Loading linux virtual package";
if (!env::get("CONDA_OVERRIDE_LINUX").empty())
{
return env::get("CONDA_OVERRIDE_LINUX");
@ -283,7 +291,7 @@ namespace mamba
if (ec)
{
LOG_INFO << "Could not find linux version by calling 'uname -r' (skipped)";
LOG_DEBUG << "Could not find linux version by calling 'uname -r' (skipped)";
return "";
}

View File

@ -49,6 +49,8 @@ namespace mamba
std::string cuda_version()
{
LOG_DEBUG << "Loading CUDA virtual package";
if (!env::get("CONDA_OVERRIDE_CUDA").empty())
{
return env::get("CONDA_OVERRIDE_CUDA");
@ -89,7 +91,7 @@ namespace mamba
&& fs::exists(p.path() / "nvidia-smi.exe"))
{
std::string f = p.path() / "nvidia-smi.exe";
LOG_INFO << "Found nvidia-smi in: " << f;
LOG_DEBUG << "Found nvidia-smi in: " << f;
std::vector<std::string> args = { f, "--query", "-u", "-x" };
auto [status, ec] = reproc::run(args,
reproc::options{},
@ -107,7 +109,7 @@ namespace mamba
if (out.empty())
{
LOG_INFO << "Could not find CUDA version by calling 'nvidia-smi' (skipped)\n";
LOG_DEBUG << "Could not find CUDA version by calling 'nvidia-smi' (skipped)\n";
return "";
}
@ -124,6 +126,7 @@ namespace mamba
}
}
LOG_DEBUG << "CUDA not found";
return "";
}
@ -144,6 +147,8 @@ namespace mamba
std::vector<PackageInfo> dist_packages()
{
LOG_DEBUG << "Loading distribution virtual packages";
std::vector<PackageInfo> res;
auto platform = Context::instance().platform;
auto split_platform = split(platform, "-", 1);
@ -215,6 +220,7 @@ namespace mamba
std::vector<PackageInfo> get_virtual_packages()
{
LOG_DEBUG << "Loading virtual packages";
auto res = detail::dist_packages();
auto cuda_ver = detail::cuda_version();

View File

@ -42,12 +42,12 @@ namespace mamba
p_tempdir = std::make_unique<TemporaryDirectory>();
tempdir_path = p_tempdir->path();
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kTrace;
spdlog::set_level(spdlog::level::trace);
}
~LockDirTest()
{
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
};
@ -159,12 +159,12 @@ namespace mamba
p_tempfile = std::make_unique<TemporaryFile>();
tempfile_path = p_tempfile->path();
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kTrace;
spdlog::set_level(spdlog::level::trace);
}
~LockFileTest()
{
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
};

View File

@ -31,7 +31,7 @@ namespace validate
auto pk_bytes = ed25519_key_hex_to_bytes(pk_hex);
EXPECT_EQ(pk_hex, ::mamba::hex_string(pk_bytes));
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
spdlog::set_level(spdlog::level::debug);
std::array<unsigned char, 5> not_even_key;
pk_hex = ::mamba::hex_string(not_even_key);
@ -43,7 +43,7 @@ namespace validate
pk_bytes = ed25519_key_hex_to_bytes(pk_hex);
EXPECT_FALSE(pk_hex == ::mamba::hex_string(pk_bytes));
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
TEST(Validate, ed25519_sig_hex_to_bytes)
@ -58,7 +58,7 @@ namespace validate
auto sig_bytes = ed25519_sig_hex_to_bytes(sig_hex);
EXPECT_EQ(sig_hex, ::mamba::hex_string(sig_bytes));
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
spdlog::set_level(spdlog::level::debug);
std::array<unsigned char, 5> not_even_sig;
sig_hex = ::mamba::hex_string(not_even_sig);
@ -70,7 +70,7 @@ namespace validate
sig_bytes = ed25519_sig_hex_to_bytes(sig_hex);
EXPECT_FALSE(sig_hex == ::mamba::hex_string(sig_bytes));
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
@ -104,20 +104,20 @@ namespace validate
TEST_F(VerifyMsg, wrong_signature)
{
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
spdlog::set_level(spdlog::level::debug);
auto pk_hex = ::mamba::hex_string(pk, MAMBA_ED25519_KEYSIZE_BYTES);
EXPECT_EQ(verify("Some text.", pk_hex, "signature_hex"), 0);
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
TEST_F(VerifyMsg, wrong_public_key)
{
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
spdlog::set_level(spdlog::level::debug);
auto signature_hex = ::mamba::hex_string(signature, MAMBA_ED25519_SIGSIZE_BYTES);
EXPECT_EQ(verify("Some text.", "pk_hex", signature_hex), 0);
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kInfo;
spdlog::set_level(spdlog::level::info);
}
class VerifyGPGMsg : public ::testing::Test
@ -1230,12 +1230,12 @@ namespace validate
write_role(key_mgr_json, channel_dir->path() / "key_mgr.json");
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kDebug;
spdlog::set_level(spdlog::level::debug);
}
~RepoCheckerT()
{
mamba::MessageLogger::global_log_severity() = mamba::LogSeverity::kWarning;
spdlog::set_level(spdlog::level::warn);
}
protected:

View File

@ -66,7 +66,7 @@ main(int argc, char** argv)
}
catch (const std::exception& e)
{
LOG_ERROR << e.what();
LOG_CRITICAL << e.what();
mamba::set_sig_interrupted();
return 1;
}

View File

@ -18,5 +18,6 @@ dependencies:
- yaml-cpp
- termcolor-cpp
- cli11
- spdlog
- pybind11
- pytest

View File

@ -18,6 +18,7 @@ dependencies:
- yaml-cpp
- termcolor-cpp
- cli11
- spdlog
- pybind11
- pytest
- conda

View File

@ -31,7 +31,7 @@ main(int argc, char** argv)
}
catch (const std::exception& e)
{
LOG_ERROR << e.what();
LOG_CRITICAL << e.what();
set_sig_interrupted();
return 1;
}

View File

@ -21,3 +21,4 @@ dependencies:
- pytest
- pytest-lazy-fixture
- pyyaml
- spdlog

View File

@ -44,6 +44,12 @@ init_general_options(CLI::App* subcom)
"Set verbosity (higher verbosity with multiple -v, e.g. -vvv)")
->group(cli_group);
auto& log_level = config.at("log_level").get_wrapped<spdlog::level::level_enum>();
subcom->add_flag("--log-level", log_level.set_cli_config(""), log_level.description())
->group(cli_group)
->check(CLI::IsMember(std::vector<std::string>(
{ "critical", "error", "warning", "info", "debug", "trace", "off" })));
auto& quiet = config.at("quiet").get_wrapped<bool>();
subcom->add_flag("-q,--quiet", quiet.set_cli_config(0), quiet.description())->group(cli_group);

View File

@ -43,7 +43,7 @@ main(int argc, char** argv)
}
catch (const std::exception& e)
{
LOG_ERROR << e.what();
LOG_CRITICAL << e.what();
set_sig_interrupted();
return 1;
}