Skip to content

Commit debddc3

Browse files
authored
Merge pull request #114 from JaviCerveraIngram/CPS-83-logger-adapter
CPS-83: Added LoggerAdapter.
2 parents bdbdf63 + c6ab790 commit debddc3

File tree

9 files changed

+112
-79
lines changed

9 files changed

+112
-79
lines changed

connect/logger/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
# This file is part of the Ingram Micro Cloud Blue Connect SDK.
44
# Copyright (c) 2019-2020 Ingram Micro. All Rights Reserved.
55

6-
from .logger import function_log, logger
6+
from .logger import function_log, logger, LoggerAdapter
77

88
# TODO add auto settings for cloud platforms
99

1010
__all__ = [
1111
'function_log',
12-
'logger'
12+
'logger',
13+
'LoggerAdapter'
1314
]

connect/logger/logger.py

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,37 @@
1717
logger = logging.getLogger()
1818

1919

20-
def function_log(custom_logger=None):
21-
if not custom_logger:
22-
custom_logger = logging.getLogger()
23-
sformat = " %(levelname)-6s; %(asctime)s; %(name)-6s; %(module)s:%(funcName)s:line" \
24-
"-%(lineno)d: %(message)s"
25-
for handler in custom_logger.handlers:
26-
handler.setFormatter(logging.Formatter(sformat))
27-
28-
# noinspection PyUnusedLocal
29-
def decorator(func, **kwargs):
30-
# noinspection PyShadowingNames
31-
@wraps(func)
32-
def wrapper(self, *args, **kwargs):
33-
custom_logger.info('Entering: %s', func.__name__)
34-
custom_logger.debug('Function params: {} {}'.format(args, kwargs))
35-
result = func(self, *args, **kwargs)
36-
custom_logger.debug(
37-
u'Function `{}.{}` return: {}'.format(
38-
self.__class__.__name__, func.__name__, result))
39-
return result
40-
41-
return wrapper
42-
43-
return decorator
20+
class LoggerAdapter(logging.LoggerAdapter):
21+
def __init__(self, logger_, extra=None):
22+
super(LoggerAdapter, self).__init__(logger_, extra or {})
23+
self.prefix = None
24+
self.replace_handler = None
25+
26+
def process(self, msg, kwargs):
27+
msg, kwargs = super(LoggerAdapter, self).process(msg, kwargs)
28+
if self.replace_handler:
29+
handlers_copy = self.logger.handlers[:]
30+
for handler in handlers_copy:
31+
if isinstance(handler, type(self.replace_handler)):
32+
self.logger.removeHandler(handler)
33+
self.logger.addHandler(self.replace_handler)
34+
return (
35+
'%s %s' % (self.prefix, msg) if self.prefix else msg,
36+
kwargs
37+
)
38+
39+
def setLevel(self, level):
40+
self.logger.setLevel(level)
41+
42+
43+
def function_log(func):
44+
# noinspection PyShadowingNames
45+
@wraps(func)
46+
def wrapper(self, *args, **kwargs):
47+
logger.debug('Entering: %s', func.__name__)
48+
logger.debug('Function params: {} {}'.format(args, kwargs))
49+
result = func(self, *args, **kwargs)
50+
logger.debug(u'Function `{}.{}` return: {}'
51+
.format(self.__class__.__name__, func.__name__, result))
52+
return result
53+
return wrapper

connect/resources/automation_engine.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
# This file is part of the Ingram Micro Cloud Blue Connect SDK.
44
# Copyright (c) 2019-2020 Ingram Micro. All Rights Reserved.
55

6-
import copy
76
import logging
8-
from typing import Any, Dict
7+
from typing import Any, Dict, Optional
98

10-
from connect.logger import function_log, logger as global_logger
9+
from connect.logger import function_log, LoggerAdapter
1110
from connect.models.activation_tile_response import ActivationTileResponse
1211
from connect.models.base import BaseModel
1312
from .base import BaseResource
@@ -16,19 +15,23 @@
1615

1716
class AutomationEngine(BaseResource):
1817
limit = 1000 # type: int
19-
logger = logging.getLogger()
18+
19+
def __init__(self, config=None):
20+
super(AutomationEngine, self).__init__(config)
21+
self._current_request = None
22+
self._logger_adapter = None
2023

2124
def filters(self, status='pending', **kwargs):
2225
# type: (str, Dict[str, Any]) -> Dict[str, Any]
2326
return super(AutomationEngine, self).filters(status=status, **kwargs)
2427

25-
@function_log(custom_logger=logger)
28+
@function_log
2629
def process(self, filters=None):
27-
'''
28-
# type: (Dict[str, Any]) -> None
29-
'''
30+
# # type: (Dict[str, Any]) -> None
3031
for request in self.list(filters):
32+
self._set_current_request(request)
3133
self.dispatch(request)
34+
self._set_current_request(None)
3235

3336
def dispatch(self, request):
3437
# type: (BaseModel) -> str
@@ -39,34 +42,40 @@ def process_request(self, request):
3942
raise NotImplementedError('Please implement `{}.process_request` method'
4043
.format(self.__class__.__name__))
4144

42-
@function_log(custom_logger=logger)
45+
@function_log
4346
def approve(self, pk, data):
4447
# type: (str, dict) -> str
4548
return self._api.post(path=pk + '/approve/', json=data)[0]
4649

47-
@function_log(custom_logger=logger)
50+
@function_log
4851
def inquire(self, pk):
4952
# type: (str) -> str
5053
return self._api.post(path=pk + '/inquire/', json={})[0]
5154

52-
@function_log(custom_logger=logger)
55+
@function_log
5356
def fail(self, pk, reason):
5457
# type: (str, str) -> str
5558
return self._api.post(path=pk + '/fail/', json={'reason': reason})[0]
5659

57-
@function_log(custom_logger=logger)
60+
@function_log
5861
def render_template(self, pk, template_id):
5962
# type: (str, str) -> ActivationTileResponse
6063
return TemplateResource(self.config).render(template_id, pk)
6164

62-
def _set_custom_logger(self, *args):
63-
handlers = [copy.copy(hdlr) for hdlr in global_logger.handlers]
64-
log_level = global_logger.level
65-
self.__class__.logger.setLevel(log_level)
66-
self.__class__.logger.propagate = False
67-
self.__class__.logger.handlers = handlers
68-
base = " %(levelname)-6s; %(asctime)s; %(name)-6s; %(module)s:%(funcName)s:line" \
69-
"-%(lineno)d: %(message)s"
70-
sformat = " ".join(args) + base
71-
[handler.setFormatter(logging.Formatter(sformat))
72-
for handler in self.__class__.logger.handlers]
65+
@property
66+
def logger(self):
67+
# type: () -> logging.LoggerAdapter
68+
request_id = self._current_request.id if self._current_request else 'global'
69+
name = self.__class__.__name__ + '.' + request_id
70+
if not self._logger_adapter or self._logger_adapter.logger.name != name:
71+
self._logger_adapter = LoggerAdapter(logging.getLogger(name))
72+
return self._logger_adapter
73+
74+
def _set_current_request(self, request):
75+
# type: (Optional[BaseModel]) -> None
76+
self._current_request = request
77+
self._set_logger_prefix(request)
78+
79+
def _set_logger_prefix(self, request):
80+
# type: (Optional[BaseModel]) -> None
81+
pass

connect/resources/base.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,28 @@ def urljoin(*args):
6363
lambda a, b: compat.urljoin(a + ('' if a.endswith('/') else '/'), b) if b else a,
6464
args)
6565

