{ "info": { "author": "Delgan", "author_email": "delgan.py@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: System :: Logging" ], "description": ".. image:: https://raw.githubusercontent.com/Delgan/loguru/master/docs/_static/img/logo.png\n :target: https://github.com/Delgan/loguru\n :align: center\n :alt: Loguru logo\n\n.. image:: https://raw.githubusercontent.com/Delgan/loguru/master/docs/_static/img/demo.gif\n :target: https://github.com/Delgan/loguru\n :align: center\n :alt: Loguru demo\n\n=========\n\n.. image:: https://img.shields.io/pypi/v/loguru.svg\n :target: https://pypi.python.org/pypi/loguru\n :alt: Pypi version\n\n.. image:: https://img.shields.io/badge/python-3.5%2B%20%7C%20PyPy-blue.svg\n :target: https://github.com/Delgan/loguru/blob/master/.travis.yml\n :alt: Python versions\n\n.. image:: https://img.shields.io/readthedocs/loguru.svg\n :target: https://loguru.readthedocs.io/en/stable/index.html\n :alt: Docs\n\n.. image:: https://img.shields.io/travis/Delgan/loguru.svg\n :target: https://travis-ci.org/Delgan/loguru\n :alt: Build status\n\n.. image:: https://img.shields.io/codecov/c/github/delgan/loguru/master.svg\n :target: https://codecov.io/gh/delgan/loguru/branch/master\n :alt: Coverage\n\n.. image:: https://img.shields.io/codacy/grade/4d97edb1bb734a0d9a684a700a84f555.svg\n :target: https://www.codacy.com/app/delgan-py/loguru/dashboard\n :alt: Code quality\n\n.. image:: https://img.shields.io/github/license/delgan/loguru.svg\n :target: https://github.com/Delgan/loguru/blob/master/LICENSE\n :alt: License\n\n**Loguru** is a library which aims to bring enjoyable logging in Python.\n\nDid you ever feel lazy about configuring a logger and used ``print()`` instead?... I did, yet logging is fundamental to every application and eases the process of debugging. Using **Loguru** you have no excuse not to use logging from the start, this is as simple as ``from loguru import logger``.\n\nAlso, this library is intended to make Python logging less painful by adding a bunch of useful functionalities that solve caveats of the standard loggers. Using logs in your application should be an automatism, **Loguru** tries to make it both pleasant and powerful.\n\n\n.. end-of-readme-intro\n\nInstallation\n------------\n\n::\n\n pip install loguru\n\n\nFeatures\n--------\n\n* `Ready to use out of the box without boilerplate`_\n* `No Handler, no Formatter, no Filter: one function to rule them all`_\n* `Easier file logging with rotation / retention / compression`_\n* `Modern string formatting using braces style`_\n* `Exceptions catching within threads or main`_\n* `Pretty logging with colors`_\n* `Asynchronous, Thread-safe, Multiprocess-safe`_\n* `Fully descriptive exceptions`_\n* `Structured logging as needed`_\n* `Lazy evaluation of expensive functions`_\n* `Customizable levels`_\n* `Better datetime handling`_\n* `Suitable for scripts and libraries`_\n* `Entirely compatible with standard logging`_\n* `Personalizable defaults through environment variables`_\n* `Convenient parser`_\n* `Exhaustive notifier`_\n* `10x faster than built-in logging`_\n\nTake the tour\n-------------\n\n.. |logger| replace:: ``logger``\n.. _logger: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger\n\n.. |add| replace:: ``add()``\n.. _add: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add\n\n.. |remove| replace:: ``remove()``\n.. _remove: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.remove\n\n.. |catch| replace:: ``catch()``\n.. _catch: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.catch\n\n.. |bind| replace:: ``bind()``\n.. _bind: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.bind\n\n.. |patch| replace:: ``patch()``\n.. _patch: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.patch\n\n.. |opt| replace:: ``opt()``\n.. _opt: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.opt\n\n.. |trace| replace:: ``trace()``\n.. _trace: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.trace\n\n.. |success| replace:: ``success()``\n.. _success: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.success\n\n.. |level| replace:: ``level()``\n.. _level: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.level\n\n.. |configure| replace:: ``configure()``\n.. _configure: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.configure\n\n.. |disable| replace:: ``disable()``\n.. _disable: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.disable\n\n.. |enable| replace:: ``enable()``\n.. _enable: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.enable\n\n.. |parse| replace:: ``parse()``\n.. _parse: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.parse\n\n.. _sinks: https://loguru.readthedocs.io/en/stable/api/logger.html#sink\n.. _record dict: https://loguru.readthedocs.io/en/stable/api/logger.html#record\n.. _log messages: https://loguru.readthedocs.io/en/stable/api/logger.html#message\n.. _easily configurable: https://loguru.readthedocs.io/en/stable/api/logger.html#file\n.. _markup tags: https://loguru.readthedocs.io/en/stable/api/logger.html#color\n.. _fixes it: https://loguru.readthedocs.io/en/stable/api/logger.html#time\n.. _No problem: https://loguru.readthedocs.io/en/stable/api/logger.html#env\n.. _logging levels: https://loguru.readthedocs.io/en/stable/api/logger.html#levels\n\n.. |better_exceptions| replace:: ``better_exceptions``\n.. _better_exceptions: https://github.com/Qix-/better-exceptions\n\n.. |notifiers| replace:: ``notifiers``\n.. _notifiers: https://github.com/notifiers/notifiers\n\nReady to use out of the box without boilerplate\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe main concept of `Loguru` is that **there is one and only one** |logger|_.\n\nFor convenience, it is pre-configured and outputs to ``stderr`` to begin with (but that's entirely configurable).\n\n::\n\n from loguru import logger\n\n logger.debug(\"That's it, beautiful and simple logging!\")\n\nThe |logger|_ is just an interface which dispatches log messages to configured handlers. Simple, right?\n\n\nNo Handler, no Formatter, no Filter: one function to rule them all\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow to add a handler? How to set up logs formatting? How to filter messages? How to set level?\n\nOne answer: the |add|_ function.\n\n::\n\n logger.add(sys.stderr, format=\"{time} {level} {message}\", filter=\"my_module\", level=\"INFO\")\n\nThis function should be used to register sinks_ which are responsible for managing `log messages`_ contextualized with a `record dict`_. A sink can take many forms: a simple function, a string path, a file-like object, a built-in Handler or a custom class.\n\nNote that you may also |remove|_ previously added handlers, this is particularly useful if you want to supersede the default one.\n\n\nEasier file logging with rotation / retention / compression\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIf you want to send logged messages to a file, you just have to use a string path as the sink. It can be automatically timed too for convenience::\n\n logger.add(\"file_{time}.log\")\n\nIt is also `easily configurable`_ if you need rotating logger, if you want to remove older logs, or if you wish to compress your files at closure.\n\n::\n\n logger.add(\"file_1.log\", rotation=\"500 MB\") # Automatically rotate too big file\n logger.add(\"file_2.log\", rotation=\"12:00\") # New file is created each day at noon\n logger.add(\"file_3.log\", rotation=\"1 week\") # Once the file is too old, it's rotated\n\n logger.add(\"file_X.log\", retention=\"10 days\") # Cleanup after some time\n\n logger.add(\"file_Y.log\", compression=\"zip\") # Save some loved space\n\n\nModern string formatting using braces style\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n`Loguru` favors the much more elegant and powerful ``{}`` formatting over ``%``, logging functions are actually equivalent to ``str.format()``.\n\n::\n\n logger.info(\"If you're using Python {}, prefer {feature} of course!\", 3.6, feature=\"f-strings\")\n\n\nExceptions catching within threads or main\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHave you ever seen your program crashing unexpectedly without seeing anything in the log file? Did you ever noticed that exceptions occurring in threads were not logged? This can be solved using the |catch|_ decorator / context manager which ensures that any error is correctly propagated to the |logger|_.\n\n::\n\n @logger.catch\n def my_function(x, y, z):\n # An error? It's caught anyway!\n return 1 / (x + y + z)\n\n\nPretty logging with colors\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n`Loguru` automatically adds colors to your logs if your terminal is compatible. You can define your favorite style by using `markup tags`_ in the sink format.\n\n::\n\n logger.add(sys.stdout, colorize=True, format=\"{time} {message}\")\n\n\nAsynchronous, Thread-safe, Multiprocess-safe\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAll sinks added to the |logger|_ are thread-safe by default. They are not multiprocess-safe, but you can ``enqueue`` the messages to ensure logs integrity. This same argument can also be used if you want async logging.\n\n::\n\n logger.add(\"somefile.log\", enqueue=True)\n\n\nFully descriptive exceptions\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nLogging exceptions that occur in your code is important to track bugs, but it's quite useless if you don't know why it failed. `Loguru` helps you identify problems by allowing the entire stack trace to be displayed, including values of variables (thanks |better_exceptions|_ for this!).\n\nThe code::\n\n logger.add(\"output.log\", backtrace=True, diagnose=True) # Set 'False' to not leak sensitive data in prod\n\n def func(a, b):\n return a / b\n\n def nested(c):\n try:\n func(5, c)\n except ZeroDivisionError:\n logger.exception(\"What?!\")\n\n nested(0)\n\nWould result in:\n\n.. code-block::\n\n 2018-07-17 01:38:43.975 | ERROR | __main__:nested:10 - What?!\n Traceback (most recent call last):\n\n File \"test.py\", line 12, in \n nested(0)\n \u2514 \n\n > File \"test.py\", line 8, in nested\n func(5, c)\n \u2502 \u2514 0\n \u2514 \n\n File \"test.py\", line 4, in func\n return a / b\n \u2502 \u2514 0\n \u2514 5\n\n ZeroDivisionError: division by zero\n\n\nStructured logging as needed\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWant your logs to be serialized for easier parsing or to pass them around? Using the ``serialize`` argument, each log message will be converted to a JSON string before being sent to the configured sink.\n\n::\n\n logger.add(custom_sink_function, serialize=True)\n\nUsing |bind|_ you can contextualize your logger messages by modifying the `extra` record attribute.\n\n::\n\n logger.add(\"file.log\", format=\"{extra[ip]} {extra[user]} {message}\")\n logger_ctx = logger.bind(ip=\"192.168.0.1\", user=\"someone\")\n logger_ctx.info(\"Contextualize your logger easily\")\n logger_ctx.bind(user=\"someoneelse\").info(\"Inline binding of extra attribute\")\n\nYou can also have more fine-grained control over your logs by combining |bind|_ and ``filter``:\n\n::\n\n logger.add(\"special.log\", filter=lambda record: \"special\" in record[\"extra\"])\n logger.debug(\"This message is not logged to the file\")\n logger.bind(special=True).info(\"This message, though, is logged to the file!\")\n\nFinally, the |patch|_ method allows dynamic values to be attached to the record dict of each new message:\n\n::\n\n logger.add(sys.stderr, format=\"{extra[utc]} {message}\")\n logger = logger.patch(lambda record: record[\"extra\"].update(utc=datetime.utcnow())\n\n\nLazy evaluation of expensive functions\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSometime you would like to log verbose information without performance penalty in production, you can use the |opt|_ method to achieve this.\n\n::\n\n logger.opt(lazy=True).debug(\"If sink level <= DEBUG: {x}\", x=lambda: expensive_function(2**64))\n\n # By the way, \"opt()\" serves many usages\n logger.opt(exception=True).info(\"Error stacktrace added to the log message\")\n logger.opt(ansi=True).info(\"Per message colors\")\n logger.opt(record=True).info(\"Display values from the record (eg. {record[thread]})\")\n logger.opt(raw=True).info(\"Bypass sink formatting\\n\")\n logger.opt(depth=1).info(\"Use parent stack context (useful within wrapped functions)\")\n\n\nCustomizable levels\n^^^^^^^^^^^^^^^^^^^\n\n`Loguru` comes with all standard `logging levels`_ to which |trace|_ and |success|_ are added. Do you need more? Then, just create it by using the |level|_ function.\n\n::\n\n new_level = logger.level(\"SNAKY\", no=38, color=\"\", icon=\"\ud83d\udc0d\")\n\n logger.log(\"SNAKY\", \"Here we go!\")\n\n\nBetter datetime handling\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe standard logging is bloated with arguments like ``datefmt`` or ``msecs``, ``%(asctime)s`` and ``%(created)s``, naive datetimes without timezone information, not intuitive formatting, etc. `Loguru` `fixes it`_:\n\n::\n\n logger.add(\"file.log\", format=\"{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}\")\n\n\nSuitable for scripts and libraries\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nUsing the logger in your scripts is easy, and you can |configure|_ it at start. To use `Loguru` from inside a library, remember to never call |add|_ but use |disable|_ instead so logging functions become no-op. If a developer wishes to see your library's logs, he can |enable|_ it again.\n\n::\n\n # For scripts\n config = {\n \"handlers\": [\n {\"sink\": sys.stdout, \"format\": \"{time} - {message}\"},\n {\"sink\": \"file.log\", \"serialize\": True},\n ],\n \"extra\": {\"user\": \"someone\"}\n }\n logger.configure(**config)\n\n # For libraries\n logger.disable(\"my_library\")\n logger.info(\"No matter added sinks, this message is not displayed\")\n logger.enable(\"my_library\")\n logger.info(\"This message however is propagated to the sinks\")\n\n\nEntirely compatible with standard logging\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWish to use built-in logging ``Handler`` as a `Loguru` sink?\n\n::\n\n handler = logging.handlers.SysLogHandler(address=('localhost', 514))\n logger.add(handler)\n\nNeed to propagate `Loguru` messages to standard `logging`?\n\n::\n\n class PropagateHandler(logging.Handler):\n def emit(self, record):\n logging.getLogger(record.name).handle(record)\n\n logger.add(PropagateHandler(), format=\"{message}\")\n\nWant to intercept standard `logging` messages toward your `Loguru` sinks?\n\n::\n\n class InterceptHandler(logging.Handler):\n def emit(self, record):\n # Retrieve context where the logging call occurred, this happens to be in the 6th frame upward\n logger_opt = logger.opt(depth=6, exception=record.exc_info)\n logger_opt.log(record.levelno, record.getMessage())\n\n logging.basicConfig(handlers=[InterceptHandler()], level=0)\n\n\nPersonalizable defaults through environment variables\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nDon't like the default logger formatting? Would prefer another ``DEBUG`` color? `No problem`_::\n\n # Linux / OSX\n export LOGURU_FORMAT=\"{time} | {message}\"\n\n # Windows\n setx LOGURU_DEBUG_COLOR \"\"\n\n\nConvenient parser\n^^^^^^^^^^^^^^^^^\n\nIt is often useful to extract specific information from generated logs, this is why `Loguru` provides a |parse|_ method which helps to deal with logs and regexes.\n\n::\n\n pattern = r\"(?P