-
Notifications
You must be signed in to change notification settings - Fork 711
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
Example for Django #977
Comments
Hi @TheRealBecks. I'm not familiar with logging in Django, but I hope we can come up with a recipe that works. Regarding your concerns:
Is there any difficulty in that? What's stopping you from implementing middleware as a class?
Kind of same question. The guide uses
You mention the use of a middleware, but given that Django uses the standard
As opposed to the
I guess you need a custom sink then?
Is this the default behavior (Celery automatically inherits Django logging settings)? |
@Delgan I will be back in a few weeks. Afterwards I will check and test the Django default logging configuration, maybe we can start from that point on. |
@Delgan After hours and hours of work I finally got some progress thanks to your
At startup code for an app can be loaded in the But there are still some wrongly formatted logging messages:
It's correct that there are As we can see the log message after the But there are also these ones that are 'Django formatted':
I also checked the [Django default logging settings|https://docs.djangoproject.com/en/4.2/ref/logging/#default-logging-configuration] and I found the setting for Do you know why the message for |
When you call According to the docs you linked:
That means message logged by the I do not know why Django behaves this way. You might need to remove or replace the handler they're installing on the |
Thanks, so I'm already on the right track! 😄 I also tried to reset the
-> Nothing will be logged anymore. Maybe that's the way to go and I need to configure Loguru from there on?
Do you think solution 1 could be the way to go? Because starting from scratch sounds good? |
From what I read in the docs, the
It sounds like it prevents the messages to be propagated to the root logger, which implies the |
I came across this thread while searching for a Loguru example in Django to compare with Structlog. Sharing my Django and Structlog setup to inspire a Loguru+Django setup hopefully: logging.py:import logging
from typing import Any
import ddtrace
import structlog
def tracer_injection(_: logging.Logger, __: str, event_dict: structlog.types.EventDict) -> structlog.types.EventDict:
span = ddtrace.tracer.current_span()
trace_id, span_id = (span.trace_id, span.span_id) if span else (None, None)
event_dict['dd.trace_id'] = str(trace_id or 0)
event_dict['dd.span_id'] = str(span_id or 0)
event_dict['dd.env'] = ddtrace.config.env or ''
event_dict['dd.service'] = ddtrace.config.service or ''
event_dict['dd.version'] = ddtrace.config.version or ''
return event_dict
# To enable standard library logs to be formatted via structlog, we add this
# `foreign_pre_chain` to both formatters.
foreign_pre_chain: list[structlog.types.Processor] = [
structlog.contextvars.merge_contextvars,
structlog.processors.TimeStamper(fmt='iso'),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
]
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
*foreign_pre_chain,
structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
],
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Configure all logs to be handled by structlog `ProcessorFormatter` and
# rendered either as pretty colored console lines or as single JSON lines with structured tracebacks
def get_logger_config(json_logs: bool = False, log_level: str = 'INFO') -> dict[str, Any]:
"""Configure python stdlib's logging"""
base_config: dict[str, Any] = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': structlog.stdlib.ProcessorFormatter,
'processor': structlog.processors.JSONRenderer(sort_keys=True),
'foreign_pre_chain': [*foreign_pre_chain, structlog.processors.dict_tracebacks, tracer_injection],
},
'console': {
'()': structlog.stdlib.ProcessorFormatter,
'processor': structlog.dev.ConsoleRenderer(),
'foreign_pre_chain': foreign_pre_chain,
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'json' if json_logs else 'console',
},
},
'loggers': {},
}
logger_config_map = {
'': {'level': log_level, 'propagate': False},
'src': {'level': log_level, 'propagate': True},
'django': {'level': 'WARNING', 'propagate': True},
'django.request': {'level': 'ERROR', 'propagate': False},
'django.security': {'level': 'WARNING', 'propagate': False},
'django_oso.oso': {'level': 'ERROR', 'propagate': False},
'requests': {'level': 'WARNING', 'propagate': False},
'celery': {'level': 'WARNING', 'propagate': False},
'urllib3': {'level': 'WARNING', 'propagate': False},
'asyncio': {'level': 'WARNING', 'propagate': False},
'redis': {'level': 'WARNING', 'propagate': False},
'uvicorn': {'level': 'WARNING', 'propagate': False},
'gunicorn': {'level': 'INFO', 'propagate': False},
'uvicorn.access': {'level': 'INFO', 'propagate': False},
'gunicorn.access': {'level': 'INFO', 'propagate': False},
'uvicorn.error': {'level': 'ERROR', 'propagate': False},
'gunicorn.error': {'level': 'ERROR', 'propagate': False},
'ddtrace': {'level': 'ERROR', 'propagate': False},
'botocore': {'level': 'ERROR', 'propagate': False},
'boto3': {'level': 'ERROR', 'propagate': False},
'opensearch': {'level': 'ERROR', 'propagate': False},
'numexpr': {'level': 'ERROR', 'propagate': False},
# Add other loggers as needed
}
for logger_name, config in logger_config_map.items():
base_config['loggers'][logger_name] = {
'handlers': ['console'],
'level': config['level'],
'propagate': config.get('propagate', False),
}
return base_config settings.py
gunicorn_config.py
|
@adilnaimi Thanks for your input! Unfortunately I'm still very busy, but still interested to get Django + Loguru running. I already had some progress and I would like to get it running as a package that can be installed via Pypi. But there's still a lot of work to be done, so if you or someone else is interested in joining the forces feel free to contact me! 😃 |
I just ran into this issue and found a reasonable solution for me: In from proj.logging import configure_logging
LOGGING_CONFIG=None
configure_logging() I import the logging.py after settings LOGGING_CONFIG to None - don't know if this is necessary.
import inspect
import logging
import sys
from loguru import logger
class InterceptHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
# Get corresponding Loguru level if it exists.
try:
level: str | int = logger.level(record.levelname).name
except ValueError:
level = record.levelno
# Find caller from where originated the logged message.
frame, depth = inspect.currentframe(), 0
while frame:
filename = frame.f_code.co_filename
is_logging = filename == logging.__file__
is_frozen = "importlib" in filename and "_bootstrap" in filename
if depth > 0 and not (is_logging or is_frozen):
break
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
def log_filter(record):
if record['name'].startswith("django."):
return record['level'].no >= logger.level("INFO").no
return record['level'].no >= logger.level("DEBUG").no
def configure_logging():
# Remove default logger configuration
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
logger.remove()
logger.add(sys.stderr, filter=log_filter) The django loggers are very verbose on the DEBUG Loglevel, so I use the |
I saw that some people already asked for a Django example, but nobody provided a full example for Django. I also want to use Loguru for my Django 4.2 project, because I think it's a very good and flexible logging mechanism to log several packages in a huge project.
I also asked in the Django forum for help, but it looks like that I won't get any further with a solution.
This is my starting point:
I already found out that I can use Loguru as middleware in Django: https://docs.djangoproject.com/en/4.2/topics/http/middleware/
I found this useful tutorial to get an idea how that integration could work: https://betterstack.com/community/guides/logging/loguru/#creating-a-request-logging-middleware
But:
info
logging, but all other levels (debug
,warning
, ...) are missingAnd:
Next steps afterwards:
Do you think it makes sense what I'm writing here? 😃 If yes is it possible to get help from your side? If yes then I would like provide a full example for your documentation as PR if you would like to get that :)
The text was updated successfully, but these errors were encountered: