{ "info": { "author": "Morten Jensen", "author_email": "release@virtuability.com", "bugtrack_url": null, "classifiers": [], "description": "# Python context-log library\n\n## About\n\ncontext-log is a simple library to emit contextual information in structured logs (JSON).\n\nIt works particularly well in a Docker or Serverless (e.g. AWS Lambda) environment where a single thread executes a request and produces a response.\n\nThe library uses python threading to store contextual information that is automatically added to all subsequent logs in a `contextMap` field.\n\nBecause the library uses the Python thread local context it works across packages and modules in a given project.\n\nThe approach is loosely based on the [Log4j 2 API Thread Context](https://logging.apache.org/log4j/2.x/manual/thread-context.html).\n\n## Usage\n\nStructured logging can be achieved with the [python-json-logger library](https://pypi.org/project/python-json-logger/).\n\nSimply add project dependencies to requirements.txt:\n\n```python\npython_json_logger\nPyYAML\ncontext-log\n```\n\nAdd the code below to the main code module.\n\nAdd the following YAML configuration in the `resources/logging.yaml` file, which outputs JSON structured logs to `stdout`.\n\n```yaml\nversion: 1\nformatters:\n json:\n class: .jsonlogger.JsonFormatter\n format: '%(asctime)s %(name)s %(levelname)s %(message)s %(filename)s'\nhandlers:\n console:\n class: logging.StreamHandler\n level: DEBUG\n formatter: json\nroot:\n level: DEBUG\n handlers:\n - console\n```\n\nUse the context_log library to emit logs. Example below.\n\n```python\nimport logging.config\nimport yaml\n\nwith open('resources/logging.yaml', 'r') as log_config_file:\n logging.config.dictConfig(yaml.safe_load(log_config_file))\n\nfrom context_log import ContextLog\n\ndef handler(event, context):\n # Clear context (e.g. re-use) and get logger\n log = ContextLog.get_logger('handler', True)\n log.info('start')\n\n ContextLog.put('ip', '1.2.3.4')\n\n # Helper to add start time in ISO and epoch time\n ContextLog.put_request_start_time()\n\n # Process request\n sleep(0.1)\n\n # Helper to add end time in ISO and epoch time\n # as well as duration in milliseconds\n ContextLog.put_request_end_time()\n\n log.info('end')\n```\n\nFirst log info event:\n\n```json\n{\n \"asctime\": \"2019-09-19 11:53:20,479\",\n \"name\": \"handler\",\n \"levelname\": \"INFO\",\n \"message\": \"start\",\n \"filename\": \"test_example.py\",\n \"contextMap\": {}\n}\n```\n\nSecond log info event:\n\n```json\n{\n \"asctime\": \"2019-09-19 11:53:20,580\",\n \"name\": \"handler\",\n \"levelname\": \"INFO\",\n \"message\": \"end\",\n \"filename\": \"test_example.py\",\n \"contextMap\": {\n \"ip\": \"1.2.3.4\",\n \"start-time\": \"2019-09-19T11:53:20.480085\",\n \"epoch-start-time\": 1568890400.480085,\n \"end-time\": \"2019-09-19T11:53:20.580513\",\n \"epoch-end-time\": 1568890400.580513,\n \"duration\": 100.428}\n}\n```\n\n## The Detail\n\nThe standard logger is wrapped by a `LoggerAdapter`. It is therefore imperative that the `ContextLog.get_logger(name='', clear=True|False)` call is made to get the logger to emit contextual logs.\n\nUse `clear=True` when starting a new request in order to clear the previous context if the thread is re-used. This is typically the case in thread pools and in AWS Lambda's.\n\nTo manipulate or retrieve the `contextMap` use the following methods:\n\n* `clear()`\n* `put(key, value)`\n* `get(key)`\n* `get_map()`\n\nThere are also a number of helpers in an attempt to standardise log output `contextMap` fields:\n\n* `put_request_id(request_id)`\n* `put_request_method(request_method)`\n* `put_request_path(request_path)`\n* `put_response_status(response_status)`\n* `put_start_time()`\n* `put_end_time()`\n* `put_request_user_id(request_user_id)`\n* `put_request_client_id(request_client_id)`\n* `put_request_primary_ip(primary_ip)`\n* `put_request_client_ip(client_ip)`\n* `put_request_viewer_country(viewer_country)`\n* `put_trigger_source(trigger_source)`\n\n## Contributing\n\nPull requests are more than welcome.\n\n## Running pytest\n\nCreate virtualenv, download dependencies and run tests:\n\n```bash\npython3 -m venv .venv\nsource .venv/bin/activate\npip3 install -r tests/requirements.txt\npip3 install -e .\npytest\n```\n\n## Running tox\n\n```bash\npip3 install --user --upgrade tox\ntox\n```\n\n## Releasing library to PyPI\n\nShort version from the [Packaging Python Projects](https://packaging.python.org/tutorials/packaging-projects/) site.\n\nInstall the release tools:\n\n```bash\npython3 -m pip install --user --upgrade setuptools wheel twine\n```\n\nRemove old distribution(s):\n\n```\nrm -rf dist/\n```\n\nBuild the context-log package:\n\n```bash\npython3 setup.py sdist bdist_wheel\n```\n\nUpload context-log first to Test PyPI:\n\n```bash\npython3 -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*\n```\n\n\n", "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/virtuability/context-log", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "context-log", "package_url": "https://pypi.org/project/context-log/", "platform": "", "project_url": "https://pypi.org/project/context-log/", "project_urls": { "Homepage": "https://github.com/virtuability/context-log" }, "release_url": "https://pypi.org/project/context-log/0.1.2/", "requires_dist": null, "requires_python": ">=3.6", "summary": "A simple library to emit contextual information in structured logs (JSON)", "version": "0.1.2", "yanked": false, "yanked_reason": null }, "last_serial": 6020044, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "9e5268ac42ef8c23d6b17a775ca68413", "sha256": "0a60541a824225604023476fad28acc21aa4bc21af064f6545490a07c1fc9948" }, "downloads": -1, "filename": "context_log-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "9e5268ac42ef8c23d6b17a775ca68413", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 5700, "upload_time": "2019-09-20T08:06:26", "upload_time_iso_8601": "2019-09-20T08:06:26.294784Z", "url": "https://files.pythonhosted.org/packages/80/a1/0df23bbb5d12a64b40c10de1c2d9da26e2bc855f1e4f2b3d171abce8cdf1/context_log-0.1.0-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "b147d371a93d9e64ea2e359ea41cb825", "sha256": "f7473b5cb1fcc1a5943c4eb1ce6c69541dfbbe3fb01c72e52ae05d343c26d5ea" }, "downloads": -1, "filename": "context-log-0.1.0.tar.gz", "has_sig": false, "md5_digest": "b147d371a93d9e64ea2e359ea41cb825", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 4345, "upload_time": "2019-09-20T08:06:28", "upload_time_iso_8601": "2019-09-20T08:06:28.834782Z", "url": "https://files.pythonhosted.org/packages/d7/f9/0f041f79f4c8064eb2f07d2e23305a831a2fed7ddd46008b56071896cafb/context-log-0.1.0.tar.gz", "yanked": false, "yanked_reason": null } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "2b943b3e56f3373622851f7a6950893f", "sha256": "3b4afbcdad68fb6e756a4189fa2fed2dedd79dc585ac97f1d4b115b0b15d9e74" }, "downloads": -1, "filename": "context_log-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "2b943b3e56f3373622851f7a6950893f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 6015, "upload_time": "2019-10-23T18:46:50", "upload_time_iso_8601": "2019-10-23T18:46:50.832969Z", "url": "https://files.pythonhosted.org/packages/af/2e/a5f7167defaceda79657cb20f72690b4ac082508db9a2e34c90a4890f464/context_log-0.1.2-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "9433d02de03e11be22a4daf3cba91e4c", "sha256": "c4022a86ebe2c55cb456ade01db05f696177437c7f5dc1f9cb8c05240585082a" }, "downloads": -1, "filename": "context-log-0.1.2.tar.gz", "has_sig": false, "md5_digest": "9433d02de03e11be22a4daf3cba91e4c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 4752, "upload_time": "2019-10-23T18:46:52", "upload_time_iso_8601": "2019-10-23T18:46:52.783035Z", "url": "https://files.pythonhosted.org/packages/e7/97/de326181cb1238675abb50ce34b6ec17474f1416f39eb1f0a08a17f4d304/context-log-0.1.2.tar.gz", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "2b943b3e56f3373622851f7a6950893f", "sha256": "3b4afbcdad68fb6e756a4189fa2fed2dedd79dc585ac97f1d4b115b0b15d9e74" }, "downloads": -1, "filename": "context_log-0.1.2-py3-none-any.whl", "has_sig": false, "md5_digest": "2b943b3e56f3373622851f7a6950893f", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6", "size": 6015, "upload_time": "2019-10-23T18:46:50", "upload_time_iso_8601": "2019-10-23T18:46:50.832969Z", "url": "https://files.pythonhosted.org/packages/af/2e/a5f7167defaceda79657cb20f72690b4ac082508db9a2e34c90a4890f464/context_log-0.1.2-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "9433d02de03e11be22a4daf3cba91e4c", "sha256": "c4022a86ebe2c55cb456ade01db05f696177437c7f5dc1f9cb8c05240585082a" }, "downloads": -1, "filename": "context-log-0.1.2.tar.gz", "has_sig": false, "md5_digest": "9433d02de03e11be22a4daf3cba91e4c", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6", "size": 4752, "upload_time": "2019-10-23T18:46:52", "upload_time_iso_8601": "2019-10-23T18:46:52.783035Z", "url": "https://files.pythonhosted.org/packages/e7/97/de326181cb1238675abb50ce34b6ec17474f1416f39eb1f0a08a17f4d304/context-log-0.1.2.tar.gz", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }