}
// module-independent sink for the Python root-logger fallback; the Python
-// side filters and formats, this just emits like PyModuleRunner::log()
+// side maps the record's level to a ceph debug level, so the usual
+// subsystem gating applies per record
static PyObject*
ceph_mgr_log(PyObject *self, PyObject *args)
{
+ int level = 0;
char *record = nullptr;
- if (!PyArg_ParseTuple(args, "s:mgr_log", &record)) {
+ if (!PyArg_ParseTuple(args, "is:mgr_log", &level, &record)) {
return nullptr;
}
+ if (level < 0) {
+ level = 0;
+ }
#undef dout_prefix
#define dout_prefix *_dout
- dout(0) << record << dendl;
+ dout(ceph::dout::need_dynamic(level)) << record << dendl;
#undef dout_prefix
#define dout_prefix *_dout << "mgr[py] "
Py_RETURN_NONE;
{
static PyMethodDef module_methods[] = {
{"mgr_log", ceph_mgr_log, METH_VARARGS,
- "log a preformatted record to the mgr daemon log"},
+ "log a preformatted record to the mgr daemon log at a debug level"},
{nullptr, nullptr, 0, nullptr}
};
static PyModuleDef ceph_module_def = {
"[mgr %(levelname)-4s %(name)s] %(message)s"
))
+ @staticmethod
+ def _ceph_log_level(levelno: int) -> int:
+ # inverse of _ceph_log_level_to_python. INFO -> 2 keeps it in the
+ # log file at the default debug_mgr=2/5; DEBUG -> 20 keeps floods
+ # out unless raised, e.g. 2/20 for in-memory capture only.
+ if levelno >= logging.ERROR:
+ return 0
+ if levelno >= logging.WARNING:
+ return 1
+ if levelno >= logging.INFO:
+ return 2
+ return 20
+
def emit(self, record: logging.LogRecord) -> None:
if record.levelno >= self.level:
- ceph_module.mgr_log(self.format(record))
+ ceph_module.mgr_log(self._ceph_log_level(record.levelno),
+ self.format(record))
class ClusterLogHandler(logging.Handler):
return next((h for h in root.handlers
if isinstance(h, MgrRootHandler)), None)
+ @staticmethod
+ def _debug_mgr_gather_to_python(log_level: str) -> str:
+ # records are emitted at their own mapped levels, so forward
+ # everything the C++ side could gather: gate on the higher of the
+ # two debug_mgr levels, not just the file level
+ gather = 0
+ if log_level:
+ try:
+ gather = max(int(level) for level in log_level.split("/", 1))
+ except ValueError:
+ pass
+ if gather >= 20:
+ return "DEBUG"
+ if gather >= 2:
+ return "INFO"
+ if gather >= 1:
+ return "WARNING"
+ return "ERROR"
+
def _set_log_level(self,
mgr_level: str,
module_level: str,
# even when the module level is unchanged
root_handler = self._mgr_root_handler()
if root_handler is not None:
- root_handler.setLevel(self._ceph_log_level_to_python(mgr_level))
+ root_handler.setLevel(self._debug_mgr_gather_to_python(mgr_level))
module_level = module_level.upper() if module_level else ''
if not self._module_level: