pyramid-log 2.0.0a1
Include pyramid request attributes in your log messages
License: BSD
Requires Python: >=3.6
Keywords: pyramid logging

##################################
Request Attributes in Log Messages
##################################

|version| |py_versions| |py_implementation| |license| |build status|

What It Does
============

The `pyramid_log`_ distribution includes a Python `logging formatter`_
which makes Pyramid_ request attributes available for use in its
format string.  Specifically, ``pyramid_log.Formatter`` is special in
the following ways:

- It sets a ``.request`` attribute on the log record (if one doesn’t
  already exist.)

- It supports dotted attribute access in its format string. For
  example, ``"%(request.method)s"`` and even
  ``"%(request.matched_route.name)s"`` will work in the format string.

- There is a syntax for explicitly specifying fallback values.  For
  example, a format string of ``"%(request.method|<no request>)s"``
  will format to ``"<no request>"`` if there is no current request (or
  if the current request has no ``method`` attribute.)

The pyramid request has many attributes which can be useful when included
in the logs of a web app.  These include, but are not limited to:

- ``request.method``
- ``request.url`` (or ``request.path``, ``request.path_qs``, etc…)
- ``request.unauthenticated_userid``
- ``request.client_addr``
- ``request.GET`` (or ``request.POST`` or ``request.params``)
- ``request.matched_route.name``, ``request.view_name``

See the `Pyramid documentation <pyramid.request_>`_ for a more
complete list of available request attributes.

.. _pyramid_log: https://pypi.python.org/pypi/pyramid_log/
.. _logging formatter:
   https://docs.python.org/3/library/logging.html#formatter-objects
.. _pyramid: http://docs.pylonsproject.org/projects/pyramid/en/latest/
.. _pyramid.request:
   http://docs.pylonsproject.org/projects/pyramid/en/latest/api/request.html


Installation
============

The distribution may be downloaded from pypi_, but it may be easier to
install using pip_::

    pip install pyramid-log

It has been tested on python 2.7, 3.4–3.6 and pypy.

Development happens at https://github.com/dairiki/pyramid_log/.

.. _pypi: `logging formatter`_
.. _pip: https://pip.pypa.io/en/latest/


Configuration
=============

Configuring Logging in a File
-----------------------------

If you configure logging in your application configuration (or some
other) file you can do something like::

    [loggers]
    key = root

    [handlers]
    keys = console

    [formatters]
    keys = pyramid

    [logger_root]
    level = INFO
    handlers = console

    [handler_console]
    class = StreamHandler
    args = (sys.stderr,)
    level = NOTSET
    formatter = pyramid

    [formatter_pyramid]
    # NB: Here is the interesting part!
    class = pyramid_log.Formatter
    format = %(asctime)s %(request.method|no request)s %(request.path_qs|)s
             %(levelname)-5.5s [%(name)s] %(message)s

This will result in your log messages looking something like::

    2014-10-01 17:55:02,001 GET /path?arg=foo
    WARNI [myapp.views] This is some log message!

Refer to Pyramid’s `chapter on logging`_ and the documentation for the
Python logging_ module’s `configuration file format`_ for more details
on how this works.

.. _chapter on logging:
   http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
.. _logging:
   https://docs.python.org/3/library/logging.html
.. _configuration file format:
   https://docs.python.org/3/library/logging.config.html#logging-config-fileformat


Imperative Configuration
------------------------

You can of course configure logging imperatively.  For example, with::

    import logging
    from pyramid_log import Formatter

    fmt = Formatter(
        '%(asctime)s %(request.client_addr|-)s'
        ' %(request.method|-)s %(request.path_qs|-)s: %(message)s')

    logging.basicConfig()
    root_logger = logging.getLogger()
    for handler in root_logger.handlers:
        handler.setFormatter(fmt)

Then, a view can log a message like so::

    log = logging.getLogger(__name__)

    @view_config(name='persimmon')
    def persimmon_view(request):
        log.warning("%s was called!", request.view_name)

Which will yield a log message like::

    2014-10-01 17:55:02,001 192.168.1.1 GET /persimmon: persimmon was called


Further Details
===============

Accessing Dict-like Values
--------------------------

The dot notation can be used to access not only instance attributes,
but also to access items in ``dict``-like values.  Attribute access is
tried first; if there is no attribute of the given name, then the
instances ``__getitem__`` method is tried.  For example,
``"%(request.matchdict.id)s"`` will get at
``request.matchdict['id']``.

Numeric Fallback
----------------

Explicit fallback values are always interpreted as strings, however,
if the fallback is used in a numeric context, an attempt will be made
at conversion to the requested type.  For example, if there is no
request, ``"%+(request.status_code|555)d"`` will format to ``"+555"``.

If the fallback string can not be converted to a numeric value, then
``0`` (zero) is used in integer contexts and NaN_ is used in ``float``
contexts.

.. _NaN: https://en.wikipedia.org/wiki/NaN

Default Fallback Values
-----------------------

If no fallback value is explicitly specified, then a default fallback
value will be used if the requested attribute does not exist.  The
missing attribute name is included in the default fallback value.  For
example ``"%(request.method)s"`` will produce ``"<?request.method?>"``
if there is no current request.


See Also
========

The `pyramid_logging`_ distribution provides similar functionality.

.. _pyramid_logging: https://pypi.python.org/pypi/pyramid_logging


Author
======

Jeff Dairiki <dairiki@dairiki.org>


.. ==== Badges ====

.. |build status| image::
    https://github.com/dairiki/pyramid_log/actions/workflows/tests.yml/badge.svg?branch=master
    :target: https://github.com/dairiki/pyramid_log/actions/workflows/tests.yml
.. |downloads| image::
    https://img.shields.io/pypi/dm/pyramid_log.svg
    :target: https://pypi.python.org/pypi/pyramid_log/
    :alt: Downloads
.. |version| image::
    https://img.shields.io/pypi/v/pyramid_log.svg
    :target: https://pypi.python.org/pypi/pyramid_log/
    :alt: Latest Version
.. |py_versions| image::
    https://img.shields.io/pypi/pyversions/pyramid_log.svg
    :target: https://pypi.python.org/pypi/pyramid_log/
    :alt: Supported Python versions
.. |py_implementation| image::
    https://img.shields.io/pypi/implementation/pyramid_log.svg
    :target: https://pypi.python.org/pypi/pyramid_log/
    :alt: Supported Python versions
.. |license| image::
    https://img.shields.io/pypi/l/pyramid_log.svg
    :target: https://github.com/dairiki/pyramid_log/blob/master/LICENSE
    :alt: License
.. |dev_status| image::
    https://img.shields.io/pypi/status/pyramid_log.svg
    :target: https://pypi.python.org/pypi/pyramid_log/
    :alt: Development Status

History
=======

Release 2.0.0a1 (2021-12-07)
----------------------------

This release drops support for python 2.7.

API
^^^

- Raise ``ValueError`` if ``pyramid_log.Formatter`` is passed a value of
  other than ``'%'`` for ``style`` parameter.

- Issue a ``UserWarning`` if ``pyramid_log.Formatter`` is explicitly passed
  a ``validate=True``.


Release 1.0.1 (2021-12-07)
--------------------------

A bit of code refactoring and cleanup in preparation for dropping
python 2 support.

No API or functional changes.

Release 1.0 (2021-12-05)
------------------------

This release adds support for python>=3.8 and pyramid>=2.

The 1.x releases will be the last to support running under python 2.7.

Compatibility
^^^^^^^^^^^^^

- Python >= 3.8: ``logger.Formatter`` requires the ``validate=False``
  argument, otherwise it forbids ``'.'`` in %-style format strings.
- Pyramid >= 2.0: provide our own replacement for ``pyramid.compat``
  which no longer exists

Testing
^^^^^^^

- Test under python 3.7–3.10 and pypy3.
- Stop testing under python 3.4 and 3.5.
- Test with Pyramid 1.*
- Convert CI tests from Travis to github workflow

Packaging
^^^^^^^^^

- The packaging has been PEP517-ized.

Release 0.2.1 (2017-12-17)
--------------------------

This release officially drops support for python 2.6, 3.2, 3.3 (and
therefore pypy3) and adds support for python 3.5 and 3.6.

Other than changes in test configuration, there are no substantive
changes from `0.2`.

Release 0.2 (2014-10-09)
------------------------

Features
^^^^^^^^

Better fallback values.
"""""""""""""""""""""""

- Now, by default, if an attribute is missing (which can happen, e.g.,
  for ``%(request.method)s`` is there is no current request) it is
  rendered as ``<?``\ *attribute-name*\ ``?>``
  (e.g. ``"<?request.method?>"``.)

- There is now a syntax for explicitly specifying fallback values.  E.g.
  ``"%(request.method|(no-request))"`` which will format to ``(no request)``,
  if there is no current request (or if the current request does not have
  a ``method`` attribute.)

Dict-like access to values
""""""""""""""""""""""""""

- When looking up a dotted name, if an attribute can not be found,
  ``dict``-style (``__getitem__``) lookup will be attempted.
  E.g. ``"%(request.matchdict.arg)"`` will get at
  ``request.matchdict['arg']``.

Release 0.1.1 (2014-10-02)
--------------------------

Bugs Fixed
^^^^^^^^^^

- If an exception is thrown by a request property, render it as ``None``.

- Disable logging during log formatting to prevent recursion if a request
  property generates a log message.

Release 0.1 (2014-10-02)
------------------------

- Initial release