-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: permitir personalização do logger #134
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
Open
IvanildoBarauna
wants to merge
2
commits into
main
Choose a base branch
from
codex/refactor-logger-configuration-and-testing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,5 @@ | ||
| from .controller.client_builder import ClientBuilder | ||
| from .models.retainer import Strategies as RetryStrategies | ||
| from .utils.logger import configure_logger | ||
|
|
||
| __all__ = ["ClientBuilder", "RetryStrategies", "configure_logger"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,84 @@ | ||
| """Utilities to manage the library logging configuration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
| from typing import Iterable, Optional | ||
|
|
||
| DEFAULT_LOGGER_NAME = "api-to-dataframe" | ||
|
|
||
|
|
||
| logger = logging.getLogger(DEFAULT_LOGGER_NAME) | ||
| if not any(isinstance(handler, logging.NullHandler) for handler in logger.handlers): | ||
| logger.addHandler(logging.NullHandler()) | ||
|
|
||
|
|
||
| def configure_logger(**kwargs) -> logging.Logger: | ||
| """Configure the default logger or replace it with a custom instance. | ||
| This helper avoids applying a global configuration automatically while | ||
| allowing consumers to fine-tune the logger used by the library. The caller | ||
| can provide an existing :class:`logging.Logger` instance or customize the | ||
| default logger by supplying keyword arguments similar to ``logging.basicConfig``. | ||
| Keyword Args: | ||
| logger (logging.Logger, optional): | ||
| Custom logger instance to use across the library. | ||
| level (int, optional): | ||
| Logging level to apply to the default logger. | ||
| handlers (Iterable[logging.Handler], optional): | ||
| Handlers that will replace the current ones of the default logger. | ||
| formatter (logging.Formatter, optional): | ||
| Formatter applied to every handler in the default logger. | ||
| format (str, optional): | ||
| Format string used to build a :class:`logging.Formatter`. | ||
| datefmt (str, optional): | ||
| Date format passed to :class:`logging.Formatter` when ``format`` is | ||
| provided. | ||
| propagate (bool, optional): | ||
| Whether the default logger should propagate messages to ancestor | ||
| loggers. | ||
| Returns: | ||
| logging.Logger: The configured logger instance. | ||
| Raises: | ||
| TypeError: If the provided ``logger`` argument is not a Logger instance. | ||
| """ | ||
|
|
||
| global logger # pylint: disable=global-statement | ||
|
|
||
| custom_logger: Optional[logging.Logger] = kwargs.pop("logger", None) | ||
| if custom_logger is not None: | ||
| if not isinstance(custom_logger, logging.Logger): | ||
| raise TypeError("The 'logger' argument must be an instance of logging.Logger") | ||
| logger = custom_logger | ||
| return logger | ||
|
|
||
| level: Optional[int] = kwargs.get("level") | ||
| if level is not None: | ||
| logger.setLevel(level) | ||
|
|
||
| propagate: Optional[bool] = kwargs.get("propagate") | ||
| if propagate is not None: | ||
| logger.propagate = propagate | ||
|
|
||
| handlers: Optional[Iterable[logging.Handler]] = kwargs.get("handlers") | ||
| if handlers is not None: | ||
| logger.handlers = list(handlers) | ||
|
|
||
| formatter: Optional[logging.Formatter] = kwargs.get("formatter") | ||
| if formatter is None: | ||
| fmt: Optional[str] = kwargs.get("format") | ||
| datefmt: Optional[str] = kwargs.get("datefmt") | ||
| if fmt is not None: | ||
| formatter = logging.Formatter(fmt=fmt, datefmt=datefmt) | ||
|
|
||
| if formatter is not None: | ||
| for handler in logger.handlers: | ||
| handler.setFormatter(formatter) | ||
|
|
||
| logging.basicConfig( | ||
| encoding="utf-8", | ||
| format="%(asctime)s :: api-to-dataframe[%(levelname)s] :: %(message)s", | ||
| datefmt="%Y-%m-%d %H:%M:%S %Z", | ||
| level=logging.INFO, | ||
| ) | ||
| if not logger.handlers: | ||
| logger.addHandler(logging.NullHandler()) | ||
|
|
||
| # Initialize traditional logger | ||
| logger = logging.getLogger("api-to-dataframe") | ||
| return logger | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,59 @@ | ||
| import io | ||
| import logging | ||
|
|
||
| import pytest | ||
| from api_to_dataframe.utils.logger import logger | ||
|
|
||
|
|
||
| def test_logger_exists(): | ||
| """Test logger is properly initialized.""" | ||
| assert logger.name == "api-to-dataframe" | ||
| # Verificar apenas que é uma instância de logger, sem verificar propriedades específicas | ||
| assert isinstance(logger, logging.Logger) | ||
|
|
||
|
|
||
| def test_logger_can_log(): | ||
| """Test logger can log messages without errors.""" | ||
| # Tentativa de logging não deve lançar exceções | ||
| try: | ||
| logger.info("Test message") | ||
| logger.warning("Test warning") | ||
| logger.error("Test error") | ||
| # Se chegamos aqui, não houve exceções | ||
| assert True | ||
| except Exception as e: | ||
| assert False, f"Logger raised an exception: {e}" | ||
|
|
||
| from api_to_dataframe.utils import logger as logger_module | ||
| from api_to_dataframe.utils.logger import DEFAULT_LOGGER_NAME, configure_logger, logger | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def restore_logger_state(): | ||
| """Restore the original logger configuration after each test run.""" | ||
|
|
||
| original_logger = logger_module.logger | ||
| original_handlers = list(original_logger.handlers) | ||
| original_level = original_logger.level | ||
| original_propagate = original_logger.propagate | ||
|
|
||
| yield | ||
|
|
||
| configure_logger(logger=original_logger) | ||
| original_logger.handlers = original_handlers | ||
| original_logger.setLevel(original_level) | ||
| original_logger.propagate = original_propagate | ||
|
|
||
|
|
||
| def test_default_logger_is_exposed(): | ||
| """Ensure the module exposes the default logger with a NullHandler.""" | ||
|
|
||
| assert logger.name == DEFAULT_LOGGER_NAME | ||
| assert any(isinstance(handler, logging.NullHandler) for handler in logger.handlers) | ||
|
|
||
|
|
||
| def test_configure_logger_accepts_custom_instance(): | ||
| """Ensure configure_logger can swap the default logger instance.""" | ||
|
|
||
| custom_logger = logging.getLogger("api-to-dataframe-custom") | ||
| configured_logger = configure_logger(logger=custom_logger) | ||
|
|
||
| assert configured_logger is custom_logger | ||
|
|
||
|
|
||
| def test_configure_logger_applies_custom_format(): | ||
| """Ensure configure_logger applies the provided formatter to handlers.""" | ||
|
|
||
| stream = io.StringIO() | ||
| handler = logging.StreamHandler(stream) | ||
| format_str = "%(levelname)s -- %(message)s" | ||
|
|
||
| configured_logger = configure_logger( | ||
| handlers=[handler], | ||
| level=logging.INFO, | ||
| format=format_str, | ||
| ) | ||
| configured_logger.info("custom message") | ||
| handler.flush() | ||
|
|
||
| log_output = stream.getvalue().strip() | ||
| assert log_output == "INFO -- custom message" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
configure_loggerconfigures a logger named"api-to-dataframe", but all logging inside the package now useslogging.getLogger(__name__)(e.g.ClientBuilder,GetData,retry_strategies), which produces loggers in theapi_to_dataframe.*hierarchy. Callingconfigure_logger(...)as documented will therefore not influence any of the library’s emitted log messages unless the caller manually injects the returned logger everywhere. This makes the advertised customization hook ineffective and leaves users without a simple way to enable logging. Consider basingDEFAULT_LOGGER_NAMEon the package namespace (e.g."api_to_dataframe") or otherwise wiringconfigure_loggerto the loggers actually used by the code.Useful? React with 👍 / 👎.