Skip to content

Commit 1966ce7

Browse files
feat(configurable-logger): add support for the configurable logger (#58)
This commit introduces the configurable logger feature. With this enhancement, the core library now boasts a flexible logging system tailored specifically for logging HTTP request and response information. By addressing the lack of a standardized logging mechanism, developers can now effectively debug and monitor network interactions with ease. The implementation includes a modular logging framework supporting customizable logging formats, default logger, and verbosity levels, empowering users to tailor logging behavior to their specific requirements. Additionally, seamless integration with existing logging libraries. This enhancement marks a significant step towards improving the usability and reliability of the core library, enhancing the development experience for our community.
1 parent 45ce9e1 commit 1966ce7

23 files changed

Lines changed: 1029 additions & 205 deletions

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,21 @@ pip install apimatic-core
6767
| [`ApiResponse`](apimatic_core/http/response/api_response.py) | A wrapper class for Api Response |
6868
| [`HttpResponse`](apimatic_core/http/response/http_response.py) | A class which contains information about the HTTP Response |
6969

70+
## Logging Configuration
71+
| Name | Description |
72+
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
73+
| [`ApiLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds overall logging configuration for logging an API call |
74+
| [`ApiRequestLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds logging configuration for API request |
75+
| [`ApiResponseLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds logging configuration for API response |
76+
77+
7078
## Logger
71-
| Name | Description |
72-
|------------------------------------------------------------------|-----------------------------------------------------|
73-
| [`EndpointLogger`](apimatic_core/logger/endpoint_logger.py) | A class to provide logging for an HTTP request |
79+
| Name | Description |
80+
|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
81+
| [`SdkLogger`](apimatic_core/logger/sdk_logger.py) | Responsible for logging the request and response of an API call, it represents the default implementation of ApiLogger when there exist any logging configuration |
82+
| [`NoneSdkLogger`](apimatic_core/logger/sdk_logger.py) | Represents the default implementation for ApiLogger when no logging configuration is provided |
83+
| [`ConsoleLogger`](apimatic_core/logger/default_logger.py) | Represents the default implementation for Logger when no custom implementation is provided |
84+
| [`LoggerFactory`](apimatic_core/logger/sdk_logger.py) | Responsible for providing the ApiLogger implementation (`SdkLogger` \| `NoneSdkLogger`) based on the logging configuration |
7485

7586
## Types
7687
| Name | Description |

apimatic_core/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
'factories',
1111
'types',
1212
'logger',
13-
'exceptions'
13+
'exceptions',
14+
'constants'
1415
]

apimatic_core/api_call.py

Lines changed: 24 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
from apimatic_core.configurations.endpoint_configuration import EndpointConfiguration
22
from apimatic_core.configurations.global_configuration import GlobalConfiguration
3-
from apimatic_core.logger.endpoint_logger import EndpointLogger
3+
from apimatic_core.logger.sdk_logger import LoggerFactory
44
from apimatic_core.response_handler import ResponseHandler
55

66

77
class ApiCall:
88

99
@property
1010
def new_builder(self):
11-
return ApiCall(self._global_configuration, logger=self._endpoint_logger.logger)
11+
return ApiCall(self._global_configuration)
1212

1313
def __init__(
1414
self,
15-
global_configuration=GlobalConfiguration(),
16-
logger=None
15+
global_configuration=GlobalConfiguration()
1716
):
1817
self._global_configuration = global_configuration
1918
self._request_builder = None
2019
self._response_handler = ResponseHandler()
2120
self._endpoint_configuration = EndpointConfiguration()
22-
self._endpoint_logger = EndpointLogger(logger)
23-
self._endpoint_name_for_logging = None
21+
self._api_logger = LoggerFactory.get_api_logger(self._global_configuration.get_http_client_configuration()
22+
.logging_configuration)
2423

2524
def request(self, request_builder):
2625
self._request_builder = request_builder
@@ -34,52 +33,32 @@ def endpoint_configuration(self, endpoint_configuration):
3433
self._endpoint_configuration = endpoint_configuration
3534
return self
3635

37-
def endpoint_name_for_logging(self, endpoint_name_for_logging):
38-
self._endpoint_name_for_logging = endpoint_name_for_logging
39-
return self
40-
4136
def execute(self):
42-
try:
43-
_http_client_configuration = self._global_configuration.get_http_client_configuration()
44-
45-
if _http_client_configuration.http_client is None:
46-
raise ValueError("An HTTP client instance is required to execute an Api call.")
47-
48-
_http_request = self._request_builder \
49-
.endpoint_logger(self._endpoint_logger) \
50-
.endpoint_name_for_logging(self._endpoint_name_for_logging) \
51-
.build(self._global_configuration)
37+
_http_client_configuration = self._global_configuration.get_http_client_configuration()
5238

53-
_http_callback = _http_client_configuration.http_callback
39+
if _http_client_configuration.http_client is None:
40+
raise ValueError("An HTTP client instance is required to execute an Api call.")
5441

55-
self.update_http_callback(_http_callback,
56-
_http_callback.on_before_request if _http_callback is not None else None,
57-
_http_request, "Calling the on_before_request method of http_call_back for {}.")
42+
_http_request = self._request_builder.build(self._global_configuration)
5843

59-
self._endpoint_logger.debug("Raw request for {} is: {}".format(
60-
self._endpoint_name_for_logging, vars(_http_request)))
44+
# Logging the request
45+
self._api_logger.log_request(_http_request)
6146

62-
_http_response = _http_client_configuration.http_client.execute(
63-
_http_request, self._endpoint_configuration)
47+
_http_callback = _http_client_configuration.http_callback
6448

65-
self._endpoint_logger.debug("Raw response for {} is: {}".format(
66-
self._endpoint_name_for_logging, vars(_http_response)))
49+
# Applying the on before sending HTTP request callback
50+
if _http_callback is not None:
51+
_http_callback.on_before_request(_http_request)
6752

68-
self.update_http_callback(_http_callback,
69-
_http_callback.on_after_response if _http_callback is not None else None,
70-
_http_response, "Calling on_after_response method of http_call_back for {}.")
53+
# Executing the HTTP call
54+
_http_response = _http_client_configuration.http_client.execute(
55+
_http_request, self._endpoint_configuration)
7156

72-
return self._response_handler.endpoint_logger(self._endpoint_logger) \
73-
.endpoint_name_for_logging(self._endpoint_name_for_logging) \
74-
.handle(_http_response, self._global_configuration.get_global_errors())
75-
except Exception as e:
76-
self._endpoint_logger.error(e)
77-
raise
57+
# Logging the response
58+
self._api_logger.log_response(_http_response)
7859

79-
def update_http_callback(self, http_callback, func, argument, log_message):
80-
if http_callback is None:
81-
return
60+
# Applying the after receiving HTTP response callback
61+
if _http_callback is not None:
62+
_http_callback.on_after_response(_http_response)
8263

83-
self._endpoint_logger.info(log_message.format(
84-
self._endpoint_name_for_logging))
85-
func(argument)
64+
return self._response_handler.handle(_http_response, self._global_configuration.get_global_errors())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__all__ = [
2+
'logger_constants'
3+
]
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
class LoggerConstants:
3+
METHOD = "method"
4+
"""Key representing the method of the HTTP request."""
5+
6+
URL = "url"
7+
"""Key representing the URL of the HTTP request."""
8+
9+
QUERY_PARAMETER = "query_parameter"
10+
"""Key representing the query parameters of the HTTP request."""
11+
12+
HEADERS = "headers"
13+
"""Key representing the headers of the HTTP request or response."""
14+
15+
BODY = "body"
16+
"""Key representing the body of the HTTP request or response."""
17+
18+
STATUS_CODE = "status_code"
19+
"""Key representing the status code of the HTTP response."""
20+
21+
CONTENT_LENGTH = "content_length"
22+
"""Key representing the content length of the HTTP response."""
23+
24+
CONTENT_TYPE = "content_type"
25+
"""Key representing the content type of the HTTP response."""
26+
27+
CONTENT_LENGTH_HEADER = "content-length"
28+
"""Key representing the content length header."""
29+
30+
CONTENT_TYPE_HEADER = "content-type"
31+
"""Key representing the content type header."""
32+
33+
NON_SENSITIVE_HEADERS = tuple(map(lambda x: x.lower(), [
34+
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language",
35+
"Access-Control-Allow-Origin", "Cache-Control", "Connection",
36+
"Content-Encoding", "Content-Language", "Content-Length", "Content-Location",
37+
"Content-MD5", "Content-Range", "Content-Type", "Date", "ETag", "Expect",
38+
"Expires", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match",
39+
"If-Range", "If-Unmodified-Since", "Keep-Alive", "Last-Modified", "Location",
40+
"Max-Forwards", "Pragma", "Range", "Referer", "Retry-After", "Server",
41+
"Trailer", "Transfer-Encoding", "Upgrade", "User-Agent", "Vary", "Via",
42+
"Warning", "X-Forwarded-For", "X-Requested-With", "X-Powered-By"
43+
]))
44+
"""List of sensitive headers that need to be filtered."""

apimatic_core/http/configurations/http_client_configuration.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,20 @@ def retry_statuses(self):
4646
def retry_methods(self):
4747
return self._retry_methods
4848

49-
def __init__(
50-
self, http_client_instance=None,
51-
override_http_client_configuration=False, http_call_back=None,
52-
timeout=60, max_retries=0, backoff_factor=2,
53-
retry_statuses=[408, 413, 429, 500, 502, 503, 504, 521, 522, 524],
54-
retry_methods=['GET', 'PUT']
55-
):
49+
@property
50+
def logging_configuration(self):
51+
return self._logging_configuration
52+
53+
def __init__(self, http_client_instance=None,
54+
override_http_client_configuration=False, http_call_back=None,
55+
timeout=60, max_retries=0, backoff_factor=2,
56+
retry_statuses=None, retry_methods=None, logging_configuration=None):
57+
if retry_statuses is None:
58+
retry_statuses = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524]
59+
60+
if retry_methods is None:
61+
retry_methods = ['GET', 'PUT']
62+
5663
self._http_response_factory = HttpResponseFactory()
5764

5865
# The Http Client passed from the sdk user for making requests
@@ -84,5 +91,7 @@ def __init__(
8491
# The Http Client to use for making requests.
8592
self._http_client = None
8693

94+
self._logging_configuration = logging_configuration
95+
8796
def set_http_client(self, http_client):
8897
self._http_client = http_client

apimatic_core/logger/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
__all__ = [
2-
'endpoint_logger'
3-
]
2+
'default_logger',
3+
'sdk_logger',
4+
'configuration'
5+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__all__ = [
2+
"api_logging_configuration",
3+
]

0 commit comments

Comments
 (0)