Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(configurable-logger): logging support in the core library #58

Merged
merged 31 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8da43c2
feat(configurable-logger): logging support in the core library
sufyankhanrao Apr 29, 2024
ea7c701
updates library version
sufyankhanrao Apr 29, 2024
fe556a4
reverts core interfaces dependency change
sufyankhanrao Apr 29, 2024
dc816a0
adds the logging implementation
sufyankhanrao Apr 30, 2024
36a2bca
fixes http_method property name
sufyankhanrao Apr 30, 2024
334a014
fixed default value for headers list and also fixes the case sensitiv…
sufyankhanrao Apr 30, 2024
85b281c
updates the format of the logger
sufyankhanrao Apr 30, 2024
c210adb
renamed logging fields
sufyankhanrao Apr 30, 2024
a9291f2
renamed classes
sufyankhanrao Apr 30, 2024
e13bc61
removed previous logger implementation
sufyankhanrao Apr 30, 2024
33d2b2a
fixes all tests for log_helper
sufyankhanrao May 1, 2024
e39dc07
removed the json formatted console logging
sufyankhanrao May 2, 2024
4a51c88
moved non-sensitive headers to constants class
sufyankhanrao May 2, 2024
c811334
assigned the responsibility to the SdkLogger class regarding using th…
sufyankhanrao May 2, 2024
5a5e172
fixed content-type headers value bug
sufyankhanrao May 2, 2024
13ee8b3
removed older logging support and adds tests for new implementation
sufyankhanrao May 6, 2024
5e8dafe
adds core interfaces commit reference as dependency
sufyankhanrao May 6, 2024
7875726
adds documentation around newly added logging classes
sufyankhanrao May 7, 2024
da8810f
removed string formatter as it is not needed anymore
sufyankhanrao May 7, 2024
023ee92
removed duplication reported by code-climate
sufyankhanrao May 7, 2024
233d083
removed intermediate params variable when logging headers
sufyankhanrao May 7, 2024
7bea4b9
reverted back the core-interfaces dep change
sufyankhanrao May 7, 2024
df70761
removed logger type
sufyankhanrao May 7, 2024
6cfdab7
removes timestamp from the default logger
sufyankhanrao May 8, 2024
8c8443c
renamed BaseLoggingConfiguration class
sufyankhanrao May 8, 2024
de1f602
applied suggestions after internal demo to Mujahid
sufyankhanrao May 8, 2024
681de11
removed unused import
sufyankhanrao May 8, 2024
580c669
updates logging level default-value's decision responsibility
sufyankhanrao May 9, 2024
2e25a08
adds suggestion after the first PR review
sufyankhanrao May 9, 2024
3e08bb3
adds final refactoring
sufyankhanrao May 9, 2024
fd39f64
reverted back the change for interfaces dependency
sufyankhanrao May 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,21 @@ pip install apimatic-core
| [`ApiResponse`](apimatic_core/http/response/api_response.py) | A wrapper class for Api Response |
| [`HttpResponse`](apimatic_core/http/response/http_response.py) | A class which contains information about the HTTP Response |

