# # SPDX-FileCopyrightText: 2021 Splunk, Inc. # SPDX-License-Identifier: LicenseRef-Splunk-8-2021 # # import logging import logging.handlers as handlers import os import os.path as op import time try: from splunk.clilib.bundle_paths import make_splunkhome_path except ImportError: from splunk.appserver.mrsparkle.lib.util import make_splunkhome_path logging.Formatter.converter = time.gmtime __LOG_FORMAT__ = ( "%(asctime)s +0000 log_level=%(levelname)s, pid=%(process)d, " "tid=%(threadName)s, file=%(filename)s, " "func_name=%(funcName)s, code_line_no=%(lineno)d | %(message)s" ) class Log(object): def __init__(self, namespace=None, default_level=logging.INFO): self._loggers = {} self._default_level = default_level if namespace is None: namespace = self._get_appname_from_path(op.abspath(__file__)) if namespace: namespace = namespace.lower() self._namespace = namespace def get_logger(self, name, level=None, maxBytes=25000000, backupCount=5): """ Set up a default logger. :param name: The log file name. :param level: The logging level. :param maxBytes: The maximum log file size before rollover. :param backupCount: The number of log files to retain. """ # Strip ".py" from the log file name if auto-generated by a script. if level is None: level = self._default_level name = self._get_log_name(name) if name in self._loggers: return self._loggers[name] logger = logging.getLogger(name) logfile = make_splunkhome_path(["var", "log", "splunk", name]) handler_exists = any( [True for h in logger.handlers if h.baseFilename == logfile] ) if not handler_exists: file_handler = handlers.RotatingFileHandler( logfile, mode="a", maxBytes=maxBytes, backupCount=backupCount ) formatter = logging.Formatter(__LOG_FORMAT__) file_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.setLevel(level) logger.propagate = False self._loggers[name] = logger return logger def set_level(self, level, name=None): """ Change the log level of the logging :param level: the level of the logging to be setLevel :param name: the name of the logging to set, in case it is not set, all the loggers will be affected """ if name is not None: name = self._get_log_name(name) logger = self._loggers.get(name) if logger is not None: logger.setLevel(level) else: self._default_level = level for logger in self._loggers.values(): logger.setLevel(level) def _get_log_name(self, name): if name.endswith(".py"): name = name.replace(".py", "") if self._namespace: name = "{}_{}.log".format(self._namespace, name) else: name = "{}.log".format(name) return name def _get_appname_from_path(self, absolute_path): absolute_path = op.normpath(absolute_path) parts = absolute_path.split(os.path.sep) parts.reverse() for key in ("apps", "slave-apps", "master-apps"): try: # nosemgrep: gitlab.bandit.B112 idx = parts.index(key) except ValueError: continue else: try: # nosemgrep: gitlab.bandit.B110 if parts[idx + 1] == "etc": return parts[idx - 1] except IndexError: pass continue # return None return "-"