修改ceph源码——增加函数级别日志输出功能

ceph中原有日志功能介绍

当前ceph中的日志只能做到某一子系统(subsystem)的某一级别日志,比如给ceph_subsys_osd子系统设置日志级别为5,则表示任何级别小于等于5的osd子系统日志都可以输出到日志文件。关于子系统日志级别设置有两种方法
(1)在配置文件中指定
比如

debug_osd 5 

就是将osd子系统的日志输出级别设置为5
(2)利用admin_socket动态设置
ceph实现了admin_socket(即unix socket)来用于用户和ceph的组件进行通信。我们可以利用admin_socket来动态设置日志输出级别,如下:

ceph daemon /var/run/ceph/ceph-osd.0.asok congfig set debug_osd 5

在调试问题时一般会用到第二种方法进行运行时更改日志输出级别,但是这个方法有一个问题就是会输出大量不相关的日志信息,这会增加问题排查的难度,因此本人修改ceph日志模块,增加了可以具体到某一函数的日志输出功能,并可以利用admin_socket动态修改.

函数级别日志输出

增加函数级别日志的动态设置、查看、删除等功能,具体实现如下
(1)定义相关类与方法
在src/log/SubsystemMap.h中定义相关的类和方法

class ZyLog
{

private:
  std::map> zy_log_funs;

public:
  void zy_set_fun_log(unsigned subsys, std::string fun_name)
  {
    zy_log_funs[subsys].insert(fun_name);
  }

  void zy_unset_fun_log(unsigned subsys, std::string fun_name)
  {
    zy_log_funs[subsys].erase(fun_name); 
    if(zy_log_funs[subsys].empty())
    {
      zy_log_funs.erase(subsys);
    }
  }

  bool zy_should_gather(unsigned subsys, std::string fun_name)
  {
    return (zy_log_funs[subsys].find(fun_name) == zy_log_funs[subsys].end());
  }

  int zy_subsys_string_to_unsigned(std::string subsys_str)
  {
    constexpr auto t = ceph_subsys_get_as_array();
    for(unsigned i = 0; i < t.size(); i++)
    {
      if(std::string(t[i].name) == subsys_str)
        return i;
    }
    return -1;
  }

  void get_all_subsys(Formatter *f)
  {
    auto t = ceph_subsys_get_as_array();
    for(unsigned i = 0; i < t.size(); i++)
    {
      f->dump_stream(t[i].name);
    }
  }

  void get_all_setted_funs(Formatter *f)
  {
    for(auto subsys_it = zy_log_funs.begin(); subsys_it != zy_log_funs.end(); subsys_it++)
    {
      if(subsys_it->first >= ceph_subsys_get_num())
        continue;

      const auto subsys_name = ceph_subsys_get_as_array()[subsys_it->first].name; 
      for(auto fun_it = subsys_it->second.begin(); fun_it != subsys_it->second.end(); fun_it++)
        f->dump_string(subsys_name, *fun_it);
    } 
  }

(2)注册相关命令
在src/common/ceph_context.cc的CephContext类的构造函数中,增加如下几行

  _admin_socket->register_command("zylog set", "zylog set name=subsys,type=CephString name=fun,type=CephString", _admin_hook, "");
  _admin_socket->register_command("zylog unset", "zylog unset name=subsys,type=CephString name=fun,type=CephString", _admin_hook, "");
  _admin_socket->register_command("zylog showsubsys", "zylog showsubsys", _admin_hook, "");
  _admin_socket->register_command("zylog showsets", "zylog showsets", _admin_hook, "");

这里分别注册了set unset showsubsys showsets四个命令,其中showsubsys是查看ceph中有哪些子系统可以设置
set命令的用法就是ceph daemon /var/run/ceph/ceph-osd.0.asok zylog set osd heartbeat ,表示设置osd子系统中的heartbeat函数中的日志为输出。
(3)处理相关命令
在src/common/ceph_context.cc的do_command函数中增加如下代码

