Skip to content
This repository has been archived by the owner on Jun 23, 2022. It is now read-only.

refactor(util): use flags to define configurations #275

Merged
merged 13 commits into from
Apr 29, 2020
80 changes: 80 additions & 0 deletions include/dsn/utility/flags.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) 2017-present, Xiaomi, Inc. All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#pragma once

#include <string>
#include <cstdint>
#include <functional>

// Example:
// DSN_DEFINE_string("core", filename, "my_file.txt", "The file to read");
// DSN_DEFINE_validator(filename, [](const char *fname){ return is_file(fname); });
// auto fptr = file::open(FLAGS_filename, O_RDONLY | O_BINARY, 0);

#define DSN_DECLARE_VARIABLE(type, name) extern type FLAGS_##name

#define DSN_DECLARE_int32(name) DSN_DECLARE_VARIABLE(int32_t, name)
#define DSN_DECLARE_uint32(name) DSN_DECLARE_VARIABLE(uint32_t, name)
#define DSN_DECLARE_int64(name) DSN_DECLARE_VARIABLE(int64_t, name)
#define DSN_DECLARE_uint64(name) DSN_DECLARE_VARIABLE(uint64_t, name)
#define DSN_DECLARE_double(name) DSN_DECLARE_VARIABLE(double, name)
#define DSN_DECLARE_bool(name) DSN_DECLARE_VARIABLE(bool, name)
#define DSN_DECLARE_string(name) DSN_DECLARE_VARIABLE(const char *, name)

#define DSN_DEFINE_VARIABLE(type, section, name, default_value, desc) \
type FLAGS_##name = default_value; \
static dsn::flag_registerer FLAGS_REG_##name(section, #name, desc, &FLAGS_##name)

#define DSN_DEFINE_int32(section, name, val, desc) \
DSN_DEFINE_VARIABLE(int32_t, section, name, val, desc)
#define DSN_DEFINE_uint32(section, name, val, desc) \
DSN_DEFINE_VARIABLE(uint32_t, section, name, val, desc)
#define DSN_DEFINE_int64(section, name, val, desc) \
DSN_DEFINE_VARIABLE(int64_t, section, name, val, desc)
#define DSN_DEFINE_uint64(section, name, val, desc) \
DSN_DEFINE_VARIABLE(uint64_t, section, name, val, desc)
#define DSN_DEFINE_double(section, name, val, desc) \
DSN_DEFINE_VARIABLE(double, section, name, val, desc)
#define DSN_DEFINE_bool(section, name, val, desc) \
DSN_DEFINE_VARIABLE(bool, section, name, val, desc)
#define DSN_DEFINE_string(section, name, val, desc) \
DSN_DEFINE_VARIABLE(const char *, section, name, val, desc)

// Convenience macro for the registration of a flag validator.
// `validator` must be a std::function<bool(FLAG_TYPE)> and receives the flag value as argument,
// returns true if validation passed.
// The program corrupts if the validation failed.
#define DSN_DEFINE_validator(name, validator) \
static auto FLAGS_VALIDATOR_FN_##name = validator; \
static const dsn::flag_validator FLAGS_VALIDATOR_##name(#name, []() { \
dassert(FLAGS_VALIDATOR_FN_##name(FLAGS_##name), "validation failed: %s", #name); \
})

namespace dsn {

// An utility class that registers a flag upon initialization.
class flag_registerer
{
public:
flag_registerer(const char *section, const char *name, const char *desc, int32_t *val);
flag_registerer(const char *section, const char *name, const char *desc, uint32_t *val);
flag_registerer(const char *section, const char *name, const char *desc, int64_t *val);
flag_registerer(const char *section, const char *name, const char *desc, uint64_t *val);
flag_registerer(const char *section, const char *name, const char *desc, double *val);
flag_registerer(const char *section, const char *name, const char *desc, bool *val);
flag_registerer(const char *section, const char *name, const char *desc, const char **val);
};

// An utility class that registers a validator upon initialization.
class flag_validator
{
public:
flag_validator(const char *name, std::function<void()>);
};

// Loads all the flags from configuration.
extern void flags_initialize();

} // namespace dsn
128 changes: 128 additions & 0 deletions src/core/core/flags.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright (c) 2017-present, Xiaomi, Inc. All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#include <dsn/utility/flags.h>
#include <dsn/utility/config_api.h>
#include <dsn/utility/singleton.h>
#include <dsn/c/api_utilities.h>
#include <boost/optional/optional.hpp>