## Logging Configuration
| Name | Description |
|------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
| [`ApiLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds overall logging configuration for logging an API call |
| [`ApiRequestLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds logging configuration for API request |
| [`ApiResponseLoggingConfiguration`](apimatic_core/logger/configuration/api_logging_configuration.py) | Holds logging configuration for API response |


## Logger
| Name | Description |
|------------------------------------------------------------------|-----------------------------------------------------|
| [`EndpointLogger`](apimatic_core/logger/endpoint_logger.py) | A class to provide logging for an HTTP request |
| Name | Description |
|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`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 |
| [`NoneSdkLogger`](apimatic_core/logger/sdk_logger.py) | Represents the default implementation for ApiLogger when no logging configuration is provided |
| [`ConsoleLogger`](apimatic_core/logger/default_logger.py) | Represents the default implementation for Logger when no custom implementation is provided |
| [`LoggerFactory`](apimatic_core/logger/sdk_logger.py) | Responsible for providing the ApiLogger implementation (`SdkLogger` \| `NoneSdkLogger`) based on the logging configuration |

## Types
| Name | Description |
Expand Down
3 changes: 2 additions & 1 deletion apimatic_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
'factories',
'types',
'logger',
'exceptions'
'exceptions',
'constants'
]
69 changes: 24 additions & 45 deletions apimatic_core/api_call.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
from apimatic_core.configurations.endpoint_configuration import EndpointConfiguration
from apimatic_core.configurations.global_configuration import GlobalConfiguration
from apimatic_core.logger.endpoint_logger import EndpointLogger
from apimatic_core.logger.sdk_logger import LoggerFactory
from apimatic_core.response_handler import ResponseHandler


class ApiCall:

@property
def new_builder(self):
return ApiCall(self._global_configuration, logger=self._endpoint_logger.logger)
return ApiCall(self._global_configuration)

def __init__(
self,
global_configuration=GlobalConfiguration(),
logger=None
global_configuration=GlobalConfiguration()
):
self._global_configuration = global_configuration
self._request_builder = None
self._response_handler = ResponseHandler()
self._endpoint_configuration = EndpointConfiguration()
self._endpoint_logger = EndpointLogger(logger)
self._endpoint_name_for_logging = None
self._api_logger = LoggerFactory.get_api_logger(self._global_configuration.get_http_client_configuration()
.logging_configuration)

def request(self, request_builder):
self._request_builder = request_builder
Expand All @@ -34,52 +33,32 @@ def endpoint_configuration(self, endpoint_configuration):
self._endpoint_configuration = endpoint_configuration
return self

def endpoint_name_for_logging(self, endpoint_name_for_logging):
self._endpoint_name_for_logging = endpoint_name_for_logging
return self

def execute(self):
try:
_http_client_configuration = self._global_configuration.get_http_client_configuration()

if _http_client_configuration.http_client is None:
raise ValueError("An HTTP client instance is required to execute an Api call.")

_http_request = self._request_builder \
.endpoint_logger(self._endpoint_logger) \
.endpoint_name_for_logging(self._endpoint_name_for_logging) \
.build(self._global_configuration)
_http_client_configuration = self._global_configuration.get_http_client_configuration()

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

self.update_http_callback(_http_callback,
_http_callback.on_before_request if _http_callback is not None else None,
_http_request, "Calling the on_before_request method of http_call_back for {}.")
_http_request = self._request_builder.build(self._global_configuration)

self._endpoint_logger.debug("Raw request for {} is: {}".format(
self._endpoint_name_for_logging, vars(_http_request)))
# Logging the request
self._api_logger.log_request(_http_request)

_http_response = _http_client_configuration.http_client.execute(
_http_request, self._endpoint_configuration)
_http_callback = _http_client_configuration.http_callback

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

self.update_http_callback(_http_callback,
_http_callback.on_after_response if _http_callback is not None else None,
_http_response, "Calling on_after_response method of http_call_back for {}.")
# Executing the HTTP call
_http_response = _http_client_configuration.http_client.execute(
_http_request, self._endpoint_configuration)

return self._response_handler.endpoint_logger(self._endpoint_logger) \
.endpoint_name_for_logging(self._endpoint_name_for_logging) \
.handle(_http_response, self._global_configuration.get_global_errors())
except Exception as e:
self._endpoint_logger.error(e)
raise
# Logging the response
self._api_logger.log_response(_http_response)

def update_http_callback(self, http_callback, func, argument, log_message):
if http_callback is None:
return
# Applying the after receiving HTTP response callback
if _http_callback is not None:
_http_callback.on_after_response(_http_response)

self._endpoint_logger.info(log_message.format(
self._endpoint_name_for_logging))
func(argument)
return self._response_handler.handle(_http_response, self._global_configuration.get_global_errors())
3 changes: 3 additions & 0 deletions apimatic_core/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__all__ = [
'logger_constants'
]
44 changes: 44 additions & 0 deletions apimatic_core/constants/logger_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

class LoggerConstants:
METHOD = "method"
"""Key representing the method of the HTTP request."""

URL = "url"
"""Key representing the URL of the HTTP request."""

QUERY_PARAMETER = "query_parameter"
"""Key representing the query parameters of the HTTP request."""

HEADERS = "headers"
"""Key representing the headers of the HTTP request or response."""

BODY = "body"
"""Key representing the body of the HTTP request or response."""

STATUS_CODE = "status_code"
"""Key representing the status code of the HTTP response."""

CONTENT_LENGTH = "content_length"
"""Key representing the content length of the HTTP response."""

CONTENT_TYPE = "content_type"
"""Key representing the content type of the HTTP response."""

CONTENT_LENGTH_HEADER = "content-length"
"""Key representing the content length header."""

CONTENT_TYPE_HEADER = "content-type"
"""Key representing the content type header."""

NON_SENSITIVE_HEADERS = tuple(map(lambda x: x.lower(), [
"Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language",
"Access-Control-Allow-Origin", "Cache-Control", "Connection",
"Content-Encoding", "Content-Language", "Content-Length", "Content-Location",
"Content-MD5", "Content-Range", "Content-Type", "Date", "ETag", "Expect",
"Expires", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match",
"If-Range", "If-Unmodified-Since", "Keep-Alive", "Last-Modified", "Location",
"Max-Forwards", "Pragma", "Range", "Referer", "Retry-After", "Server",
"Trailer", "Transfer-Encoding", "Upgrade", "User-Agent", "Vary", "Via",
"Warning", "X-Forwarded-For", "X-Requested-With", "X-Powered-By"
]))
"""List of sensitive headers that need to be filtered."""
23 changes: 16 additions & 7 deletions apimatic_core/http/configurations/http_client_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,20 @@ def retry_statuses(self):
def retry_methods(self):
return self._retry_methods

def __init__(
self, http_client_instance=None,
override_http_client_configuration=False, http_call_back=None,
timeout=60, max_retries=0, backoff_factor=2,
retry_statuses=[408, 413, 429, 500, 502, 503, 504, 521, 522, 524],
retry_methods=['GET', 'PUT']
):
@property
def logging_configuration(self):
return self._logging_configuration

def __init__(self, http_client_instance=None,
override_http_client_configuration=False, http_call_back=None,
timeout=60, max_retries=0, backoff_factor=2,
retry_statuses=None, retry_methods=None, logging_configuration=None):
if retry_statuses is None:
retry_statuses = [408, 413, 429, 500, 502, 503, 504, 521, 522, 524]

if retry_methods is None:
retry_methods = ['GET', 'PUT']

self._http_response_factory = HttpResponseFactory()

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

self._logging_configuration = logging_configuration

def set_http_client(self, http_client):
self._http_client = http_client
6 changes: 4 additions & 2 deletions apimatic_core/logger/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
__all__ = [
'endpoint_logger'
]
'default_logger',
'sdk_logger',
'configuration'
]
3 changes: 3 additions & 0 deletions apimatic_core/logger/configuration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__all__ = [
"api_logging_configuration",
]
Loading
Loading