1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| from functools import wraps, partial
import logging
def attach_wrapper(obj, func=None): # Helper function that attaches function as
if func is None: # attribute of an object
return partial(attach_wrapper, obj)
setattr(obj, func.__name__, func)
return func
def log(level, message): # Actual decorator
def decorate(funct):
logger = logging.getLogger(func.__module__) # Setup logger
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
log_message = f"{func.__name__} - {message}"
@wraps(func)
def wrapper(8args, **kwargs): # Logs the message and before
logger.log(level, log_message) # executing the decorated function
return func(*args, **kwargs)
@attach_wrapper(wrapper) # Attaches "set_level" to "wrapper" as attribute
def set_level(new_level): # Function that allows us to set log level
nonlocal level
level = new_level
@attach_wrapper(wrapper) # Attaches "set_message" to "wrapper"
def set_message(new_message): # Function that allows us to set message
nonlocal log_message
log_message = f"{func.__name__} - {new_message}"
return wrapper
return decorate
# Example Usage
@log(Logging.WARN "example-param")
def somefunc(args):
return args
somefunc("some args")
somefunc.set_level(logging.CRITICAL) # Change log level by accessing internal
# decorator function
somefunc.set_message("new-message") # Change log message by accessing internal
# decorator function
somefunc("some args")
|