#include <map>

namespace dsn {

enum value_type
{
FV_BOOL = 0,
FV_INT32 = 1,
FV_UINT32 = 2,
FV_INT64 = 3,
FV_UINT64 = 4,
FV_DOUBLE = 5,
FV_STRING = 6,
FV_MAX_INDEX = 6,
};

using validator_fn = std::function<void()>;

class flag_data
{
public:
#define FLAG_DATA_LOAD_CASE(type, type_enum, suffix) \
case type_enum: \
value<type>() = dsn_config_get_value_##suffix(_section, _name, value<type>(), _desc); \
if (_validator) { \
_validator(); \
} \
break

void load()
{
switch (_type) {
FLAG_DATA_LOAD_CASE(int32_t, FV_INT32, int64);
FLAG_DATA_LOAD_CASE(int64_t, FV_INT64, int64);
FLAG_DATA_LOAD_CASE(uint32_t, FV_UINT32, uint64);
FLAG_DATA_LOAD_CASE(uint64_t, FV_UINT64, uint64);
FLAG_DATA_LOAD_CASE(bool, FV_BOOL, bool);
FLAG_DATA_LOAD_CASE(double, FV_DOUBLE, double);
FLAG_DATA_LOAD_CASE(const char *, FV_STRING, string);
}
}

flag_data(const char *section, const char *name, const char *desc, value_type type, void *val)
: _type(type), _val(val), _section(section), _name(name), _desc(desc)
{
}

void set_validator(validator_fn &validator) { _validator = std::move(validator); }
const validator_fn &validator() const { return _validator; }

private:
template <typename T>
T &value()
{
return *reinterpret_cast<T *>(_val);
}

private:
const value_type _type;
void *const _val;
const char *_section;
const char *_name;
const char *_desc;
validator_fn _validator;
};

class flag_registry : public utils::singleton<flag_registry>
{
public:
flag_registry() {}
Copy link
Member

Choose a reason for hiding this comment

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

Better to move it to private for singleton.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

singleton requires constructor to be public, or the compilation wil fail.

Copy link
Member

Choose a reason for hiding this comment

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

Make ::utils::singleton<flag_registry> to be friend class of flag_registry may help.


void add_flag(const char *name, flag_data flag) { _flags.emplace(name, flag); }

void add_validator(const char *name, validator_fn &validator)
{
auto it = _flags.find(name);
dassert(it != _flags.end(), "flag \"%s\" does not exist", name);
flag_data &flag = it->second;
if (!flag.validator()) {
flag.set_validator(validator);
}
}

void load_from_config()
{
for (auto &kv : _flags) {
flag_data &flag = kv.second;
flag.load();
}
}

private:
std::map<std::string, flag_data> _flags;
};

#define FLAG_REG_CONSTRUCTOR(type, type_enum) \
flag_registerer::flag_registerer( \
Copy link
Contributor

@levy5307 levy5307 Apr 29, 2020

Choose a reason for hiding this comment

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

why the name is flag_registerer, but not flag_register?

Copy link
Contributor Author

@neverchanje neverchanje Apr 29, 2020

Choose a reason for hiding this comment

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

These class names are learned from gflags. If users are familiar with gflags, he will soon get started to use dsn flags.

See this: https://github.com/gflags/gflags/blob/master/src/gflags.h.in#L432.

"registerer" plays the role to register the flag, where "register" is a verb, not a noun.

const char *section, const char *name, const char *desc, type *val) \
{ \
flag_registry::instance().add_flag(name, flag_data(section, name, desc, type_enum, val)); \
}

FLAG_REG_CONSTRUCTOR(int32_t, FV_INT32);
FLAG_REG_CONSTRUCTOR(uint32_t, FV_UINT32);
FLAG_REG_CONSTRUCTOR(int64_t, FV_INT64);
FLAG_REG_CONSTRUCTOR(uint64_t, FV_UINT64);
FLAG_REG_CONSTRUCTOR(bool, FV_BOOL);
FLAG_REG_CONSTRUCTOR(double, FV_DOUBLE);
FLAG_REG_CONSTRUCTOR(const char *, FV_STRING);

flag_validator::flag_validator(const char *name, validator_fn validator)
{
flag_registry::instance().add_validator(name, validator);
}

/*extern*/ void flags_initialize() { flag_registry::instance().load_from_config(); }

} // namespace dsn
27 changes: 12 additions & 15 deletions src/core/core/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,17 @@
#include <dsn/tool_api.h>
#include "service_engine.h"
#include <dsn/tool-api/auto_codes.h>
#include <dsn/utility/flags.h>
#include <dsn/utility/smart_pointers.h>
#include "core/tools/common/simple_logger.h"

