35 #include <boost/property_tree/ptree.hpp>
36 #include <boost/property_tree/ini_parser.hpp>
37 #include <boost/algorithm/string/predicate.hpp>
38 #include <boost/algorithm/string/split.hpp>
39 #include <boost/algorithm/string/split.hpp>
40 #include <boost/algorithm/string.hpp>
44 namespace bpo = boost::program_options;
46 namespace graphene {
namespace app {
namespace detail {
53 deduplicator(
const boost::shared_ptr<bpo::option_description> (*mod_fn)(
const boost::shared_ptr<bpo::option_description>&))
56 const boost::shared_ptr<bpo::option_description>
next(
const boost::shared_ptr<bpo::option_description>& o)
58 const std::string name = o->long_name();
59 if( seen.find( name ) != seen.end() )
62 return modifier ? modifier(o) : o;
66 boost::container::flat_set<std::string> seen;
67 const boost::shared_ptr<bpo::option_description> (*modifier)(
const boost::shared_ptr<bpo::option_description>&);
76 static void write_default_logging_config_to_stream(std::ostream& out)
78 out <<
"# declare an appender named \"stderr\" that writes messages to the console\n"
79 "[log.console_appender.stderr]\n"
80 "stream=std_error\n\n"
81 "# declare an appender named \"default\" that writes messages to default.log\n"
82 "[log.file_appender.default]\n"
83 "# filename can be absolute or relative to this config file\n"
84 "filename=logs/default/default.log\n"
85 "# Rotate log every ? minutes, if leave out default to 60\n"
86 "rotation_interval=60\n"
87 "# how long will logs be kept (in days), if leave out default to 1\n"
88 "rotation_limit=7\n\n"
89 "# declare an appender named \"p2p\" that writes messages to p2p.log\n"
90 "[log.file_appender.p2p]\n"
91 "# filename can be absolute or relative to this config file\n"
92 "filename=logs/p2p/p2p.log\n"
93 "# Rotate log every ? minutes, if leave out default to 60\n"
94 "rotation_interval=60\n"
95 "# how long will logs be kept (in days), if leave out default to 1\n"
96 "rotation_limit=7\n\n"
97 "# declare an appender named \"rpc\" that writes messages to rpc.log\n"
98 "[log.file_appender.rpc]\n"
99 "# filename can be absolute or relative to this config file\n"
100 "filename=logs/rpc/rpc.log\n"
101 "# Rotate log every ? minutes, if leave out default to 60\n"
102 "rotation_interval=60\n"
103 "# how long will logs be kept (in days), if leave out default to 1\n"
104 "rotation_limit=7\n\n"
105 "# route any messages logged to the default logger to the \"stderr\" appender and\n"
106 "# \"default\" appender we declared above, if they are info level or higher\n"
109 "appenders=stderr,default\n\n"
110 "# route messages sent to the \"p2p\" logger to the \"p2p\" appender declared above\n"
114 "# route messages sent to the \"rpc\" logger to the \"rpc\" appender declared above\n"
127 bool found_logging_config =
false;
129 boost::property_tree::ptree config_ini_tree;
130 boost::property_tree::ini_parser::read_ini(config_ini_filename.
preferred_string().c_str(), config_ini_tree);
131 for (
const auto& section : config_ini_tree)
133 const std::string& section_name = section.first;
134 const boost::property_tree::ptree& section_tree = section.second;
136 const std::string console_appender_section_prefix =
"log.console_appender.";
137 const std::string file_appender_section_prefix =
"log.file_appender.";
138 const std::string logger_section_prefix =
"logger.";
140 if (boost::starts_with(section_name, console_appender_section_prefix))
142 std::string console_appender_name = section_name.substr(console_appender_section_prefix.length());
143 std::string stream_name = section_tree.get<std::string>(
"stream");
151 console_appender_config.level_colors.emplace_back(
154 console_appender_config.level_colors.emplace_back(
159 found_logging_config =
true;
161 else if (boost::starts_with(section_name, file_appender_section_prefix))
163 std::string file_appender_name = section_name.substr(file_appender_section_prefix.length());
164 fc::path file_name = section_tree.get<std::string>(
"filename");
168 int interval = section_tree.get_optional<
int>(
"rotation_interval").get_value_or(60);
169 int limit = section_tree.get_optional<
int>(
"rotation_limit").get_value_or(1);
174 file_appender_config.
filename = file_name;
175 file_appender_config.
flush =
true;
176 file_appender_config.
rotate =
true;
180 found_logging_config =
true;
182 else if (boost::starts_with(section_name, logger_section_prefix))
184 std::string logger_name = section_name.substr(logger_section_prefix.length());
185 std::string level_string = section_tree.get<std::string>(
"level");
186 std::string appenders_string = section_tree.get<std::string>(
"appenders");
189 boost::split(logger_config.appenders, appenders_string,
190 boost::is_any_of(
" ,"),
191 boost::token_compress_on);
192 logging_config.
loggers.push_back(logger_config);
193 found_logging_config =
true;
196 if (found_logging_config)
197 return logging_config;
204 static const boost::shared_ptr<bpo::option_description> new_option_description(
const std::string& name,
const bpo::value_semantic* value,
const std::string& description )
206 bpo::options_description helper(
"");
207 helper.add_options()( name.c_str(), value, description.c_str() );
208 return helper.options()[0];
212 static void load_config_file(
const fc::path& config_ini_path,
const bpo::options_description& cfg_options,
213 bpo::variables_map& options )
216 bpo::options_description unique_options(
"BitShares Witness Node");
217 for(
const boost::shared_ptr<bpo::option_description>& opt : cfg_options.options() )
219 const boost::shared_ptr<bpo::option_description> od = dedup.
next(opt);
221 unique_options.add( od );
225 bpo::store(bpo::parse_config_file<char>(config_ini_path.
preferred_string().c_str(),
226 unique_options,
true), options);
229 static bool load_logging_config_file(
const fc::path& config_ini_path)
243 wlog(
"Error parsing logging config from logging config file ${config}, using default config", (
"config", config_ini_path.
preferred_string()));
248 static void create_new_config_file(
const fc::path& config_ini_path,
const fc::path& data_dir,
249 const bpo::options_description& cfg_options )
251 ilog(
"Writing new config file at ${path}", (
"path", config_ini_path));
255 auto modify_option_defaults = [](
const boost::shared_ptr<bpo::option_description>& o) ->
const boost::shared_ptr<bpo::option_description> {
256 const std::string& name = o->long_name();
257 if( name ==
"partial-operations" )
258 return new_option_description(name, bpo::value<bool>()->default_value(
true), o->description() );
259 if( name ==
"max-ops-per-account" )
260 return new_option_description(name, bpo::value<uint64_t>()->default_value(100), o->description() );
265 std::string plugin_header_surrounding( 78,
'=' );
266 for(
const boost::shared_ptr<bpo::option_description>& opt : cfg_options.options() )
268 const boost::shared_ptr<bpo::option_description> od = dedup.
next(opt);
271 if( od->long_name().find(
"plugin-cfg-header-") == 0 )
274 out_cfg <<
"# " << plugin_header_surrounding <<
"\n";
275 out_cfg <<
"# " << od->description() <<
"\n";
276 out_cfg <<
"# " << plugin_header_surrounding <<
"\n";
281 if( !od->description().empty() )
282 out_cfg <<
"# " << od->description() <<
"\n";
284 if( !od->semantic()->apply_default(store) )
285 out_cfg <<
"# " << od->long_name() <<
" = \n";
287 auto example = od->format_parameter();
288 if( example.empty() )
290 out_cfg << od->long_name() <<
" = " <<
"false\n";
294 example.erase(example.length()-1);
295 out_cfg << od->long_name() <<
" = " << example <<
"\n";
302 <<
"# " << plugin_header_surrounding <<
"\n"
303 <<
"# logging options\n"
304 <<
"# " << plugin_header_surrounding <<
"\n"
306 <<
"# Logging configuration is loaded from logging.ini by default.\n"
307 <<
"# If logging.ini exists, logging configuration added in this file will be ignored.\n";
311 static void create_logging_config_file(
const fc::path& config_ini_path,
const fc::path& data_dir)
313 ilog(
"Writing new config file at ${path}", (
"path", config_ini_path));
320 write_default_logging_config_to_stream(out_cfg);
324 namespace graphene {
namespace app {
328 const auto config_ini_path = data_dir /
"config.ini";
329 const auto logging_ini_path = data_dir /
"logging.ini";
334 create_new_config_file(config_ini_path, data_dir, cfg_options);
336 else if(!
exists(config_ini_path))
339 create_new_config_file(config_ini_path, data_dir, cfg_options);
340 create_logging_config_file(logging_ini_path, data_dir);
344 load_config_file(config_ini_path, cfg_options, options);
349 load_logging_config_file(logging_ini_path);
354 load_logging_config_file(config_ini_path);