From f8da5415e3877832a92f65fda5c13b259011cd3c Mon Sep 17 00:00:00 2001 From: obucinac Date: Fri, 13 Sep 2019 17:34:34 +0200 Subject: [PATCH] Part two of SON-83 - plugins option in command line and config file (#126) - Empty SON plugin is INACTIVE by default - To enable it, add peerplays_sidechain to plugins section in config file, or use --plugins command line option - Plugin can work with or without witness --- libraries/app/CMakeLists.txt | 1 + libraries/app/application.cpp | 44 ++- libraries/app/config_util.cpp | 354 ++++++++++++++++++ .../app/include/graphene/app/application.hpp | 8 +- .../app/include/graphene/app/config_util.hpp | 34 ++ .../delayed_node/delayed_node_plugin.cpp | 3 +- programs/delayed_node/main.cpp | 13 + programs/witness_node/main.cpp | 219 ++--------- 8 files changed, 484 insertions(+), 192 deletions(-) create mode 100644 libraries/app/config_util.cpp create mode 100644 libraries/app/include/graphene/app/config_util.hpp diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 077eb4aa..93e540f9 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -7,6 +7,7 @@ add_library( graphene_app database_api.cpp impacted.cpp plugin.cpp + config_util.cpp ${HEADERS} ${EGENESIS_HEADERS} ) diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 5e4f9c7e..b73227e1 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -924,7 +924,8 @@ namespace detail { std::shared_ptr _websocket_server; std::shared_ptr _websocket_tls_server; - std::map> _plugins; + std::map> _active_plugins; + std::map> _available_plugins; bool _is_finished_syncing = false; }; @@ -1008,6 +1009,22 @@ void application::initialize(const fc::path& data_dir, const boost::program_opti std::exit(EXIT_SUCCESS); } + + std::vector wanted; + if( options.count("plugins") ) + { + boost::split(wanted, options.at("plugins").as(), [](char c){return c == ' ';}); + } + else + { + wanted.push_back("witness"); + wanted.push_back("account_history"); + wanted.push_back("market_history"); + } + for (auto& it : wanted) + { + if (!it.empty()) enable_plugin(it); + } } void application::startup() @@ -1025,7 +1042,14 @@ void application::startup() std::shared_ptr application::get_plugin(const string& name) const { - return my->_plugins[name]; + return my->_active_plugins[name]; +} + +bool application::is_plugin_enabled(const string& name) const +{ + if(my->_active_plugins.find(name) == my->_active_plugins.end()) + return false; + return true; } net::node_ptr application::p2p_node() @@ -1058,14 +1082,20 @@ bool application::is_finished_syncing() const return my->_is_finished_syncing; } -void graphene::app::application::add_plugin(const string& name, std::shared_ptr p) +void graphene::app::application::enable_plugin(const string& name) { - my->_plugins[name] = p; + FC_ASSERT(my->_available_plugins[name], "Unknown plugin '" + name + "'"); + my->_active_plugins[name] = my->_available_plugins[name]; + my->_active_plugins[name]->plugin_set_app(this); +} + +void graphene::app::application::add_available_plugin(std::shared_ptr p) { + my->_available_plugins[p->plugin_name()] = p; } void application::shutdown_plugins() { - for( auto& entry : my->_plugins ) + for( auto& entry : my->_active_plugins ) entry.second->plugin_shutdown(); return; } @@ -1079,14 +1109,14 @@ void application::shutdown() void application::initialize_plugins( const boost::program_options::variables_map& options ) { - for( auto& entry : my->_plugins ) + for( auto& entry : my->_active_plugins ) entry.second->plugin_initialize( options ); return; } void application::startup_plugins() { - for( auto& entry : my->_plugins ) + for( auto& entry : my->_active_plugins ) entry.second->plugin_startup(); return; } diff --git a/libraries/app/config_util.cpp b/libraries/app/config_util.cpp new file mode 100644 index 00000000..797e3131 --- /dev/null +++ b/libraries/app/config_util.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2018 Lubos Ilcik, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace bpo = boost::program_options; + +class deduplicator +{ +public: + deduplicator() : modifier(nullptr) {} + + deduplicator(const boost::shared_ptr (*mod_fn)(const boost::shared_ptr&)) + : modifier(mod_fn) {} + + const boost::shared_ptr next(const boost::shared_ptr& o) + { + const std::string name = o->long_name(); + if( seen.find( name ) != seen.end() ) + return nullptr; + seen.insert(name); + return modifier ? modifier(o) : o; + } + +private: + boost::container::flat_set seen; + const boost::shared_ptr (*modifier)(const boost::shared_ptr&); +}; + +// Currently, you can only specify the filenames and logging levels, which +// are all most users would want to change. At a later time, options can +// be added to control rotation intervals, compression, and other seldom- +// used features +static void write_default_logging_config_to_stream(std::ostream& out) +{ + out << "# declare an appender named \"stderr\" that writes messages to the console\n" + "[log.console_appender.stderr]\n" + "stream=std_error\n\n" + "# declare an appender named \"default\" that writes messages to default.log\n" + "[log.file_appender.default]\n" + "# filename can be absolute or relative to this config file\n" + "filename=logs/default/default.log\n" + "# Rotate log every ? minutes, if leave out default to 60\n" + "rotation_interval=60\n" + "# how long will logs be kept (in days), if leave out default to 1\n" + "rotation_limit=7\n\n" + "# declare an appender named \"p2p\" that writes messages to p2p.log\n" + "[log.file_appender.p2p]\n" + "# filename can be absolute or relative to this config file\n" + "filename=logs/p2p/p2p.log\n" + "# Rotate log every ? minutes, if leave out default to 60\n" + "rotation_interval=60\n" + "# how long will logs be kept (in days), if leave out default to 1\n" + "rotation_limit=7\n\n" + "# declare an appender named \"rpc\" that writes messages to rpc.log\n" + "[log.file_appender.rpc]\n" + "# filename can be absolute or relative to this config file\n" + "filename=logs/rpc/rpc.log\n" + "# Rotate log every ? minutes, if leave out default to 60\n" + "rotation_interval=60\n" + "# how long will logs be kept (in days), if leave out default to 1\n" + "rotation_limit=7\n\n" + "# route any messages logged to the default logger to the \"stderr\" appender and\n" + "# \"default\" appender we declared above, if they are info level or higher\n" + "[logger.default]\n" + "level=info\n" + "appenders=stderr,default\n\n" + "# route messages sent to the \"p2p\" logger to the \"p2p\" appender declared above\n" + "[logger.p2p]\n" + "level=warn\n" + "appenders=p2p\n\n" + "# route messages sent to the \"rpc\" logger to the \"rpc\" appender declared above\n" + "[logger.rpc]\n" + "level=error\n" + "appenders=rpc\n\n"; +} + +// logging config is too complicated to be parsed by boost::program_options, +// so we do it by hand +static fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) +{ + try + { + fc::logging_config logging_config; + bool found_logging_config = false; + + boost::property_tree::ptree config_ini_tree; + boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree); + for (const auto& section : config_ini_tree) + { + const std::string& section_name = section.first; + const boost::property_tree::ptree& section_tree = section.second; + + const std::string console_appender_section_prefix = "log.console_appender."; + const std::string file_appender_section_prefix = "log.file_appender."; + const std::string logger_section_prefix = "logger."; + + if (boost::starts_with(section_name, console_appender_section_prefix)) + { + std::string console_appender_name = section_name.substr(console_appender_section_prefix.length()); + std::string stream_name = section_tree.get("stream"); + + // construct a default console appender config here + // stdout/stderr will be taken from ini file, everything else hard-coded here + fc::console_appender::config console_appender_config; + console_appender_config.level_colors.emplace_back( + fc::console_appender::level_color(fc::log_level::debug, + fc::console_appender::color::green)); + console_appender_config.level_colors.emplace_back( + fc::console_appender::level_color(fc::log_level::warn, + fc::console_appender::color::brown)); + console_appender_config.level_colors.emplace_back( + fc::console_appender::level_color(fc::log_level::error, + fc::console_appender::color::cyan)); + console_appender_config.stream = fc::variant(stream_name).as(); + logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config))); + found_logging_config = true; + } + else if (boost::starts_with(section_name, file_appender_section_prefix)) + { + std::string file_appender_name = section_name.substr(file_appender_section_prefix.length()); + fc::path file_name = section_tree.get("filename"); + if (file_name.is_relative()) + file_name = fc::absolute(config_ini_filename).parent_path() / file_name; + + int interval = section_tree.get_optional("rotation_interval").get_value_or(60); + int limit = section_tree.get_optional("rotation_limit").get_value_or(1); + + // construct a default file appender config here + // filename will be taken from ini file, everything else hard-coded here + fc::file_appender::config file_appender_config; + file_appender_config.filename = file_name; + file_appender_config.flush = true; + file_appender_config.rotate = true; + file_appender_config.rotation_interval = fc::minutes(interval); + file_appender_config.rotation_limit = fc::days(limit); + logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config))); + found_logging_config = true; + } + else if (boost::starts_with(section_name, logger_section_prefix)) + { + std::string logger_name = section_name.substr(logger_section_prefix.length()); + std::string level_string = section_tree.get("level"); + std::string appenders_string = section_tree.get("appenders"); + fc::logger_config logger_config(logger_name); + logger_config.level = fc::variant(level_string).as(); + boost::split(logger_config.appenders, appenders_string, + boost::is_any_of(" ,"), + boost::token_compress_on); + logging_config.loggers.push_back(logger_config); + found_logging_config = true; + } + } + if (found_logging_config) + return logging_config; + else + return fc::optional(); + } + FC_RETHROW_EXCEPTIONS(warn, "") +} + +static const boost::shared_ptr new_option_description( const std::string& name, const bpo::value_semantic* value, const std::string& description ) +{ + bpo::options_description helper(""); + helper.add_options()( name.c_str(), value, description.c_str() ); + return helper.options()[0]; +} + + +static void load_config_file(const fc::path& config_ini_path, const bpo::options_description& cfg_options, + bpo::variables_map& options ) +{ + deduplicator dedup; + bpo::options_description unique_options("Graphene Witness Node"); + for( const boost::shared_ptr opt : cfg_options.options() ) + { + const boost::shared_ptr od = dedup.next(opt); + if( !od ) continue; + unique_options.add( od ); + } + + // get the basic options + bpo::store(bpo::parse_config_file(config_ini_path.preferred_string().c_str(), + unique_options, true), options); +} + +static bool load_logging_config_file(const fc::path& config_ini_path) +{ + // try to get logging options from the config file. + try + { + fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); + if (logging_config) + { + fc::configure_logging(*logging_config); + return true; + } + } + catch (const fc::exception& ex) + { + wlog("Error parsing logging config from logging config file ${config}, using default config", ("config", config_ini_path.preferred_string())); + } + return false; +} + +static void create_new_config_file(const fc::path& config_ini_path, const fc::path& data_dir, + const bpo::options_description& cfg_options ) +{ + ilog("Writing new config file at ${path}", ("path", config_ini_path)); + if( !fc::exists(data_dir) ) + fc::create_directories(data_dir); + + auto modify_option_defaults = [](const boost::shared_ptr& o) -> const boost::shared_ptr { + const std::string& name = o->long_name(); + if( name == "partial-operations" ) + return new_option_description(name, bpo::value()->default_value(true), o->description() ); + if( name == "max-ops-per-account" ) + return new_option_description(name, bpo::value()->default_value(100), o->description() ); + return o; + }; + deduplicator dedup(modify_option_defaults); + std::ofstream out_cfg(config_ini_path.preferred_string()); + std::string plugin_header_surrounding( 78, '=' ); + for( const boost::shared_ptr opt : cfg_options.options() ) + { + const boost::shared_ptr od = dedup.next(opt); + if( !od ) continue; + + if( od->long_name().find("plugin-cfg-header-") == 0 ) // it's a plugin header + { + out_cfg << "\n"; + out_cfg << "# " << plugin_header_surrounding << "\n"; + out_cfg << "# " << od->description() << "\n"; + out_cfg << "# " << plugin_header_surrounding << "\n"; + out_cfg << "\n"; + continue; + } + + if( !od->description().empty() ) + out_cfg << "# " << od->description() << "\n"; + boost::any store; + if( !od->semantic()->apply_default(store) ) + out_cfg << "# " << od->long_name() << " = \n"; + else { + auto example = od->format_parameter(); + if( example.empty() ) + // This is a boolean switch + out_cfg << od->long_name() << " = " << "false\n"; + else { + // The string is formatted "arg (=)" + example.erase(0, 6); + example.erase(example.length()-1); + out_cfg << od->long_name() << " = " << example << "\n"; + } + } + out_cfg << "\n"; + } + + out_cfg << "\n" + << "# " << plugin_header_surrounding << "\n" + << "# logging options\n" + << "# " << plugin_header_surrounding << "\n" + << "#\n" + << "# Logging configuration is loaded from logging.ini by default.\n" + << "# If logging.ini exists, logging configuration added in this file will be ignored.\n"; + out_cfg.close(); +} + +static void create_logging_config_file(const fc::path& config_ini_path, const fc::path& data_dir) +{ + ilog("Writing new config file at ${path}", ("path", config_ini_path)); + if (!exists(data_dir)) + { + create_directories(data_dir); + } + + std::ofstream out_cfg(config_ini_path.preferred_string()); + write_default_logging_config_to_stream(out_cfg); + out_cfg.close(); +} + +namespace graphene { namespace app { + + void load_configuration_options(const fc::path& data_dir, const bpo::options_description& cfg_options, bpo::variables_map& options) + { + const auto config_ini_path = data_dir / "config.ini"; + const auto logging_ini_path = data_dir / "logging.ini"; + + if(!exists(config_ini_path) && fc::exists(logging_ini_path)) + { + // this is an uncommon case + create_new_config_file(config_ini_path, data_dir, cfg_options); + } + else if(!exists(config_ini_path)) + { + // create default config.ini and logging.ini + create_new_config_file(config_ini_path, data_dir, cfg_options); + create_logging_config_file(logging_ini_path, data_dir); + } + + // load witness node configuration + load_config_file(config_ini_path, cfg_options, options); + + // load logging configuration + if (fc::exists(logging_ini_path)) + { + load_logging_config_file(logging_ini_path); + } + else + { + // this is the legacy config.ini case + load_logging_config_file(config_ini_path); + } + } + +} } // graphene::app diff --git a/libraries/app/include/graphene/app/application.hpp b/libraries/app/include/graphene/app/application.hpp index 26ae78ef..a74d9043 100644 --- a/libraries/app/include/graphene/app/application.hpp +++ b/libraries/app/include/graphene/app/application.hpp @@ -63,7 +63,7 @@ namespace graphene { namespace app { if( !plugin_cfg_options.options().empty() ) _cfg_options.add(plugin_cfg_options); - add_plugin( plug->plugin_name(), plug ); + add_available_plugin( plug ); return plug; } std::shared_ptr get_plugin( const string& name )const; @@ -88,8 +88,12 @@ namespace graphene { namespace app { /// Emitted when syncing finishes (is_finished_syncing will return true) boost::signals2::signal syncing_finished; + void enable_plugin( const string& name ); + + bool is_plugin_enabled(const string& name) const; + private: - void add_plugin( const string& name, std::shared_ptr p ); + void add_available_plugin( std::shared_ptr p ); std::shared_ptr my; boost::program_options::options_description _cli_options; diff --git a/libraries/app/include/graphene/app/config_util.hpp b/libraries/app/include/graphene/app/config_util.hpp new file mode 100644 index 00000000..d7358f22 --- /dev/null +++ b/libraries/app/include/graphene/app/config_util.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Lubos Ilcik, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include +#include + +namespace graphene { namespace app { + + void load_configuration_options(const fc::path &data_dir, const boost::program_options::options_description &cfg_options, + boost::program_options::variables_map &options); + +} } // graphene::app \ No newline at end of file diff --git a/libraries/plugins/delayed_node/delayed_node_plugin.cpp b/libraries/plugins/delayed_node/delayed_node_plugin.cpp index fb70cb68..a73e5923 100644 --- a/libraries/plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/plugins/delayed_node/delayed_node_plugin.cpp @@ -58,7 +58,7 @@ delayed_node_plugin::~delayed_node_plugin() void delayed_node_plugin::plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) { cli.add_options() - ("trusted-node", boost::program_options::value()->required(), "RPC endpoint of a trusted validating node (required)") + ("trusted-node", boost::program_options::value(), "RPC endpoint of a trusted validating node (required)") ; cfg.add(cli); } @@ -74,6 +74,7 @@ void delayed_node_plugin::connect() void delayed_node_plugin::plugin_initialize(const boost::program_options::variables_map& options) { + FC_ASSERT(options.count("trusted-node") > 0); my->remote_endpoint = "ws://" + options.at("trusted-node").as(); } diff --git a/programs/delayed_node/main.cpp b/programs/delayed_node/main.cpp index 74cd8fc3..da831d2d 100644 --- a/programs/delayed_node/main.cpp +++ b/programs/delayed_node/main.cpp @@ -70,10 +70,19 @@ int main(int argc, char** argv) { bpo::variables_map options; + bpo::options_description cli, cfg; + node.set_program_options(cli, cfg); + cfg_options.add(cfg); + + cfg_options.add_options() + ("plugins", bpo::value()->default_value("delayed_node account_history market_history"), + "Space-separated list of plugins to activate"); + auto delayed_plug = node.register_plugin(); auto history_plug = node.register_plugin(); auto market_history_plug = node.register_plugin(); + // add plugin options to config try { bpo::options_description cli, cfg; @@ -160,6 +169,10 @@ int main(int argc, char** argv) { elog("Error parsing configuration file: ${e}", ("e", e.what())); return 1; } + + if( !options.count("plugins") ) + options.insert( std::make_pair( "plugins", bpo::variable_value(std::string("delayed_node account_history market_history"), true) ) ); + node.initialize(data_dir, options); node.initialize_plugins( options ); diff --git a/programs/witness_node/main.cpp b/programs/witness_node/main.cpp index c9a09ca3..2085cbb5 100644 --- a/programs/witness_node/main.cpp +++ b/programs/witness_node/main.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include +#include #include #include @@ -35,40 +36,28 @@ #include //#include -#include #include #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include +#include +#include #include -#include #include #include -#include #ifdef WIN32 -# include +# include #else # include #endif using namespace graphene; namespace bpo = boost::program_options; - -void write_default_logging_config_to_stream(std::ostream& out); -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename); int main(int argc, char** argv) { app::application* node = new app::application(); @@ -78,12 +67,23 @@ int main(int argc, char** argv) { bpo::options_description cfg_options("Graphene Witness Node"); app_options.add_options() ("help,h", "Print this help message and exit.") - ("version", "Display the version info and exit") - ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), "Directory containing databases, configuration file, etc.") - ; + ("data-dir,d", bpo::value()->default_value("witness_node_data_dir"), + "Directory containing databases, configuration file, etc.") + ("version,v", "Display version information") + ("plugins", bpo::value() + ->default_value("witness account_history market_history accounts_list affiliate_stats bookie"), + "Space-separated list of plugins to activate"); bpo::variables_map options; + bpo::options_description cli, cfg; + node->set_program_options(cli, cfg); + cfg_options.add(cfg); + + cfg_options.add_options() + ("plugins", bpo::value()->default_value("witness account_history market_history accounts_list affiliate_stats bookie"), + "Space-separated list of plugins to activate"); + auto witness_plug = node->register_plugin(); auto history_plug = node->register_plugin(); auto market_history_plug = node->register_plugin(); @@ -95,6 +95,7 @@ int main(int argc, char** argv) { auto peerplays_sidechain = node->register_plugin(); // auto snapshot_plug = node->register_plugin(); + // add plugin options to config try { bpo::options_description cli, cfg; @@ -109,11 +110,6 @@ int main(int argc, char** argv) { return 1; } - if( options.count("help") ) - { - std::cout << app_options << "\n"; - return 0; - } if (options.count("version")) { std::string witness_version(graphene::utilities::git_revision_description); @@ -127,6 +123,11 @@ int main(int argc, char** argv) { std::cout << "Boost: " << boost::replace_all_copy(std::string(BOOST_LIB_VERSION), "_", ".") << "\n"; return 0; } + if( options.count("help") ) + { + std::cout << app_options << "\n"; + return 0; + } fc::path data_dir; if( options.count("data-dir") ) @@ -135,60 +136,21 @@ int main(int argc, char** argv) { if( data_dir.is_relative() ) data_dir = fc::current_path() / data_dir; } + app::load_configuration_options(data_dir, cfg_options, options); - fc::path config_ini_path = data_dir / "config.ini"; - if( fc::exists(config_ini_path) ) - { - // get the basic options - bpo::store(bpo::parse_config_file(config_ini_path.preferred_string().c_str(), cfg_options, true), options); + std::set plugins; + boost::split(plugins, options.at("plugins").as(), [](char c){return c == ' ';}); - // try to get logging options from the config file. - try - { - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } - catch (const fc::exception&) - { - wlog("Error parsing logging config from config file ${config}, using default config", ("config", config_ini_path.preferred_string())); - } + if(plugins.count("account_history") && plugins.count("elasticsearch")) { + std::cerr << "Plugin conflict: Cannot load both account_history plugin and elasticsearch plugin\n"; + return 1; } - else - { - ilog("Writing new config file at ${path}", ("path", config_ini_path)); - if( !fc::exists(data_dir) ) - fc::create_directories(data_dir); - std::ofstream out_cfg(config_ini_path.preferred_string()); - for( const boost::shared_ptr od : cfg_options.options() ) - { - if( !od->description().empty() ) - out_cfg << "# " << od->description() << "\n"; - boost::any store; - if( !od->semantic()->apply_default(store) ) - out_cfg << "# " << od->long_name() << " = \n"; - else { - auto example = od->format_parameter(); - if( example.empty() ) - // This is a boolean switch - out_cfg << od->long_name() << " = " << "false\n"; - else { - // The string is formatted "arg (=)" - example.erase(0, 6); - example.erase(example.length()-1); - out_cfg << od->long_name() << " = " << example << "\n"; - } - } - out_cfg << "\n"; - } - write_default_logging_config_to_stream(out_cfg); - out_cfg.close(); - // read the default logging config we just wrote out to the file and start using it - fc::optional logging_config = load_logging_config_from_ini_file(config_ini_path); - if (logging_config) - fc::configure_logging(*logging_config); - } + std::for_each(plugins.begin(), plugins.end(), [node](const std::string& plug) mutable { + if (!plug.empty()) { + node->enable_plugin(plug); + } + }); bpo::notify(options); node->initialize(data_dir, options); @@ -217,7 +179,7 @@ int main(int argc, char** argv) { node->shutdown_plugins(); node->shutdown(); delete node; - return 0; + return EXIT_SUCCESS; } catch( const fc::exception& e ) { // deleting the node can yield, so do this outside the exception handler unhandled_exception = e; @@ -228,113 +190,6 @@ int main(int argc, char** argv) { elog("Exiting with error:\n${e}", ("e", unhandled_exception->to_detail_string())); node->shutdown(); delete node; - return 1; + return EXIT_FAILURE; } } - -// logging config is too complicated to be parsed by boost::program_options, -// so we do it by hand -// -// Currently, you can only specify the filenames and logging levels, which -// are all most users would want to change. At a later time, options can -// be added to control rotation intervals, compression, and other seldom- -// used features -void write_default_logging_config_to_stream(std::ostream& out) -{ - out << "# declare an appender named \"stderr\" that writes messages to the console\n" - "[log.console_appender.stderr]\n" - "stream=std_error\n\n" - "# declare an appender named \"p2p\" that writes messages to p2p.log\n" - "[log.file_appender.p2p]\n" - "filename=logs/p2p/p2p.log\n" - "# filename can be absolute or relative to this config file\n\n" - "# route any messages logged to the default logger to the \"stderr\" logger we\n" - "# declared above, if they are info level are higher\n" - "[logger.default]\n" - "level=info\n" - "appenders=stderr\n\n" - "# route messages sent to the \"p2p\" logger to the p2p appender declared above\n" - "[logger.p2p]\n" - "level=info\n" - "appenders=p2p\n\n"; -} - -fc::optional load_logging_config_from_ini_file(const fc::path& config_ini_filename) -{ - try - { - fc::logging_config logging_config; - bool found_logging_config = false; - - boost::property_tree::ptree config_ini_tree; - boost::property_tree::ini_parser::read_ini(config_ini_filename.preferred_string().c_str(), config_ini_tree); - for (const auto& section : config_ini_tree) - { - const std::string& section_name = section.first; - const boost::property_tree::ptree& section_tree = section.second; - - const std::string console_appender_section_prefix = "log.console_appender."; - const std::string file_appender_section_prefix = "log.file_appender."; - const std::string logger_section_prefix = "logger."; - - if (boost::starts_with(section_name, console_appender_section_prefix)) - { - std::string console_appender_name = section_name.substr(console_appender_section_prefix.length()); - std::string stream_name = section_tree.get("stream"); - - // construct a default console appender config here - // stdout/stderr will be taken from ini file, everything else hard-coded here - fc::console_appender::config console_appender_config; - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::debug, - fc::console_appender::color::green)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::warn, - fc::console_appender::color::brown)); - console_appender_config.level_colors.emplace_back( - fc::console_appender::level_color(fc::log_level::error, - fc::console_appender::color::cyan)); - console_appender_config.stream = fc::variant(stream_name).as(); - logging_config.appenders.push_back(fc::appender_config(console_appender_name, "console", fc::variant(console_appender_config))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, file_appender_section_prefix)) - { - std::string file_appender_name = section_name.substr(file_appender_section_prefix.length()); - fc::path file_name = section_tree.get("filename"); - if (file_name.is_relative()) - file_name = fc::absolute(config_ini_filename).parent_path() / file_name; - - - // construct a default file appender config here - // filename will be taken from ini file, everything else hard-coded here - fc::file_appender::config file_appender_config; - file_appender_config.filename = file_name; - file_appender_config.flush = true; - file_appender_config.rotate = true; - file_appender_config.rotation_interval = fc::hours(1); - file_appender_config.rotation_limit = fc::days(1); - logging_config.appenders.push_back(fc::appender_config(file_appender_name, "file", fc::variant(file_appender_config))); - found_logging_config = true; - } - else if (boost::starts_with(section_name, logger_section_prefix)) - { - std::string logger_name = section_name.substr(logger_section_prefix.length()); - std::string level_string = section_tree.get("level"); - std::string appenders_string = section_tree.get("appenders"); - fc::logger_config logger_config(logger_name); - logger_config.level = fc::variant(level_string).as(); - boost::split(logger_config.appenders, appenders_string, - boost::is_any_of(" ,"), - boost::token_compress_on); - logging_config.loggers.push_back(logger_config); - found_logging_config = true; - } - } - if (found_logging_config) - return logging_config; - else - return fc::optional(); - } - FC_RETHROW_EXCEPTIONS(warn, "") -}