DSN_API dsn_log_level_t dsn_log_start_level = dsn_log_level_t::LOG_LEVEL_INFORMATION;
DSN_DEFINE_string("core",
logging_start_level,
"LOG_LEVEL_INFORMATION",
"logs with level below this will not be logged");

DSN_DEFINE_bool("core", logging_flush_on_exit, true, "flush log when exit system");

static void log_on_sys_exit(::dsn::sys_exit_type)
{
Expand All @@ -43,20 +50,14 @@ static void log_on_sys_exit(::dsn::sys_exit_type)

void dsn_log_init(const std::string &logging_factory_name, const std::string &dir_log)
{
dsn_log_start_level = enum_from_string(
dsn_config_get_value_string("core",
"logging_start_level",
enum_to_string(dsn_log_start_level),
"logs with level below this will not be logged"),
dsn_log_level_t::LOG_LEVEL_INVALID);
dsn_log_start_level =
enum_from_string(FLAGS_logging_start_level, dsn_log_level_t::LOG_LEVEL_INVALID);

dassert(dsn_log_start_level != dsn_log_level_t::LOG_LEVEL_INVALID,
"invalid [core] logging_start_level specified");

// register log flush on exit
bool logging_flush_on_exit = dsn_config_get_value_bool(
"core", "logging_flush_on_exit", true, "flush log when exit system");
if (logging_flush_on_exit) {
if (FLAGS_logging_flush_on_exit) {
::dsn::tools::sys_exit.put_back(log_on_sys_exit, "log.flush");
}

Expand All @@ -81,12 +82,8 @@ void dsn_log_init(const std::string &logging_factory_name, const std::string &di
[](const std::vector<std::string> &args) {
dsn_log_level_t start_level;
if (args.size() == 0) {
start_level = enum_from_string(
dsn_config_get_value_string("core",
"logging_start_level",
enum_to_string(dsn_log_start_level),
"logs with level below this will not be logged"),
dsn_log_level_t::LOG_LEVEL_INVALID);
start_level =
enum_from_string(FLAGS_logging_start_level, dsn_log_level_t::LOG_LEVEL_INVALID);
} else {
std::string level_str = "LOG_LEVEL_" + args[0];
start_level =
Expand Down
3 changes: 3 additions & 0 deletions src/core/core/service_api_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <dsn/cpp/serialization.h>
#include <dsn/utility/filesystem.h>
#include <dsn/utility/process_utils.h>
#include <dsn/utility/flags.h>
#include <dsn/tool-api/command_manager.h>
#include <fstream>
#ifdef DSN_ENABLE_GPERF
Expand Down Expand Up @@ -307,6 +308,8 @@ bool run(const char *config_file,
return false;
}

dsn::flags_initialize();

// pause when necessary
if (dsn_config_get_value_bool("core",
"pause_on_start",
Expand Down
Loading