No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

logger.py 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #-*- coding: utf-8 -*-
  2. import logging, logging.handlers
  3. import os.path
  4. from lodel.settings import Settings
  5. # Variables & constants definitions
  6. default_format = '%(asctime)-15s %(levelname)s %(_pathname)s:%(_lineno)s:%(_funcName)s() : %(message)s'
  7. simple_format = '%(asctime)-15s %(levelname)s : %(message)s'
  8. SECURITY_LOGLEVEL = 35
  9. logging.addLevelName(SECURITY_LOGLEVEL, 'SECURITY')
  10. handlers = dict() # Handlers list (generated from settings)
  11. # Fetching default root logger
  12. logger = logging.getLogger()
  13. # Setting options from Lodel.settings.Settings.logging
  14. def __init_from_settings():
  15. # Disabled, because the custom format raises error (enable to give the _ preffixed arguments to logger resulting into a KeyError exception )
  16. #logging.captureWarnings(True) # Log warnings
  17. logger.setLevel(logging.DEBUG)
  18. for name, logging_opt in Settings.logging.items():
  19. add_handler(name, logging_opt)
  20. ##@brief Add an handler, identified by a name, to a given logger
  21. #
  22. # logging_opt is a dict with logger option. Allowed keys are :
  23. # - filename : take a filepath as value and cause the use of a logging.handlers.RotatingFileHandler
  24. # - level : the minimum logging level for a logger, takes values [ 'DEBUG', 'INFO', 'WARNING', 'SECURITY', 'ERROR', 'CRITICAL' ]
  25. # - format : DONT USE THIS OPTION (or if you use it be sure to includes %(_pathname)s %(_lineno)s %(_funcName)s format variables in format string
  26. # - context : boolean, if True include the context (module:lineno:function_name) in the log format
  27. # @todo Move the logging_opt documentation somewhere related with settings
  28. #
  29. # @param name str : The handler name
  30. # @param logging_opt dict : dict containing options ( see above )
  31. def add_handler(name, logging_opt):
  32. logger = logging.getLogger()
  33. if name in handlers:
  34. raise KeyError("A handler named '%s' allready exists")
  35. if 'filename' in logging_opt:
  36. maxBytes = (1024 * 10) if 'maxBytes' not in logging_opt else logging_opt['maxBytes']
  37. backupCount = 10 if 'backupCount' not in logging_opt else logging_opt['backupCount']
  38. handler = logging.handlers.RotatingFileHandler(
  39. logging_opt['filename'],
  40. maxBytes = maxBytes,
  41. backupCount = backupCount,
  42. encoding = 'utf-8')
  43. else:
  44. handler = logging.StreamHandler()
  45. if 'level' in logging_opt:
  46. handler.setLevel(getattr(logging, logging_opt['level'].upper()))
  47. if 'format' in logging_opt:
  48. formatter = logging.Formatter(logging_opt['format'])
  49. else:
  50. if 'context' in logging_opt and not logging_opt['context']:
  51. formatter = logging.Formatter(simple_format)
  52. else:
  53. formatter = logging.Formatter(default_format)
  54. handler.setFormatter(formatter)
  55. handlers[name] = handler
  56. logger.addHandler(handler)
  57. ##@brief Remove an handler generated from configuration (runtime logger configuration)
  58. # @param name str : handler name
  59. def remove_handler(name):
  60. if name in handlers:
  61. logger.removeHandler(handlers[name])
  62. # else: can we do anything ?
  63. ##@brief Utility function that disable unconditionnaly handlers that implies console output
  64. # @note In fact, this function disables handlers generated from settings wich are instances of logging.StreamHandler
  65. def remove_console_handlers():
  66. for name, handler in handlers.items():
  67. if isinstance(handler, logging.StreamHandler):
  68. remove_handler(name)
  69. # Utility functions
  70. ##@brief Generic logging function
  71. # @param lvl int : Log severity
  72. # @param msg str : log message
  73. # @param *args : additional positionnal arguments
  74. # @param **kwargs : additional named arguments
  75. def log(lvl, msg, *args, **kwargs):
  76. caller = logger.findCaller() # Opti warning : small overhead
  77. extra = {
  78. '_pathname': os.path.relpath(
  79. caller[0],
  80. start=Settings.lodel2_lib_path
  81. ), # os.path.relpath add another small overhead
  82. '_lineno': caller[1],
  83. '_funcName': caller[2],
  84. }
  85. logger.log(lvl, msg, extra = extra, *args, **kwargs)
  86. def debug(msg, *args, **kwargs): log(logging.DEBUG, msg, *args, **kwargs)
  87. def info(msg, *args, **kwargs): log(logging.INFO, msg, *args, **kwargs)
  88. def warning(msg, *args, **kwargs): log(logging.WARNING, msg, *args, **kwargs)
  89. def security(msg, *args, **kwargs): log(SECURITY_LOGLEVEL, msg, *args, **kwargs)
  90. def error(msg, *args, **kwargs): log(logging.ERROR, msg, *args, **kwargs)
  91. def critical(msg, *args, **kwargs): log(logging.CRITICAL, msg, *args, **kwargs)
  92. # Initialisation triggering
  93. if len(handlers) == 0:
  94. __init_from_settings()