66-
@function_log()
66+
@function_log
6767
def get(self, path='', **kwargs):
6868
# type: (str, Any) -> Tuple[str, int]
6969
kwargs = self._fix_request_kwargs(path, kwargs)
7070
response = requests.get(**kwargs)
7171
return self._check_and_pack_response(response)
7272

73-
@function_log()
73+
@function_log
7474
def post(self, path='', **kwargs):
7575
# type: (str, Any) -> Tuple[str, int]
7676
kwargs = self._fix_request_kwargs(path, kwargs)
7777
response = requests.post(**kwargs)
7878
return self._check_and_pack_response(response)
7979

80-
@function_log()
80+
@function_log
8181
def put(self, path='', **kwargs):
8282
# type: (str, Any) -> Tuple[str, int]
8383
kwargs = self._fix_request_kwargs(path, kwargs)
8484
response = requests.put(**kwargs)
8585
return self._check_and_pack_response(response)
8686

87-
@function_log()
87+
@function_log
8888
def delete(self, path='', **kwargs):
8989
# type: (str, Any) -> Tuple[str, int]
9090
kwargs = self._fix_request_kwargs(path, kwargs)
@@ -168,7 +168,7 @@ def filters(self, **kwargs):
168168
filters[key] = val
169169
return filters
170170

171-
@function_log()
171+
@function_log
172172
def search(self, filters=None):
173173
# type: (Dict[str, Any]) -> List[Any]
174174
filters = filters or self.filters()

connect/resources/fulfillment_automation.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# Copyright (c) 2019-2020 Ingram Micro. All Rights Reserved.
55

66
from abc import ABCMeta
7-
import logging
87

98
from deprecation import deprecated
109
from typing import Optional
@@ -13,6 +12,7 @@
1312
from connect.logger import function_log
1413
from connect.models.activation_template_response import ActivationTemplateResponse
1514
from connect.models.activation_tile_response import ActivationTileResponse
15+
from connect.models.asset_request import AssetRequest
1616
from connect.models.param import Param
1717
from connect.models.fulfillment import Fulfillment
1818
from connect.models.tier_config_request import TierConfigRequest
@@ -41,7 +41,6 @@ class FulfillmentAutomation(AutomationEngine):
4141
__metaclass__ = ABCMeta
4242
resource = 'requests'
4343
model_class = Fulfillment
44-
logger = logging.getLogger('Fullfilment.logger')
4544

4645
def filters(self, status='pending', **kwargs):
4746
""" Returns the default set of filters for Fulfillment request, plus any others that you
@@ -74,11 +73,9 @@ def filters(self, status='pending', **kwargs):
7473
filters['asset.product.id__in'] = ','.join(self.config.products)
7574
return filters
7675

77-
@function_log(custom_logger=logger)
76+
@function_log
7877
def dispatch(self, request):
7978
# type: (Fulfillment) -> str
80-
self._set_custom_logger(request.asset.id, request.id)
81-
8279
conversation = request.get_conversation(self.config)
8380

8481
try:
@@ -170,7 +167,7 @@ def get_tier_config(self, tier_id, product_id):
170167
else:
171168
return None
172169

173-
@function_log(custom_logger=logger)
170+
@function_log
174171
def update_parameters(self, pk, params):
175172
""" Sends a list of Param objects to Connect for updating.
176173
@@ -193,3 +190,10 @@ def _update_conversation_if_exists(self, conversation, request_id, obj):
193190
except TypeError as ex:
194191
self.logger.error('Error updating conversation for request {}: {}'
195192
.format(request_id, ex))
193+
194+
def _set_logger_prefix(self, request):
195+
# type: (Optional[AssetRequest]) -> None
196+
if request:
197+
self.logger.prefix = request.id
198+
else:
199+
self.logger.prefix = ''

connect/resources/tier_account_request_automation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def process(self, filters=None):
5252
):
5353
self.dispatch(request)
5454

55-
@function_log(custom_logger=logger)
55+
@function_log
5656
def dispatch(self, request):
5757
result = self.process_request(request)
5858
if result.action == TierAccountRequestAction.ACCEPT:

