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

Publicly expose the code turning a dict filter to a filter function? #1123

Open
afeblot opened this issue Apr 7, 2024 · 3 comments
Open

Comments

@afeblot
Copy link

afeblot commented Apr 7, 2024

I used to have a handler with a dict filter:

filteredModules = {
    'docker': 'WARNING',
    'urllib3': 'WARNING',
    'azure': 'WARNING',
    'kafka': 'WARNING',
}

For some reason, I've had to turn this filter into a function to perform some pre-checks which would maybe drop some records, before dealing with the dict part.

But I found no way to easily transform my dict into a function I could call. That part of the code is embedded in your Logger.add() function.

I had to monkey patch the filter to get it done, which is brittle and ugly:

logger.configure(
    handlers=[
        {
            'filter': filteredModules,
            ...
        },
        {
            ...
        }
    ]
    ...
)

currentFilter = logger._core.handlers[1]._filter # pylint: disable=protected-access
def myNewFilter(record):
    if ...my added checks...:
        return False
    return currentFilter(record)
logger._core.handlers[1]._filter = myNewFilter # pylint: disable=protected-access

I would have been happy to do something like this instead:

moduleFilter = loguru.createFilterFunctionFromDict(filteredModules)

def myNewFilter(record):
    if ...my added checks...:
        return False
    return moduleFilter(record)

logger.configure(
    handlers=[
        {
            'filter': myNewFilter,
            ...
        },
        {
            ...
        }
    ]
    ...
)

Or would you currently see a cleaner way to achieve this?

@Delgan
Copy link
Owner

Delgan commented Apr 8, 2024

Hi @afeblot.

To be honest, I prefer not to expose such a function publicly. I think that for more advanced use cases like yours, it's advisable to implement a custom function that fits the needs exactly.

The "dict filter" isn't vert complicated and is implemented here:

def filter_by_level(record, level_per_module):
name = record["name"]
while True:
level = level_per_module.get(name, None)
if level is False:
return False
if level is not None:
return record["level"].no >= level
if not name:
return True
index = name.rfind(".")
name = name[:index] if index != -1 else ""

You could import it instead of patching the handler filter, but I'd say it's best to just copy/paste what is required and re-use it in your own function with the pre-checks you need.

@afeblot
Copy link
Author

afeblot commented Apr 9, 2024

Hi @Delgan,
Thanks for your answer.
I can indeed use this function, but I was hoping for a clean way to avoid re-coding this part which provides all the required safety nets around the use of a dictionary filter:

loguru/loguru/_logger.py

Lines 879 to 915 in 2ba868c

elif isinstance(filter, dict):
level_per_module = {}
for module, level_ in filter.items():
if module is not None and not isinstance(module, str):
raise TypeError(
"The filter dict contains an invalid module, "
"it should be a string (or None), not: '%s'" % type(module).__name__
)
if level_ is False:
levelno_ = False
elif level_ is True:
levelno_ = 0
elif isinstance(level_, str):
try:
levelno_ = self.level(level_).no
except ValueError:
raise ValueError(
"The filter dict contains a module '%s' associated to a level name "
"which does not exist: '%s'" % (module, level_)
) from None
elif isinstance(level_, int):
levelno_ = level_
else:
raise TypeError(
"The filter dict contains a module '%s' associated to an invalid level, "
"it should be an integer, a string or a boolean, not: '%s'"
% (module, type(level_).__name__)
)
if levelno_ < 0:
raise ValueError(
"The filter dict contains a module '%s' associated to an invalid level, "
"it should be a positive integer, not: '%d'" % (module, levelno_)
)
level_per_module[module] = levelno_
filter_func = functools.partial(
_filters.filter_by_level, level_per_module=level_per_module
)

@Delgan
Copy link
Owner

Delgan commented May 9, 2024

Sorry @afeblot but I don't really plan to expose such function publicly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants