{ "info": { "author": "Kyle Lahnakoski", "author_email": "kyle@lahnakoski.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# More Logs - Structured Logging and Exception Handling\n\n\n|Branch |Status |\n|------------|---------|\n|master | [![Build Status](https://travis-ci.org/klahnakoski/mo-logs.svg?branch=master)](https://travis-ci.org/klahnakoski/mo-logs) |\n|dev | [![Build Status](https://travis-ci.org/klahnakoski/mo-logs.svg?branch=dev)](https://travis-ci.org/klahnakoski/mo-logs) [![Coverage Status](https://coveralls.io/repos/github/klahnakoski/mo-logs/badge.svg?branch=dev)](https://coveralls.io/github/klahnakoski/mo-logs?branch=dev) |\n\n\nThis library provides two main features\n\n* **Structured logging** - output is all JSON (with options to serialize to text for humans)\n* **Exception handling weaved in** - Good logs must represent what happened,\nand that can not be done if the logging library is not intimately familiar with\nthe (exceptional) code paths taken.\n\n## Motivation\n\nException handling and logging are undeniably linked. There are many instances\nwhere exceptions are raised and must be logged, and others where the subsuming \nsystem can fully handle the exception, and no log should be emitted. Exception \nhandling semantics are great because they decouple the cause from the solution, \nbut this can be at odds with clean logging - which couples raising and catching \nto make appropriate decisions about what to emit to the log. \n\nThis logging module is additionally responsible for raising exceptions,\ncollecting the trace and context, and then deducing if it must be logged, or\nif it can be ignored because something can handle it.\n\n\n## Basic Usage\n\n### Use `Log.note()` for all logging\n\n```python\n Log.note(\"Hello, World!\")\n```\n\nThere is no need to create logger objects. The `Log` module will keep track of\nwhat, where and when of every call.\n\n### Using named parameters\n\nAll logging calls accept a string template with named parameters. Keyword arguments\ncan be added to the call to provide values. The template and arguments are not \ncombined at call time, rather they are held in a JSON-izable data structure for \nstructured logging. The template is only expanded *if* the log is serialized for humans. \n\n```python\n Log.note(\"Hello, {{name}}!\", name=\"World!\")\n```\n\n**Do not use Python's string formatting features:**\n \n* [string formatting operator (`%`)](http://python-reference.readthedocs.io/en/latest/docs/str/formatting.html), \n* [the `format()` function](https://docs.python.org/3/library/stdtypes.html#str.format) \n* [literal string intrpolation](https://www.python.org/dev/peps/pep-0498/).\n\nUsing any of these will expand the string template at call time, which is a parsing\nnightmare for log analysis tools.\n\n\n### Parametric parameters\n\nAll the `Log` functions accept a `default_params` as a second parameter, like so:\n\n```python\n Log.note(\"Hello, {{name}}!\", {\"name\": \"World!\"})\n```\n\nthis is meant for the situation your code already has a bundled structure you\nwish to use as a source of parameters. If keyword parameters are used, they\nwill override the default values. Be careful when sending whole data\nstructures, they will be logged!\n\n### Formatting parameters\n\nThere are a variety of formatters, and they can be applied by using the \npipe (`|`) symbol. \n\nIn this example we cast the `name` to uppercase\n\n```python\n Log.note(\"Hello, {{name|upper}}!\", name=\"World!\")\n```\n\nSome formatters accept arguments:\n\n```python\n Log.note(\"pi is {{pi|round(places=3)}}!\", pi=3.14159265)\n```\n\nYou can look at the [`strings` module](https://github.com/klahnakoski/mo-logs/blob/dev/mo_logs/strings.py#L56) to see the formatters available.\n\n### Please, never use locals()\n\n```python\n def worker(value):\n name = \"tout le monde!\"\n password = \"123\"\n Log.note(\"Hello, {{name}}\", locals()) # DO NOT DO THIS!\n```\n\nDespite the fact using `locals()` is a wonderful shortcut for logging it is\ndangerous because it also picks up sensitive local variables. Even if\n`{{name}}` is the only value in the template, the whole `locals()` dict will\nbe sent to the structured loggers for recording. \n\n## Exception Handling\n\nAll logs are structured logs; the parameters will be included, unchanged, in\nthe log structure. This library also expects all parameter values to be JSON-\nserializable so they can be stored/processed by downstream JSON tools.\n\n```javascript\n {//EXAMPLE STRUCTURED LOG\n \"template\": \"Hello, {{name}}!\",\n \"param\": {\"name\": \"World!\"},\n \"timestamp\": 1429721745,\n \"thread\": {\n \"name\": \"Main thread\"\n },\n \"location\": {\n \"line\": 3,\n \"file\": \"hello.py\",\n \"method\": \"hello\"\n },\n \"machine\": {\n \"python\": \"CPython\",\n \"os\": \"Windows10\",\n \"name\": \"ekyle-win\"\n }\n }\n```\n\n### Instead of `raise` use `Log.error()`\n\n```python\n Log.error(\"This will throw an error\")\n```\n\nThe actual call will always raise an exception, and it manipulates the stack\ntrace to ensure the caller is appropriately blamed. Feel free to use the\n`raise` keyword (as in `raise Log.error(\"\")`), if that looks nicer to you. \n\n### Always chain your exceptions\n\nThe `cause` parameter accepts an `Exception`, or a list of exceptions.\nChaining is generally good practice that helps you find the root cause of\na failure. \n\n```python\n try:\n # Do something that might raise exception\n except Exception as e:\n Log.error(\"Describe what you were trying to do\", cause=e)\n```\n\n### Use named parameters in your error descriptions too\n\nError logging accepts keyword parameters just like `Log.note()` does\n\n```python\n def worker(value):\n try:\n Log.note(\"Start working with {{key1}}\", key1=value1)\n # Do something that might raise exception\n except Exception as e:\n Log.error(\"Failure to work with {{key2}}\", key2=value2, cause=e)\n```\n\n### No need to formally type your exceptions\n\nAn exception can be uniquely identified by the first-parameter string template\nit is given; exceptions raised with the same template are the same type. You\nshould have no need to create new exception types.\n\n### Testing for exception \"types\"\n\nThis library advocates chaining exceptions early and often, and this hides\nimportant exception types in a long causal chain. `mo-logs` allows you to easily\ntest if a type (or string, or template) can be found in the causal chain by using\nthe `in` keyword: \n\n```python\n def worker(value):\n try:\n # Do something that might raise exception\n except Exception as e:\n if \"Failure to work with {{key2}}\" in e:\n # Deal with exception thrown in above code, no matter\n # how many other exception handlers were in the chain\n```\n\nFor those who may abhor the use of magic strings, feel free to use constants instead:\n\n```python\n KEY_ERROR = \"Failure to work with {{key}}\"\n\n try:\n Log.error(KEY_ERROR, key=42) \n except Exception as e:\n if KEY_ERROR in e:\n Log.note(\"dealt with key error\")\n```\n\n\n\n\n### If you can deal with an exception, then it will never be logged\n\nWhen a caller catches an exception from a callee, it is the caller's\nresponsibility to handle that exception, or re-raise it. There are many\nsituations a caller can be expected to handle exceptions; and in those cases\nlogging an error would be deceptive. \n\n```python\n def worker(value):\n try:\n Log.error(\"Failure to work with {{key3}}\", key3=value3)\n except Exception as e:\n # Try something else\n```\n\n### Use `Log.warning()` if your code can deal with an exception, but you still want to log it as an issue\n\n```python\n def worker(value):\n try:\n Log.note(\"Start working with {{key4}}\", key4=value4)\n # Do something that might raise exception\n except Exception as e:\n Log.warning(\"Failure to work with {{key4}}\", key4=value4, cause=e)\n```\n### Don't loose your stack trace!\n\nBe aware your `except` clause can also throw exceptions: In the event you\ncatch a vanilla Python Exception, you run the risk of loosing its stack trace.\nTo prevent this, wrap your exception in an `Except` object, which will capture\nyour trace for later use. Exceptions thrown from this `Log` library need not\nbe wrapped because they already captured their trace. If you wrap an `Except`\nobject, you simply get back the object you passed.\n\n\n```python\n try:\n # DO SOME WORK \n except Exception as e:\n e = Except.wrap(e)\n # DO SOME FANCY ERROR RECOVERY\n ```\n\n### Always catch all `Exceptions`\n\nCatching all exceptions is preferred over the *only-catch-what-you-can-handle*\nstrategy. First, exceptions are not lost because we are chaining. Second,\nwe catch unexpected `Exceptions` early and we annotate them with a\ndescription of what the local code was intending to do. This annotation\neffectively groups the possible errors (known, or not) into a class, which\ncan be used by callers to decide on appropriate mitigation. \n\nTo repeat: When using dependency injection, callers can not reasonably be\nexpected to know about the types of failures that can happen deep down the\ncall chain. This makes it vitally important that methods summarize all\nexceptions, both known and unknown, so their callers have the information to\nmake better decisions on appropriate action. \n\nFor example: An abstract document container, implemented on top of a SQL \ndatabase, should not emit SQLExceptions of any kind: A caller that uses a \ndocument container should not need to know how to handle SQLExceptions (or any \nother implementation-specific exceptions). Rather, in this example, the \ncaller should be told it \"can not add a document\", or \"can not remove a \ndocument\". This allows the caller to make reasonable decisions when they do \noccur. The original cause (the SQLException) is in the causal chain.\n\nAnother example, involves *nested exceptions*: If you catch a particular type \nof exception, you may inadvertently catch the that same type of exception \nfrom deeper in the call chain. Narrow exception handling is an illusion. \nBroad exception handling will force you to consider a variety of failures \nearly; force you to consider what it means when a block of code fails; and \nforce you to describe it for others.\n\n### Don't make methods you do not need\n\nThere is an argument that suggests you should break your code into methods, \nrather than catching exceptions: The method name will describe action that \nfailed, and the stack trace can be inspected to make mitigation decisions. \nBut this is a poor solution:\n\n* More methods means more complexity; the programmer must find the method, \n remember the method, and wonder if it is used elsewhere.\n* Methods can be removed while refactoring; exceptions make it clear \n the error is important\n* Compiler optimizations can interfere with the call stack\n* The method name might be very long to describe the problem\n* Inspecting stack traces makes for messy code.\n* A stack trace does not include state information that an exception can \n capture.\n\n\n## Log 'Levels'\n\nThe `mo-logs` module has no concept of logging levels it is expected that debug\nvariables (variables prefixed with `DEBUG_` are used to control the logging\noutput.\n\n\n```python\n # simple.py\n DEBUG_SHOW_DETAIL = True\n\n def worker():\n if DEBUG_SHOW_DETAIL:\n Log.note(\"Starting\")\n\n # DO WORK HERE\n\n if DEBUG_SHOW_DETAIL:\n Log.note(\"Done\")\n\n def main():\n try:\n settings = startup.read_settings()\n Log.start(settings.debug)\n\n # DO WORK HERE\n\n except Exception as e:\n Log.error(\"Complain, or not\", e)\n finally:\n Log.stop()\n```\n\nThese debug variables can be set by configuration file:\n\n```javascript\n // settings.json\n {\n \"debug\":{\n \"constants\":{\"simple.DEBUG_SHOW_DETAILS\":false}\n }\n }\n```\n\n## Log Configuration\n\nThe `logs` module will log to the console by default. ```Log.start(settings)```\nwill redirect the logging to other streams, as defined by the settings:\n\n * **log** - List of all log-streams and their parameters\n * **trace** - Show more details in every log line (default False)\n * **cprofile** - Used to enable the builtin python c-profiler (default False)\n * **profile** - Used to enable pyLibrary's simple profiling (default False)\n (eg with Profiler(\"some description\"):)\n * **constants** - Map absolute path of module constants to the values that will\n be assigned. Used mostly to set debugging constants in modules.\n\nOf course, logging should be the first thing to be setup (aside from digesting\nsettings of course). For this reason, applications should have the following\nstructure:\n\n```python\n def main():\n try:\n settings = startup.read_settings()\n Log.start(settings.debug)\n\n # DO WORK HERE\n\n except Exception as e:\n Log.error(\"Complain, or not\", e)\n finally:\n Log.stop()\n```\n\n\n\n \"log\": [\n {\n \"class\": \"logging.handlers.RotatingFileHandler\",\n \"filename\": \"examples/logs/examples_etl.log\",\n \"maxBytes\": 10000000,\n \"backupCount\": 100,\n \"encoding\": \"utf8\"\n },\n {\n \"log_type\": \"email\",\n \"from_address\": \"klahnakoski@mozilla.com\",\n \"to_address\": \"klahnakoski@mozilla.com\",\n \"subject\": \"[ALERT][DEV] Problem in ETL Spot\",\n \"$ref\": \"file://~/private.json#email\"\n },\n {\n \"log_type\": \"console\"\n }\n ]\n\n\n\n## Problems with Python Logging\n\n[Python's default `logging` module](https://docs.python.org/2/library/logging.html#logging.debug)\ncomes close to doing the right thing, but fails: \n\n * It has keyword parameters, but they are expanded at call time so the values are lost in a string. \n * It has `extra` parameters, but they are lost if not used by the matching `Formatter`. \n * It even has stack trace with `exc_info` parameter, but only if an exception is being handled.\n * Python 2.x has no builtin exception chaining, but [Python 3 does](https://www.python.org/dev/peps/pep-3134/)\n\n### More Reading\n\n* **Structured Logging is Good** - https://sites.google.com/site/steveyegge2/the-emacs-problem", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/klahnakoski/mo-logs", "keywords": "", "license": "MPL 2.0", "maintainer": "", "maintainer_email": "", "name": "mo-logs", "package_url": "https://pypi.org/project/mo-logs/", "platform": "", "project_url": "https://pypi.org/project/mo-logs/", "project_urls": { "Homepage": "https://github.com/klahnakoski/mo-logs" }, "release_url": "https://pypi.org/project/mo-logs/2.53.19239/", "requires_dist": null, "requires_python": "", "summary": "More Logs! Structured Logging and Exception Handling", "version": "2.53.19239" }, "last_serial": 5738214, "releases": { "1.0.17029": [], "1.0.17035": [ { "comment_text": "", "digests": { "md5": "927db36d239e0639a11c9ef0ec61c831", "sha256": "341f45f2678e011409af525fadfd782d209ad06dd86d05bd89b4a6f7661160ec" }, "downloads": -1, "filename": "mo_logs-1.0.17035-py2.7.egg", "has_sig": false, "md5_digest": "927db36d239e0639a11c9ef0ec61c831", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 73479, "upload_time": "2017-02-03T03:09:15", "url": "https://files.pythonhosted.org/packages/e5/a6/6d4345db7e71c535c60dbfc7f8a248dfac9d8edffa654efd17c788de8559/mo_logs-1.0.17035-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "a31c2e4eb15f76cdfd5613b5fe7914bd", "sha256": "7117669dcfb6dfcd15191deccfb3cd2010c02f825a1f4592a74f4bb5f8b3fbe4" }, "downloads": -1, "filename": "mo-logs-1.0.17035.zip", "has_sig": false, "md5_digest": "a31c2e4eb15f76cdfd5613b5fe7914bd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45772, "upload_time": "2017-02-03T03:09:19", "url": "https://files.pythonhosted.org/packages/07/59/35979c3be958289ce9f16f771b22ece8d4588093ebb203fa7081d72192d3/mo-logs-1.0.17035.zip" } ], "1.1.17035": [ { "comment_text": "", "digests": { "md5": "a3d497ae0af5d78a39243a16cf2bd4e0", "sha256": "c6df30eecb11c68ff65a457660c4df01292043028566a21ab82a1f75c2cae505" }, "downloads": -1, "filename": "mo_logs-1.1.17035-py2.7.egg", "has_sig": false, "md5_digest": "a3d497ae0af5d78a39243a16cf2bd4e0", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 73468, "upload_time": "2017-02-03T18:36:08", "url": "https://files.pythonhosted.org/packages/81/16/e095b26a38fb1777f02856db4e8e58f4734eabc2af65595cac30cf9fc1e9/mo_logs-1.1.17035-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "6557fa96d80f9cfcc2ac53681914ad77", "sha256": "4fecae0e93716bca13ea2d4f98c24282ca036940d7d831d69e609464e495d6a9" }, "downloads": -1, "filename": "mo-logs-1.1.17035.zip", "has_sig": false, "md5_digest": "6557fa96d80f9cfcc2ac53681914ad77", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 45773, "upload_time": "2017-02-03T18:36:13", "url": "https://files.pythonhosted.org/packages/d9/da/10f44391b0e2ed6a59d86f2a55c710727fee8ae58daf383fe256979fb181/mo-logs-1.1.17035.zip" } ], "1.1.17039": [ { "comment_text": "", "digests": { "md5": "111886392b99500f8b01d3a387864354", "sha256": "534bf93f74fce0a3f71a32119b0d4afebdceee3a028ae8462f14714f14d5177c" }, "downloads": -1, "filename": "mo_logs-1.1.17039-py2.7.egg", "has_sig": false, "md5_digest": "111886392b99500f8b01d3a387864354", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 76929, "upload_time": "2017-02-07T19:49:06", "url": "https://files.pythonhosted.org/packages/ee/2b/6a1e705d69e4e7626a16acc7a4149df57703695c26c0b45e83c5cfacd21a/mo_logs-1.1.17039-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "710ab4b0bedd1a11191cc7aee5516629", "sha256": "c67d0085cc80d8ad0a834d11c6878d1b76091b0ab6f1ea7f05c2951901736a35" }, "downloads": -1, "filename": "mo-logs-1.1.17039.zip", "has_sig": false, "md5_digest": "710ab4b0bedd1a11191cc7aee5516629", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47097, "upload_time": "2017-02-07T19:49:11", "url": "https://files.pythonhosted.org/packages/8e/77/3e617d32c7be2a7cacc7e46a6ef7bd20270903df0fa15cb7fdd594d1cfdf/mo-logs-1.1.17039.zip" } ], "1.1.17041": [ { "comment_text": "", "digests": { "md5": "54dabe94f64fd5525e1508104500a0ed", "sha256": "8d7f7577fa300ab8c5a74e1a34622a9ebf951a60253e2f856b8c4ff7ca2ecabd" }, "downloads": -1, "filename": "mo_logs-1.1.17041-py2.7.egg", "has_sig": false, "md5_digest": "54dabe94f64fd5525e1508104500a0ed", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 35271, "upload_time": "2017-02-09T16:48:23", "url": "https://files.pythonhosted.org/packages/1c/f4/72e45aec79e2ec2f737d33a752b2dbd197506cb4c7062f8b7447968954d6/mo_logs-1.1.17041-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "61757709f388109311905d768f6347b9", "sha256": "54a39860c77348581c54164730b1c36a713d75d98d299a2f2f53ca043c6d5b5d" }, "downloads": -1, "filename": "mo-logs-1.1.17041.zip", "has_sig": false, "md5_digest": "61757709f388109311905d768f6347b9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47080, "upload_time": "2017-02-09T16:48:27", "url": "https://files.pythonhosted.org/packages/a0/6c/59f787d521986dfc2eaf0883114b844ee4d19a2c34f47d3f44a4eaae64cb/mo-logs-1.1.17041.zip" } ], "1.1.17050": [ { "comment_text": "", "digests": { "md5": "08770277357050c06e176b1aabbb124e", "sha256": "7714295021831aee9c81d69d03408fcfe21e1c52b0e4aa0d7f4c74114b393441" }, "downloads": -1, "filename": "mo_logs-1.1.17050-py2.7.egg", "has_sig": false, "md5_digest": "08770277357050c06e176b1aabbb124e", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 35303, "upload_time": "2017-02-18T01:25:50", "url": "https://files.pythonhosted.org/packages/ed/ec/3496f26eaff28c4b333103135da594cb0caa2116c4be11db976969b605c8/mo_logs-1.1.17050-py2.7.egg" } ], "1.1.17056": [ { "comment_text": "", "digests": { "md5": "51b89f72f9ec68abd513c7363c7d2080", "sha256": "1402ba92a88d4096d30ebd4880e0bfb00103c848e5210cc077524d09973260eb" }, "downloads": -1, "filename": "mo_logs-1.1.17056-py2.7.egg", "has_sig": false, "md5_digest": "51b89f72f9ec68abd513c7363c7d2080", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 35303, "upload_time": "2017-02-25T20:24:18", "url": "https://files.pythonhosted.org/packages/43/fd/00aefadc4f455e7965ec4c05d44a828aa3dad2b4cdf61f35fb7bee66e6bb/mo_logs-1.1.17056-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "ac9226db55e69f009955e6a157e1ad58", "sha256": "68730a438893d88fca642df028f9306415a2e6fdfd0189327807599c229a4378" }, "downloads": -1, "filename": "mo-logs-1.1.17056.zip", "has_sig": false, "md5_digest": "ac9226db55e69f009955e6a157e1ad58", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47107, "upload_time": "2017-02-25T20:24:22", "url": "https://files.pythonhosted.org/packages/4c/e9/6e5242cd19fff4cef3a6cd993a8867784369883dbd23e2d13af23c8b148d/mo-logs-1.1.17056.zip" } ], "1.1.17085": [ { "comment_text": "", "digests": { "md5": "e953732719b1fc27f139aba84bc5499c", "sha256": "253790902f908b502c3363a97abf2ec3b239caf4b0c3446aad3285a9dc78a466" }, "downloads": -1, "filename": "mo_logs-1.1.17085-py2.7.egg", "has_sig": false, "md5_digest": "e953732719b1fc27f139aba84bc5499c", "packagetype": "bdist_egg", "python_version": "2.7", "requires_python": null, "size": 35767, "upload_time": "2017-03-26T12:30:37", "url": "https://files.pythonhosted.org/packages/d5/54/07bcdf8a334c28b291e8b7c6aa44b7189a5e44d17ca9b6e8f036937d7d98/mo_logs-1.1.17085-py2.7.egg" }, { "comment_text": "", "digests": { "md5": "9c0e4b3da35a822935a27fbda877e2e5", "sha256": "a63d17b20011b87b6401939cc0c7c4d6e5f4fdc0d4598df5a11b51aaa5c24a1b" }, "downloads": -1, "filename": "mo-logs-1.1.17085.zip", "has_sig": false, "md5_digest": "9c0e4b3da35a822935a27fbda877e2e5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47571, "upload_time": "2017-03-26T12:30:40", "url": "https://files.pythonhosted.org/packages/76/69/63037be1b1982949df382c03ba213f894ccd95e8dbf056bb054a8ad3e720/mo-logs-1.1.17085.zip" } ], "1.1.17092": [ { "comment_text": "", "digests": { "md5": "25208cb6905a2608c2b98b06f9326337", "sha256": "e5945f7dfc6ff8ed1f5beda22384a740bc1417250d3596a979304b710f7dd4e6" }, "downloads": -1, "filename": "mo-logs-1.1.17092.zip", "has_sig": false, "md5_digest": "25208cb6905a2608c2b98b06f9326337", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47580, "upload_time": "2017-04-02T16:00:56", "url": "https://files.pythonhosted.org/packages/c2/d2/8b624bd9dc7f52d47537ffa6f05a78e94b8d505ee3c98738540617b9ae6d/mo-logs-1.1.17092.zip" } ], "1.1.17101": [ { "comment_text": "", "digests": { "md5": "dc2248e7aabf62184d56deec1c3445aa", "sha256": "f655f9d1d1486fe2a66ad6d4c3d79f09dd396c4ac448077f1ee5b812cdfa3782" }, "downloads": -1, "filename": "mo-logs-1.1.17101.zip", "has_sig": false, "md5_digest": "dc2248e7aabf62184d56deec1c3445aa", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 47439, "upload_time": "2017-04-11T20:33:12", "url": "https://files.pythonhosted.org/packages/3b/4a/eece1ca9b9f2d4ecadb5eafde200cf85d390419a1f777b10b821417af817/mo-logs-1.1.17101.zip" } ], "1.2.17102": [ { "comment_text": "", "digests": { "md5": "e060b1fbfa2820f0662afa54ec46d3b6", "sha256": "2dea2a5a8d638c0132864d3ee7a15cd5aba3e4d6d9168afc99b688a0047e53a1" }, "downloads": -1, "filename": "mo-logs-1.2.17102.zip", "has_sig": false, "md5_digest": "e060b1fbfa2820f0662afa54ec46d3b6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 49303, "upload_time": "2017-04-12T12:09:57", "url": "https://files.pythonhosted.org/packages/8b/71/3c5879c962826e6dc82ddadc94b5f18cbf6500f301b6ae9a9059ae835c68/mo-logs-1.2.17102.zip" } ], "1.3.17102": [ { "comment_text": "", "digests": { "md5": "3499a157948cb166f12e3d7cf9f9c26a", "sha256": "d646f12bad668fb70b9f9b8af7f971d19cb06f342e7702703eccdb81a9af8a3b" }, "downloads": -1, "filename": "mo-logs-1.3.17102.zip", "has_sig": false, "md5_digest": "3499a157948cb166f12e3d7cf9f9c26a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 49303, "upload_time": "2017-04-12T12:11:11", "url": "https://files.pythonhosted.org/packages/5d/28/c4e15d782f29f268f56dc9a7867c8132d6bf111f5cc6736cbb3ab6d8f3f1/mo-logs-1.3.17102.zip" } ], "1.3.17131": [ { "comment_text": "", "digests": { "md5": "064d6bd6916e537019264d02a94518f4", "sha256": "8701d2fcac1084d2340107fe07e73c0cb8e2486ad3c2ef1e995981b3f81348d7" }, "downloads": -1, "filename": "mo-logs-1.3.17131.zip", "has_sig": false, "md5_digest": "064d6bd6916e537019264d02a94518f4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 52852, "upload_time": "2017-05-11T13:40:30", "url": "https://files.pythonhosted.org/packages/d7/f0/1e0254e4e045e17dac06adaa660c0289b9c10dcefc259d8b2a0437a17f76/mo-logs-1.3.17131.zip" } ], "1.3.17147": [ { "comment_text": "", "digests": { "md5": "b8dcd55e89de50ac03d5a6331796d2b6", "sha256": "886290682fac0e03bd4d4e65b3a4faff109b4312786b47490a4d783ce64c7174" }, "downloads": -1, "filename": "mo-logs-1.3.17147.zip", "has_sig": false, "md5_digest": "b8dcd55e89de50ac03d5a6331796d2b6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 52932, "upload_time": "2017-05-27T14:17:39", "url": "https://files.pythonhosted.org/packages/40/7a/c9626d664622345d0c1098e686f25cddca5a404612ac57221962c93a1978/mo-logs-1.3.17147.zip" } ], "1.3.17227": [ { "comment_text": "", "digests": { "md5": "c9f4982b14fb51e87abb122955aec362", "sha256": "c66882d8e8ef24fe339043d655a526f19184353ac075e6d936fb7e9452778449" }, "downloads": -1, "filename": "mo-logs-1.3.17227.zip", "has_sig": false, "md5_digest": "c9f4982b14fb51e87abb122955aec362", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 53932, "upload_time": "2017-08-15T12:54:29", "url": "https://files.pythonhosted.org/packages/c0/f5/64669bc1af7012ec36ed8bad2ed2497f5076f4deaa31fb9fadf7804b139c/mo-logs-1.3.17227.zip" } ], "1.4.17235": [ { "comment_text": "", "digests": { "md5": "300dda055c7e7a45afbaad79add54fec", "sha256": "4d944fc0f08ea629665ce5d3e3611c7ed98cf5a6b6b69c3e23d96320d0623946" }, "downloads": -1, "filename": "mo-logs-1.4.17235.zip", "has_sig": false, "md5_digest": "300dda055c7e7a45afbaad79add54fec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 53932, "upload_time": "2017-08-23T16:49:46", "url": "https://files.pythonhosted.org/packages/01/49/8685f4e8282616d33488e6c48988e342f5a5e07fb1316368d3060ed97219/mo-logs-1.4.17235.zip" } ], "1.7.17339": [ { "comment_text": "", "digests": { "md5": "b08d1c55d81614fb12a192bebd0aea49", "sha256": "c66429f861596257154b2760360aa8fcab5ae234e1e40edecf501abbc5af8906" }, "downloads": -1, "filename": "mo-logs-1.7.17339.tar.gz", "has_sig": false, "md5_digest": "b08d1c55d81614fb12a192bebd0aea49", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31021, "upload_time": "2017-12-05T01:58:25", "url": "https://files.pythonhosted.org/packages/af/a5/008d7642afb61c31cf88084d1a8b1cde2a44861da298e660a10d3329d9de/mo-logs-1.7.17339.tar.gz" } ], "1.7.18011": [ { "comment_text": "", "digests": { "md5": "9d8117f4dd98150dcd23be5fceb06b3e", "sha256": "4b01b825636e89589ee25537c1c23c5c85b01bb8497bbd3396742eb211092a64" }, "downloads": -1, "filename": "mo-logs-1.7.18011.tar.gz", "has_sig": false, "md5_digest": "9d8117f4dd98150dcd23be5fceb06b3e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31018, "upload_time": "2018-01-11T17:29:12", "url": "https://files.pythonhosted.org/packages/13/5d/82c6fbec5a199e6e2f061c6e8149190dc523d93be40a0025ddc7d8a59926/mo-logs-1.7.18011.tar.gz" } ], "1.7.18025": [ { "comment_text": "", "digests": { "md5": "9242d57fefbb0251decc7c703a0d6803", "sha256": "9c4fd0af1ef565dbcbaacc846bd664ed5d1cd29ced0d651c593087097f5aa871" }, "downloads": -1, "filename": "mo-logs-1.7.18025.tar.gz", "has_sig": false, "md5_digest": "9242d57fefbb0251decc7c703a0d6803", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31553, "upload_time": "2018-01-25T16:29:34", "url": "https://files.pythonhosted.org/packages/bf/f7/f93ea24d71ca964a13b00c0a2609d3a71367c7bcf2dc68be802f0a2d09b3/mo-logs-1.7.18025.tar.gz" } ], "1.7.18029": [ { "comment_text": "", "digests": { "md5": "e3a805cc1ca0f45b207ba4987ad739a0", "sha256": "67a6699725241bcce58ed462f332df51e70429a2a1660f200e3f639474978499" }, "downloads": -1, "filename": "mo-logs-1.7.18029.tar.gz", "has_sig": false, "md5_digest": "e3a805cc1ca0f45b207ba4987ad739a0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31616, "upload_time": "2018-01-29T14:54:53", "url": "https://files.pythonhosted.org/packages/1b/83/8ced51a024df2f4449e81972fc6f456051e5983471a70124be2b59f621c2/mo-logs-1.7.18029.tar.gz" } ], "1.7.18059": [ { "comment_text": "", "digests": { "md5": "98df6331713798960a3bf2e4979a96e9", "sha256": "1e39635a5cfb7e846c9d256264f1acc5d45391a6314fcdfaa42eee160ffee1d0" }, "downloads": -1, "filename": "mo-logs-1.7.18059.tar.gz", "has_sig": false, "md5_digest": "98df6331713798960a3bf2e4979a96e9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 31644, "upload_time": "2018-02-28T01:40:40", "url": "https://files.pythonhosted.org/packages/47/cd/04255f3c28604f616505aa014b4b4bbea18f895debd41c60c123d58d5547/mo-logs-1.7.18059.tar.gz" } ], "1.7.18072": [ { "comment_text": "", "digests": { "md5": "b3cedf4ecb8fe0dafcf12465b8124217", "sha256": "e255fe792b8e8f8ff954bc754ad10539be5241f629754e18f8954dc01670307b" }, "downloads": -1, "filename": "mo-logs-1.7.18072.tar.gz", "has_sig": false, "md5_digest": "b3cedf4ecb8fe0dafcf12465b8124217", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32614, "upload_time": "2018-03-13T18:00:23", "url": "https://files.pythonhosted.org/packages/ed/8f/24a3b88e00e1d3f0f1cd157ac9b70fd1f5f94583bcc68b59e875f134c2a8/mo-logs-1.7.18072.tar.gz" } ], "2.11.18154": [ { "comment_text": "", "digests": { "md5": "0c683082b4474ddad92cdca75de084f3", "sha256": "9388c6f427355cb07a3488f6cfb0a9df96f628119bce45f2813f537bcf820c24" }, "downloads": -1, "filename": "mo-logs-2.11.18154.tar.gz", "has_sig": false, "md5_digest": "0c683082b4474ddad92cdca75de084f3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33340, "upload_time": "2018-06-03T23:36:28", "url": "https://files.pythonhosted.org/packages/9c/94/cb16b34f4e623aef127e13390046fc1d37290c9a8e9523f8315b0fe16cd6/mo-logs-2.11.18154.tar.gz" } ], "2.16.18199": [ { "comment_text": "", "digests": { "md5": "0dba9e0786eaabb5efd2a3fc80ac33cb", "sha256": "fb5008a01986dc4649e2fed14cc558669cb8f1eb2a8f5db890cf8bb8cf4066b2" }, "downloads": -1, "filename": "mo-logs-2.16.18199.tar.gz", "has_sig": false, "md5_digest": "0dba9e0786eaabb5efd2a3fc80ac33cb", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34101, "upload_time": "2018-07-18T12:16:09", "url": "https://files.pythonhosted.org/packages/86/43/7571dbb8df80e5c41b22829c9cbc252bb39e217afbbded358346513fb21b/mo-logs-2.16.18199.tar.gz" } ], "2.18.18240": [ { "comment_text": "", "digests": { "md5": "bbc9e9b92804671607ae43ca9988dbbc", "sha256": "041127d22d2b994dd1797a8408e4f6bb23855920097861b7b3750d3a8676954f" }, "downloads": -1, "filename": "mo-logs-2.18.18240.tar.gz", "has_sig": false, "md5_digest": "bbc9e9b92804671607ae43ca9988dbbc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 32861, "upload_time": "2018-08-28T13:04:05", "url": "https://files.pythonhosted.org/packages/49/65/dd47d8920fca59df738741b24c77aa9b9e9d773007cae5045040ae88c8b8/mo-logs-2.18.18240.tar.gz" } ], "2.23.18244": [ { "comment_text": "", "digests": { "md5": "53b189e1d9d1d3be28b2ed80d973db03", "sha256": "484d2240f160c476268c47bd04688976fd3ec3c85e91b7a1845466811e015e1e" }, "downloads": -1, "filename": "mo-logs-2.23.18244.tar.gz", "has_sig": false, "md5_digest": "53b189e1d9d1d3be28b2ed80d973db03", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34658, "upload_time": "2018-09-01T15:23:58", "url": "https://files.pythonhosted.org/packages/f5/f8/aa2e6322ad6924d2b46047ea5d30b3b04287749dd087cd793923e74601df/mo-logs-2.23.18244.tar.gz" } ], "2.24.18269": [ { "comment_text": "", "digests": { "md5": "256763dd335e078adbebf570c89d1880", "sha256": "79d0a1899ff30c4188b98dcca8d9307a7467c36290b01620e27d07f95ce87776" }, "downloads": -1, "filename": "mo-logs-2.24.18269.tar.gz", "has_sig": false, "md5_digest": "256763dd335e078adbebf570c89d1880", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 33217, "upload_time": "2018-09-26T22:59:02", "url": "https://files.pythonhosted.org/packages/a6/fc/41ae5b69fa839cea82cf9ddeea8eb3a1f7d001d6fbf6b3810a06d7623a42/mo-logs-2.24.18269.tar.gz" } ], "2.26.18331": [ { "comment_text": "", "digests": { "md5": "99c77380e8fe60968b63f9506f0fe46f", "sha256": "4bac715c8cc6f84fcf3e85c6026c15e58665af3b7ed5ab35ea3e9f44ffdaa85a" }, "downloads": -1, "filename": "mo-logs-2.26.18331.tar.gz", "has_sig": false, "md5_digest": "99c77380e8fe60968b63f9506f0fe46f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 34155, "upload_time": "2018-11-27T14:20:34", "url": "https://files.pythonhosted.org/packages/cc/ec/4ba533ddc34a0cf5dd56065c979fce5d8efc976e761df3804cc5bbc3b258/mo-logs-2.26.18331.tar.gz" } ], "2.27.19008": [ { "comment_text": "", "digests": { "md5": "4e074caf3a7b86eaabd57d95f22ac8b9", "sha256": "d4d228d011acca61e2b24c1fb9abdc21262718998eaa874b829eb8bf27133581" }, "downloads": -1, "filename": "mo-logs-2.27.19008.tar.gz", "has_sig": false, "md5_digest": "4e074caf3a7b86eaabd57d95f22ac8b9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30318, "upload_time": "2019-01-08T17:28:45", "url": "https://files.pythonhosted.org/packages/cd/b0/ba6a1f91110f1c181d997a4f699b6a15f9efd135d22054342cd93cf605fa/mo-logs-2.27.19008.tar.gz" } ], "2.28.19023": [ { "comment_text": "", "digests": { "md5": "fe15f2ef779ed96ba24abb27cea1a7ad", "sha256": "a4f888d3d9a8f6e7738e068afc40656fe2436245746381b0796b157ddbcf57ec" }, "downloads": -1, "filename": "mo-logs-2.28.19023.tar.gz", "has_sig": false, "md5_digest": "fe15f2ef779ed96ba24abb27cea1a7ad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30332, "upload_time": "2019-01-23T14:55:20", "url": "https://files.pythonhosted.org/packages/0f/37/c237d1702f84f251bc7e374b1f8c47534859d5063eab451daf130382531a/mo-logs-2.28.19023.tar.gz" } ], "2.29.19025": [ { "comment_text": "", "digests": { "md5": "d9a0e58bf3cac48833487c9b6fd333b4", "sha256": "eb87006487990257c385a38fc1958afef078b1a01cae085ffa4a2b640062100e" }, "downloads": -1, "filename": "mo-logs-2.29.19025.tar.gz", "has_sig": false, "md5_digest": "d9a0e58bf3cac48833487c9b6fd333b4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30323, "upload_time": "2019-01-25T18:19:51", "url": "https://files.pythonhosted.org/packages/0e/75/d689ddf18595f5563669eeccbc8e78bd4a6c423c41ef5f179fb5f463ef67/mo-logs-2.29.19025.tar.gz" } ], "2.31.19025": [ { "comment_text": "", "digests": { "md5": "344bb346e1457f3194e42f35ee80039c", "sha256": "9c3545831fb2cb0c430f2aeaf3b5db85f215a4db88d203916b6f012986a26c4e" }, "downloads": -1, "filename": "mo-logs-2.31.19025.tar.gz", "has_sig": false, "md5_digest": "344bb346e1457f3194e42f35ee80039c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30335, "upload_time": "2019-01-25T23:51:59", "url": "https://files.pythonhosted.org/packages/40/cf/a04d58d8feb28b72923c27fbf81fedd9659f74a9b198d55d030adce2fba0/mo-logs-2.31.19025.tar.gz" } ], "2.39.19026": [ { "comment_text": "", "digests": { "md5": "6931dce4dec6cc3fe99fbfc50529a500", "sha256": "1ee5e6825764b44cfd8e19d2d10ccb79ac02d54c4740ae91ae0e1a0b58fd15db" }, "downloads": -1, "filename": "mo-logs-2.39.19026.tar.gz", "has_sig": false, "md5_digest": "6931dce4dec6cc3fe99fbfc50529a500", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30510, "upload_time": "2019-01-26T22:09:09", "url": "https://files.pythonhosted.org/packages/1a/f4/864a3cac841327fbc074df2cb36957cd3161808980a4dfb3e8c560d06cb7/mo-logs-2.39.19026.tar.gz" } ], "2.43.19055": [ { "comment_text": "", "digests": { "md5": "9b276f2e137c23df70b2d9681ddcb3d7", "sha256": "2669327421a89ba1b07d990befcb6eebf07190e2e7ff2432a5f01d309d01fb56" }, "downloads": -1, "filename": "mo-logs-2.43.19055.tar.gz", "has_sig": false, "md5_digest": "9b276f2e137c23df70b2d9681ddcb3d7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30556, "upload_time": "2019-02-24T18:45:41", "url": "https://files.pythonhosted.org/packages/8c/aa/975bc5f89540172e44923caf14445e339190eec9fb6c503240f4ff4499e0/mo-logs-2.43.19055.tar.gz" } ], "2.46.19127": [ { "comment_text": "", "digests": { "md5": "58d01be2a245b7c43c10c5266f719cfd", "sha256": "97f10c57da9af11f065093e2a601c50ca4e53271677512c1af6a7ccfc3aed929" }, "downloads": -1, "filename": "mo-logs-2.46.19127.tar.gz", "has_sig": false, "md5_digest": "58d01be2a245b7c43c10c5266f719cfd", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30660, "upload_time": "2019-05-07T15:10:07", "url": "https://files.pythonhosted.org/packages/c5/44/68de3b23f8f5153eb0cc56f66cf593713a24930c27b10ff6ccf6c6d2a6f3/mo-logs-2.46.19127.tar.gz" } ], "2.47.19127": [ { "comment_text": "", "digests": { "md5": "0d383d69b29a5328ebb5093f4dcb0b4c", "sha256": "fd8add8a647a4a30e4815b25196abaad40af7cbefe17b3e1cc131d3e484ad711" }, "downloads": -1, "filename": "mo-logs-2.47.19127.tar.gz", "has_sig": false, "md5_digest": "0d383d69b29a5328ebb5093f4dcb0b4c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30591, "upload_time": "2019-05-07T18:37:02", "url": "https://files.pythonhosted.org/packages/13/54/cf7d55bc5917bbba768cf3b3bbb0fcbf0570fb62cf7d98beb64984570396/mo-logs-2.47.19127.tar.gz" } ], "2.53.19239": [ { "comment_text": "", "digests": { "md5": "c256382c459ecb0ac505b2555034d63d", "sha256": "0ae4e3abb0e59d537129fed1462a99ad6e913e4ab2adaeb2a639b084ef9d02ae" }, "downloads": -1, "filename": "mo-logs-2.53.19239.tar.gz", "has_sig": false, "md5_digest": "c256382c459ecb0ac505b2555034d63d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30732, "upload_time": "2019-08-27T17:18:29", "url": "https://files.pythonhosted.org/packages/ca/18/60da7ab5bec030ecdeaf9576e15c76ca1491b1436ff3f07c8117595b7f8a/mo-logs-2.53.19239.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c256382c459ecb0ac505b2555034d63d", "sha256": "0ae4e3abb0e59d537129fed1462a99ad6e913e4ab2adaeb2a639b084ef9d02ae" }, "downloads": -1, "filename": "mo-logs-2.53.19239.tar.gz", "has_sig": false, "md5_digest": "c256382c459ecb0ac505b2555034d63d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 30732, "upload_time": "2019-08-27T17:18:29", "url": "https://files.pythonhosted.org/packages/ca/18/60da7ab5bec030ecdeaf9576e15c76ca1491b1436ff3f07c8117595b7f8a/mo-logs-2.53.19239.tar.gz" } ] }