Python singleton class decorator.
"""
Copyright (C) 2005-2015 Splunk Inc. All Rights Reserved.
Commonly used design partten for python user, includes:
- singleton (Decorator function used to build singleton)
"""
from functools import wraps
def singleton(class_):
"""
Singleton decoorator function.
"""
instances = {}
@wraps(class_)
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
class Singleton(type):
"""
Singleton meta class
"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(
*args, **kwargs)
print cls
return cls._instances[cls]
#
# Usage
#
@singleton
class Logs(object):
def __init__(self, namespace=None, default_level=logging.INFO):
self._loggers = {}
self._default_level = default_level
if namespace is None:
namespace = cutil.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]
logfile = make_splunkhome_path(["var", "log", "splunk", name])
logger = logging.getLogger(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(
"%(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")
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.itervalues():
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