Skip to content

Commit

Permalink
Enable debug logging (#111)
Browse files Browse the repository at this point in the history
* Formatting, timezone, type hints, docstrings

* Rename logging function

* Add tests for debug logging by retrieving message if MESSAGE_ID isn't correct; update pre-commit hooks

* Fix duplication of the log handlers

* Enable flake8 and add configs to pyproject.toml, apply flake8 linter
  • Loading branch information
skupriienko authored Dec 9, 2024
1 parent f918e16 commit ef4f5a0
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 38 deletions.
38 changes: 19 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,16 @@ repos:
args: [--write]

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.29.4
rev: 0.30.0
hooks:
- id: check-github-workflows

- repo: https://github.com/pre-commit/mirrors-autopep8
rev: v2.0.4
hooks:
- id: autopep8
exclude: ^docs/

- repo: https://github.com/akaihola/darker
rev: v2.1.1
hooks:
Expand All @@ -85,25 +91,19 @@ repos:
- --remove-unused-variable
- --ignore-init-module-imports

- repo: https://github.com/pre-commit/mirrors-autopep8
rev: v2.0.4
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: autopep8
- id: flake8
additional_dependencies:
- radon
- flake8-docstrings
- Flake8-pyproject
exclude: ^docs/

# - repo: https://github.com/pycqa/flake8
# rev: 7.1.1
# hooks:
# - id: flake8
# additional_dependencies:
# - radon
# - flake8-docstrings
# - Flake8-pyproject
# exclude: ^docs/


- repo: https://github.com/PyCQA/pylint
rev: v3.3.1
rev: v3.3.2
hooks:
- id: pylint
args:
Expand All @@ -117,7 +117,7 @@ repos:

- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: v0.7.2
rev: v0.8.2
hooks:
# Run the linter.
- id: ruff
Expand All @@ -141,12 +141,12 @@ repos:
exclude: ^samples/

- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.388
rev: v1.1.390
hooks:
- id: pyright

- repo: https://github.com/PyCQA/bandit
rev: 1.7.10
rev: 1.8.0
hooks:
- id: bandit
args: ["-c", "pyproject.toml", "-r", "."]
Expand All @@ -155,7 +155,7 @@ repos:
additional_dependencies: [".[toml]"]

- repo: https://github.com/crate-ci/typos
rev: v1.27.0
rev: typos-dict-v0.11.37
hooks:
- id: typos

Expand Down
75 changes: 61 additions & 14 deletions mailjet_rest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,18 @@
import json
import logging
import re
import sys
from datetime import datetime
from datetime import timezone
from re import Match
from typing import TYPE_CHECKING
from typing import Any
from typing import Callable

import requests # type: ignore[import-untyped]
from requests.compat import urljoin # type: ignore[import-untyped]

from .utils.version import get_version
from mailjet_rest.utils.version import get_version


if TYPE_CHECKING:
Expand Down Expand Up @@ -548,28 +552,71 @@ def build_url(
return url


def parse_response(response: Response, debug: bool = False) -> Any:
"""Parse the response from an API request.
def logging_handler(
to_file: bool = False,
) -> logging.Logger:
"""Create and configure a logger for logging API requests.
This function extracts the JSON data from the response and logs debug information if the `debug` flag is set to True.
This function creates a logger object and configures it to handle both
standard output (stdout) and a file if the `to_file` parameter is set to True.
The logger is set to log at the DEBUG level and uses a custom formatter to
include the log level and message.
Parameters:
response (requests.models.Response): The response object from the API request.
debug (bool, optional): A flag indicating whether debug information should be logged. Defaults to False.
to_file (bool): A flag indicating whether to log to a file. If True, logs will be written to a file.
Defaults to False.
Returns:
Any: The JSON data extracted from the response.
logging.Logger: A configured logger object for logging API requests.
"""
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(levelname)s | %(message)s")

if to_file:
now = datetime.now(tz=timezone.utc)
date_time = now.strftime("%Y%m%d_%H%M%S")

log_file = f"{date_time}.log"
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(formatter)
logger.addHandler(stdout_handler)

return logger


def parse_response(
response: Response,
log: Callable,
debug: bool = False,
) -> Any:
"""Parse the response from an API request and return the JSON data.
Parameters:
response (Response): The response object from the API request.
log (Callable): A function or method that logs debug information.
debug (bool): A flag indicating whether debug mode is enabled. Defaults to False.
Returns:
Any: The JSON data from the API response.
"""
data = response.json()

if debug:
logging.debug("REQUEST: %s", response.request.url)
logging.debug("REQUEST_HEADERS: %s", response.request.headers)
logging.debug("REQUEST_CONTENT: %s", response.request.body)

logging.debug("RESPONSE: %s", response.content)
logging.debug("RESP_HEADERS: %s", response.headers)
logging.debug("RESP_CODE: %s", response.status_code)
lgr = log()
lgr.debug("REQUEST: %s", response.request.url)
lgr.debug("REQUEST_HEADERS: %s", response.request.headers)
lgr.debug("REQUEST_CONTENT: %s", response.request.body)

lgr.debug("RESPONSE: %s", response.content)
lgr.debug("RESP_HEADERS: %s", response.headers)
lgr.debug("RESP_CODE: %s", response.status_code)
# Clear logger handlers to prevent making log duplications
logging.getLogger().handlers.clear()

return data

Expand Down
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,17 @@ ignore-overlong-task-comments = true
[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.flake8]
exclude = ["samples/*"]
# TODO: D100 - create docstrings for modules test_client.py and test_version.py
ignore = ['E501', "D100"]
extend-ignore = "W503"
per-file-ignores = [
'__init__.py:F401',
]
max-line-length = 88
count = true

[tool.mypy]
strict = true
# Adapted from this StackOverflow post:
Expand Down
Loading

0 comments on commit ef4f5a0

Please sign in to comment.