    else if (command == "zylog set") {
      std::string zy_subsys;
      std::string zy_fun;

      if (!(cmd_getval(this, cmdmap, "subsys", zy_subsys)) ||
          !(cmd_getval(this, cmdmap, "fun", zy_fun))) {
        f->dump_string("error", "syntax error: 'config set  '");
      } else {
          int subsys_id = _conf->funlog.zy_subsys_string_to_unsigned(zy_subsys);
          if(-1 == subsys_id)
            f->dump_stream("error") << zy_subsys << " is not a subsys";
          else
          {
            _conf->funlog.zy_set_fun_log((unsigned)subsys_id, zy_fun);
            f->dump_stream("success") << " set " << zy_subsys << "(" << subsys_id << ") " << zy_fun;
          }
      }
    }
    else if (command == "zylog unset") {
      std::string zy_subsys;
      std::string zy_fun;

      if (!(cmd_getval(this, cmdmap, "subsys", zy_subsys)) ||
          !(cmd_getval(this, cmdmap, "fun", zy_fun))) {
        f->dump_string("error", "syntax error: 'config set  '");
      } else {
          int subsys_id = _conf->funlog.zy_subsys_string_to_unsigned(zy_subsys);
          if(-1 == subsys_id)
            f->dump_stream("error ") << zy_subsys << "is not a subsys";
          else
          {
            _conf->funlog.zy_unset_fun_log((unsigned)subsys_id, zy_fun);
            f->dump_stream("success ") << "unset" << zy_subsys << "(" << subsys_id << ") " << zy_fun;
          }
      }
    }
    else if (command == "zylog showsubsys") {
      _conf->funlog.get_all_subsys(f); 
    }
    else if (command == "zylog showsets") {
      _conf->funlog.get_all_setted_funs(f);
    }

(4)修改dout.h
修改后的内容如下

#define dout_impl(cct, sub, v)            \
  do {                  \
    unsigned funsub = sub;  \
    const string funname = __func__;  \
    bool funlog_out = false;  \
  const bool should_gather = [&](const auto cctX) {     \
    if constexpr (ceph::dout::is_dynamic::value || \
      ceph::dout::is_dynamic::value) {   \
      return cctX->_conf->subsys.should_gather(sub, v);     \
    } else {                \
      /* The parentheses are **essential** because commas in angle  \
       * brackets are NOT ignored on macro expansion! A language's  \
       * limitation, sorry. */            \
      return (cctX->_conf->subsys.template should_gather());  \
    }                 \
  }(cct);               \
                  \
  if (!should_gather){    \
    funlog_out = !(cct->_conf->funlog.zy_should_gather(funsub, funname)); \
  } \             //如果日志在原有判断中不能输出,则继续判断函数级别日志是否允许输出
  if (should_gather || funlog_out) {              \
    static size_t _log_exp_length = 80;         \
    ceph::logging::Entry *_dout_e = NULL;           \
    if(!funlog_out) {                             \
      _dout_e =           \
        cct->_log->create_entry(v, sub, &_log_exp_length);    \
    }                   \
    else {              \
     _dout_e =           \
        cct->_log->create_entry(-1, sub, &_log_exp_length);    \  //如果函数级别日志允许输出,则需要将改条日志的level改为-1,这样是为了防止在submit_entry日志时再次被过滤掉。
    }                   \
    static_assert(std::is_convertible::value,    \
      "provided cct must be compatible with CephContext*"); \
    auto _dout_cct = cct;           \
    std::ostream* _dout = &_dout_e->get_ostream();

最后附上代码修改记录
https://github.com/jasonlinuxzhang/ceph/commit/c6b5a14c8ea0bfd485fd30bc0afdf74f1f2f137e

需要改进的地方

(1)在设置函数级别日志输出时,我没有判断该函数是否存在于子系统内,目前我还没有找到方法获得某个子系统内或者某个文件内的函数表。
(2)可以设置命令格式为file:function而不是subsys:function,这样就可以具体到某一个文件中的某个函数,而不是某一个某一个子系统,目前也没有找到修改方法。

你可能感兴趣的:(ceph)