# Getting Started

These steps will show how to integrate the middleware to your awesome application.

## Installation

Install the library

    pip install django-structlog

Add app

    INSTALLED_APP = [
        # ...
        "django_structlog",
        # ...
    ]

Add middleware

    MIDDLEWARE = [
        # ...
        "django_structlog.middlewares.RequestMiddleware",
    ]

Add appropriate structlog configuration to your `settings.py`

    import structlog

    LOGGING = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "json_formatter": {
                "()": structlog.stdlib.ProcessorFormatter,
                "processor": structlog.processors.JSONRenderer(),
            },
            "plain_console": {
                "()": structlog.stdlib.ProcessorFormatter,
                "processor": structlog.dev.ConsoleRenderer(),
            },
            "key_value": {
                "()": structlog.stdlib.ProcessorFormatter,
                "processor": structlog.processors.KeyValueRenderer(key_order=['timestamp', 'level', 'event', 'logger']),
            },
        },
        "handlers": {
            # Important notes regarding handlers.
            #
            # 1. Make sure you use handlers adapted for your project.
            # These handlers configurations are only examples for this library.
            # See python's logging.handlers: https://docs.python.org/3/library/logging.handlers.html
            #
            # 2. You might also want to use different logging configurations depending of the environment.
            # Different files (local.py, tests.py, production.py, ci.py, etc.) or only conditions.
            # See https://docs.djangoproject.com/en/dev/topics/settings/#designating-the-settings
            "console": {
                "class": "logging.StreamHandler",
                "formatter": "plain_console",
            },
            "json_file": {
                "class": "logging.handlers.WatchedFileHandler",
                "filename": "logs/json.log",
                "formatter": "json_formatter",
            },
            "flat_line_file": {
                "class": "logging.handlers.WatchedFileHandler",
                "filename": "logs/flat_line.log",
                "formatter": "key_value",
            },
        },
        "loggers": {
            "django_structlog": {
                "handlers": ["console", "flat_line_file", "json_file"],
                "level": "INFO",
            },
            # Make sure to replace the following logger's name for yours
            "django_structlog_demo_project": {
                "handlers": ["console", "flat_line_file", "json_file"],
                "level": "INFO",
            },
        }
    }

    structlog.configure(
        processors=[
            structlog.contextvars.merge_contextvars,
            structlog.stdlib.filter_by_level,
            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.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        cache_logger_on_first_use=True,
    )

Start logging with `structlog` instead of `logging`.

    import structlog
    logger = structlog.get_logger(__name__)

## Extending Request Log Metadata

By default only a `request_id` and the `user_id` are bound from the request but pertinent log metadata may vary from a project to another.

If you need to add more metadata from the request you can implement a convenient signal receiver to bind them. You can also override existing bound metadata the same way.

    from django.contrib.sites.shortcuts import get_current_site
    from django.dispatch import receiver
    from django_structlog import signals
    import structlog

    @receiver(signals.bind_extra_request_metadata)
    def bind_domain(request, logger, **kwargs):
        current_site = get_current_site(request)
        structlog.contextvars.bind_contextvars(domain=current_site.domain)

## Standard Loggers

It is also possible to log using standard python logger.

In your formatters, add the `foreign_pre_chain` section, and then add `structlog.contextvars.merge_contextvars`:

    LOGGING = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "json_formatter": {
                "()": structlog.stdlib.ProcessorFormatter,
                "processor": structlog.processors.JSONRenderer(),
                # Add this section:
                "foreign_pre_chain": [
                    structlog.contextvars.merge_contextvars, # <---- add this
                    # customize the rest as you need
                    structlog.processors.TimeStamper(fmt="iso"),
                    structlog.stdlib.add_logger_name,
                    structlog.stdlib.add_log_level,
                    structlog.stdlib.PositionalArgumentsFormatter(),
                ],
            },
        },
        ...
     }