Skip to content

Commit af3677e

Browse files
authored
feat(logging): Move the logger remote_command handler to command_manager (#2087)
1 parent 5057aad commit af3677e

5 files changed

Lines changed: 105 additions & 113 deletions

File tree

src/utils/command_manager.cpp

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@
2929
#include <fmt/format.h>
3030
// IWYU pragma: no_include <ext/alloc_traits.h>
3131
#include <stdlib.h>
32+
#include <algorithm>
3233
#include <chrono>
3334
#include <limits>
3435
#include <sstream> // IWYU pragma: keep
3536
#include <thread>
37+
#include <type_traits>
3638
#include <unordered_set>
3739
#include <utility>
3840

41+
#include "gutil/map_util.h"
42+
3943
namespace dsn {
4044

4145
std::unique_ptr<command_deregister>
@@ -44,19 +48,19 @@ command_manager::register_command(const std::vector<std::string> &commands,
4448
const std::string &args,
4549
command_handler handler)
4650
{
47-
auto *c = new command_instance();
48-
c->commands = commands;
49-
c->help = help;
50-
c->args = args;
51-
c->handler = std::move(handler);
51+
auto ch = std::make_shared<commands_handler>();
52+
ch->commands = commands;
53+
ch->help = help;
54+
ch->args = args;
55+
ch->handler = std::move(handler);
5256

5357
utils::auto_write_lock l(_lock);
5458
for (const auto &cmd : commands) {
5559
CHECK(!cmd.empty(), "should not register empty command");
56-
CHECK(_handlers.emplace(cmd, c).second, "command '{}' already registered", cmd);
60+
gutil::InsertOrDie(&_handler_by_cmd, cmd, ch);
5761
}
5862

59-
return std::make_unique<command_deregister>(reinterpret_cast<uintptr_t>(c));
63+
return std::make_unique<command_deregister>(reinterpret_cast<uintptr_t>(ch.get()));
6064
}
6165

6266
std::unique_ptr<command_deregister> command_manager::register_bool_command(
@@ -94,35 +98,39 @@ command_manager::register_multiple_commands(const std::vector<std::string> &comm
9498
handler);
9599
}
96100

97-
void command_manager::deregister_command(uintptr_t handle)
101+
void command_manager::deregister_command(uintptr_t cmd_id)
98102
{
99-
auto c = reinterpret_cast<command_instance *>(handle);
100-
CHECK_NOTNULL(c, "cannot deregister a null handle");
103+
const auto ch = reinterpret_cast<commands_handler *>(cmd_id);
104+
CHECK_NOTNULL(ch, "cannot deregister a null command id");
101105
utils::auto_write_lock l(_lock);
102-
for (const std::string &cmd : c->commands) {
103-
_handlers.erase(cmd);
106+
for (const auto &cmd : ch->commands) {
107+
_handler_by_cmd.erase(cmd);
104108
}
105109
}
106110

111+
void command_manager::add_global_cmd(std::unique_ptr<command_deregister> cmd)
112+
{
113+
utils::auto_write_lock l(_lock);
114+
_cmds.push_back(std::move(cmd));
115+
}
116+
107117
bool command_manager::run_command(const std::string &cmd,
108118
const std::vector<std::string> &args,
109119
/*out*/ std::string &output)
110120
{
111-
command_instance *h = nullptr;
121+
std::shared_ptr<commands_handler> ch;
112122
{
113123
utils::auto_read_lock l(_lock);
114-
auto it = _handlers.find(cmd);
115-
if (it != _handlers.end())
116-
h = it->second;
124+
ch = gutil::FindPtrOrNull(_handler_by_cmd, cmd);
117125
}
118126

119-
if (h == nullptr) {
120-
output = std::string("unknown command '") + cmd + "'";
127+
if (!ch) {
128+
output = fmt::format("unknown command '{}'", cmd);
121129
return false;
122-
} else {
123-
output = h->handler(args);
124-
return true;
125130
}
131+
132+
output = ch->handler(args);
133+
return true;
126134
}
127135

128136
std::string command_manager::set_bool(bool &value,
@@ -159,36 +167,34 @@ std::string command_manager::set_bool(bool &value,
159167

160168
command_manager::command_manager()
161169
{
162-
_cmds.emplace_back(
163-
register_multiple_commands({"help", "h", "H", "Help"},
164-
"Display help information",
165-
"[command]",
166-
[this](const std::vector<std::string> &args) {
167-
std::stringstream ss;
168-
if (args.empty()) {
169-
std::unordered_set<command_instance *> cmds;
170-
utils::auto_read_lock l(_lock);
171-
for (const auto &c : this->_handlers) {
172-
// Multiple commands with the same handler are print
173-
// only once.
174-
if (cmds.insert(c.second.get()).second) {
175-
ss << c.second->help << std::endl;
176-
}
177-
}
178-
} else {
179-
utils::auto_read_lock l(_lock);
180-
auto it = _handlers.find(args[0]);
181-
if (it == _handlers.end()) {
182-
ss << "cannot find command '" << args[0] << "'";
183-
} else {
184-
ss.width(6);
185-
ss << std::left << it->second->help << std::endl
186-
<< it->second->args << std::endl;
187-
}
188-
}
189-
190-
return ss.str();
191-
}));
170+
_cmds.emplace_back(register_multiple_commands(
171+
{"help", "h", "H", "Help"},
172+
"Display help information",
173+
"[command]",
174+
[this](const std::vector<std::string> &args) {
175+
std::stringstream ss;
176+
if (args.empty()) {
177+
std::unordered_set<commands_handler *> chs;
178+
utils::auto_read_lock l(_lock);
179+
for (const auto &[_, ch] : _handler_by_cmd) {
180+
// Multiple commands with the same handler are print only once.
181+
if (gutil::InsertIfNotPresent(&chs, ch.get())) {
182+
ss << ch->help << std::endl;
183+
}
184+
}
185+
} else {
186+
utils::auto_read_lock l(_lock);
187+
const auto ch = gutil::FindPtrOrNull(_handler_by_cmd, args[0]);
188+
if (!ch) {
189+
ss << "cannot find command '" << args[0] << "'";
190+
} else {
191+
ss.width(6);
192+
ss << std::left << ch->help << std::endl << ch->args << std::endl;
193+
}
194+
}
195+
196+
return ss.str();
197+
}));
192198

193199
_cmds.emplace_back(register_multiple_commands(
194200
{"repeat", "r", "R", "Repeat"},
@@ -241,10 +247,10 @@ command_manager::command_manager()
241247
command_manager::~command_manager()
242248
{
243249
_cmds.clear();
244-
CHECK(_handlers.empty(),
250+
CHECK(_handler_by_cmd.empty(),
245251
"All commands must be deregistered before command_manager is destroyed, however '{}' is "
246252
"still registered",
247-
_handlers.begin()->first);
253+
_handler_by_cmd.begin()->first);
248254
}
249255

250256
} // namespace dsn

src/utils/command_manager.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,16 @@
2828

2929
#include <fmt/core.h>
3030
#include <fmt/format.h>
31+
#include <nlohmann/json.hpp>
32+
#include <nlohmann/json_fwd.hpp>
3133
// IWYU pragma: no_include <ext/alloc_traits.h>
3234
#include <stdint.h>
3335
#include <functional>
3436
#include <map>
3537
#include <memory>
36-
#include <nlohmann/json.hpp>
37-
#include <nlohmann/json_fwd.hpp>
3838
#include <string>
3939
#include <vector>
4040

41-
#include "utils/autoref_ptr.h"
4241
#include "utils/fmt_logging.h"
4342
#include "utils/ports.h"
4443
#include "utils/singleton.h"
@@ -47,7 +46,6 @@
4746
#include "utils/synchronize.h"
4847

4948
namespace dsn {
50-
5149
class command_deregister;
5250

5351
class command_manager : public ::dsn::utils::singleton<command_manager>
@@ -85,22 +83,25 @@ class command_manager : public ::dsn::utils::singleton<command_manager>
8583
});
8684
}
8785

88-
// Register a single 'command' with the 'help' description, its arguments are describe in
86+
// Register a single 'command' with the 'help' description, its arguments are described in
8987
// 'args'.
9088
std::unique_ptr<command_deregister>
9189
register_single_command(const std::string &command,
9290
const std::string &help,
9391
const std::string &args,
9492
command_handler handler) WARN_UNUSED_RESULT;
9593

96-
// Register multiple 'commands' with the 'help' description, their arguments are describe in
94+
// Register multiple 'commands' with the 'help' description, their arguments are described in
9795
// 'args'.
9896
std::unique_ptr<command_deregister>
9997
register_multiple_commands(const std::vector<std::string> &commands,
10098
const std::string &help,
10199
const std::string &args,
102100
command_handler handler) WARN_UNUSED_RESULT;
103101

102+
// Register a global command which is not associated with any objects.
103+
void add_global_cmd(std::unique_ptr<command_deregister> cmd);
104+
104105
bool run_command(const std::string &cmd,
105106
const std::vector<std::string> &args,
106107
/*out*/ std::string &output);
@@ -112,7 +113,7 @@ class command_manager : public ::dsn::utils::singleton<command_manager>
112113
command_manager();
113114
~command_manager();
114115

115-
struct command_instance : public ref_counter
116+
struct commands_handler
116117
{
117118
std::vector<std::string> commands;
118119
std::string help;
@@ -126,7 +127,7 @@ class command_manager : public ::dsn::utils::singleton<command_manager>
126127
const std::string &args,
127128
command_handler handler) WARN_UNUSED_RESULT;
128129

129-
void deregister_command(uintptr_t handle);
130+
void deregister_command(uintptr_t cmd_id);
130131

131132
static std::string
132133
set_bool(bool &value, const std::string &name, const std::vector<std::string> &args);
@@ -177,9 +178,8 @@ class command_manager : public ::dsn::utils::singleton<command_manager>
177178
return msg.dump(2);
178179
}
179180

180-
typedef ref_ptr<command_instance> command_instance_ptr;
181181
utils::rw_lock_nr _lock;
182-
std::map<std::string, command_instance_ptr> _handlers;
182+
std::map<std::string, std::shared_ptr<commands_handler>> _handler_by_cmd;
183183

184184
std::vector<std::unique_ptr<command_deregister>> _cmds;
185185
};

src/utils/logging_provider.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,13 @@ class logging_provider
6464

6565
virtual void flush() = 0;
6666

67-
void deregister_commands() { _cmds.clear(); }
68-
6967
protected:
7068
static std::unique_ptr<logging_provider> _logger;
7169

7270
static logging_provider *create_default_instance();
7371

7472
logging_provider(log_level_t stderr_start_level) : _stderr_start_level(stderr_start_level) {}
7573

76-
std::vector<std::unique_ptr<command_deregister>> _cmds;
7774
const log_level_t _stderr_start_level;
7875
};
7976

src/utils/simple_logger.cpp

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
#include <cstdint>
3737
#include <ctime>
3838
#include <functional>
39-
#include <memory>
39+
#include <mutex>
4040
#include <type_traits>
4141
#include <utility>
4242
#include <vector>
@@ -234,37 +234,39 @@ simple_logger::simple_logger(const char *log_dir, const char *role_name)
234234

235235
create_log_file();
236236

237-
// TODO(yingchun): simple_logger is destroyed after command_manager, so will cause crash like
238-
// "assertion expression: [_handlers.empty()] All commands must be deregistered before
239-
// command_manager is destroyed, however 'flush-log' is still registered".
240-
// We need to fix it.
241-
_cmds.emplace_back(::dsn::command_manager::instance().register_single_command(
242-
"flush-log",
243-
"Flush log to stderr or file",
244-
"",
245-
[this](const std::vector<std::string> &args) {
246-
this->flush();
247-
return "Flush done.";
248-
}));
249-
250-
_cmds.emplace_back(::dsn::command_manager::instance().register_single_command(
251-
"reset-log-start-level",
252-
"Reset the log start level",
253-
"[DEBUG | INFO | WARNING | ERROR | FATAL]",
254-
[](const std::vector<std::string> &args) {
255-
log_level_t start_level;
256-
if (args.size() == 0) {
257-
start_level = enum_from_string(FLAGS_logging_start_level, LOG_LEVEL_INVALID);
258-
} else {
259-
std::string level_str = "LOG_LEVEL_" + args[0];
260-
start_level = enum_from_string(level_str.c_str(), LOG_LEVEL_INVALID);
261-
if (start_level == LOG_LEVEL_INVALID) {
262-
return "ERROR: invalid level '" + args[0] + "'";
263-
}
264-
}
265-
set_log_start_level(start_level);
266-
return std::string("OK, current level is ") + enum_to_string(start_level);
267-
}));
237+
static std::once_flag flag;
238+
std::call_once(flag, [&]() {
239+
::dsn::command_manager::instance().add_global_cmd(
240+
::dsn::command_manager::instance().register_single_command(
241+
"flush-log",
242+
"Flush log to stderr or file",
243+
"",
244+
[this](const std::vector<std::string> &args) {
245+
this->flush();
246+
return "Flush done.";
247+
}));
248+
249+
::dsn::command_manager::instance().add_global_cmd(
250+
::dsn::command_manager::instance().register_single_command(
251+
"reset-log-start-level",
252+
"Reset the log start level",
253+
"[DEBUG | INFO | WARNING | ERROR | FATAL]",
254+
[](const std::vector<std::string> &args) {
255+
log_level_t start_level;
256+
if (args.size() == 0) {
257+
start_level =
258+
enum_from_string(FLAGS_logging_start_level, LOG_LEVEL_INVALID);
259+
} else {
260+
std::string level_str = "LOG_LEVEL_" + args[0];
261+
start_level = enum_from_string(level_str.c_str(), LOG_LEVEL_INVALID);
262+
if (start_level == LOG_LEVEL_INVALID) {
263+
return "ERROR: invalid level '" + args[0] + "'";
264+
}
265+
}
266+
set_log_start_level(start_level);
267+
return std::string("OK, current level is ") + enum_to_string(start_level);
268+
}));
269+
});
268270
}
269271

270272
void simple_logger::create_log_file()

0 commit comments

Comments
 (0)