connect/resources/tier_config_automation.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Copyright (c) 2019-2020 Ingram Micro. All Rights Reserved.
55

66
from abc import ABCMeta
7-
import logging
7+
from typing import Optional
88

99
from connect.exceptions import FailRequest, InquireRequest, SkipRequest
1010
from connect.logger import function_log
@@ -36,7 +36,6 @@ class TierConfigAutomation(AutomationEngine):
3636
__metaclass__ = ABCMeta
3737
resource = 'tier/config-requests'
3838
model_class = TierConfigRequest
39-
logger = logging.getLogger('Tier.logger')
4039

4140
def filters(self, status='pending', **kwargs):
4241
""" Returns the default set of filters for Tier Config request, plus any others that you
@@ -63,13 +62,10 @@ def filters(self, status='pending', **kwargs):
6362
filters['configuration.product.id'] = ','.join(self.config.products)
6463
return filters
6564

66-
@function_log(custom_logger=logger)
65+
@function_log
6766
def dispatch(self, request):
6867
# type: (TierConfigRequest) -> str
6968
try:
70-
self._set_custom_logger(request.id, request.configuration.id,
71-
request.configuration.account.id)
72-
7369
if self.config.products \
7470
and request.configuration.product.id not in self.config.products:
7571
return 'Invalid product'
@@ -110,7 +106,7 @@ def dispatch(self, request):
110106

111107
return ''
112108

113-
@function_log(custom_logger=logger)
109+
@function_log
114110
def update_parameters(self, pk, params):
115111
""" Sends a list of Param objects to Connect for updating.
116112
@@ -124,3 +120,10 @@ def update_parameters(self, pk, params):
124120
path=pk,
125121
json={'params': mapped_params},
126122
)[0]
123+
124+
def _set_logger_prefix(self, request):
125+
# type: (Optional[TierConfigRequest]) -> None
126+
if request:
127+
self.logger.prefix = request.id + ' - ' + request.configuration.account.id
128+
else:
129+
self.logger.prefix = ''

connect/resources/usage_automation.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# Copyright (c) 2019-2020 Ingram Micro. All Rights Reserved.
55

66
import json
7-
import logging
87
from abc import ABCMeta
98
from tempfile import NamedTemporaryFile
109

@@ -28,7 +27,6 @@ class UsageAutomation(AutomationEngine):
2827
__metaclass__ = ABCMeta
2928
resource = 'listings'
3029
model_class = UsageFile
31-
logger = logging.getLogger('Usage.logger')
3230

3331
def filters(self, status='listed', **kwargs):
3432
"""
@@ -44,9 +42,6 @@ def filters(self, status='listed', **kwargs):
4442

4543
def dispatch(self, request):
4644
# type: (UsageListing) -> str
47-
48-
self._set_custom_logger(request.id, request.contract.marketplace.id)
49-
5045
# TODO Shouldn't this raise an exception on ALL automation classes?
5146
if self.config.products \
5247
and request.product.id not in self.config.products:
@@ -63,6 +58,7 @@ def dispatch(self, request):
6358
provider_name=request.provider.name,
6459
)
6560
)
61+
6662
try:
6763
result = self.process_request(request)
6864
except FileCreationError:
@@ -219,3 +215,10 @@ def _upload_spreadsheet(self, usage_file, spreadsheet):
219215
msg = 'Unexpected server response, returned code {}'.format(status)
220216
self.logger.error('{} -- Raw response: {}'.format(msg, content))
221217
raise FileCreationError(msg)
218+
219+
def _set_logger_prefix(self, request):
220+
# type: (Optional[UsageListing]) -> None
221+
if request:
222+
self.logger.prefix = request.id + ' - ' + request.contract.marketplace.id
223+
else:
224+
self.logger.prefix = ''

0 commit comments

Comments
 (0)