{ "info": { "author": "Stefan Berke", "author_email": "s.berke@netzkolchose.de", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Framework :: Django", "Framework :: Django :: 1.10", "Framework :: Django :: 2.0", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# django-joblog v0.2.2\n\nA generic django-utility that helps to log stuff to the database.\n\n- [Installation](#installation)\n- [Usage](#usage)\n - [Parallelism](#parallelism)\n - [Context](#context)\n - [DummyJobLogger](#dummyjoblogger)\n - [Using the Model](#using-the-model)\n- [Configuration](#configuration)\n - [DB alias](#db-alias)\n - [Live updates](#live-updates)\n - [Ping mode](#ping)\n- [Testing](#testing)\n - [This repo](#the-repository)\n\n\n# Overview\n\n```python\nfrom django_joblog import JobLogger\n\nwith JobLogger(\"task-name\") as log:\n log.log(\"task started\")\n if 1 != 2:\n log.error(\"The impossible happened!\") \n```\n\nThe following information is stored to the database for further inspection:\n- the task's name\n- the count of invocation for the specific task\n- start-time\n- end-time\n- duration \n- any log or error output \n- the exception trace, for exception occuring inside the `with`-block\n\nThis can be useful in conjuction with cronjobs and asynchronous tasks with, e.g., these libraries:\n[django-kronos](https://github.com/jgorset/django-kronos), \n[django-rq](https://github.com/rq/django-rq), ...\n\n\n# Installation\n\n```bash\npip install django-joblog\n```\n\nThen add `django_joblog` to `INSTALLED_APPS` in your django `settings.py` and call `manage.py migrate`.\n\n## Requirements\n\n- [Python](https://www.python.org) 2 or 3\n- [Django](https://www.djangoproject.com) >= 1.10\n\n\n# Usage \n\n### Parallelism\n\nBy default, jobs are not allowed to run in parallel. This can be changed with `parallel=True` in \nthe `JobLogger` constructor. \n\n**If you start a JobLogger while a job with the same name is already\nrunning, a `django_joblog.JobIsAlreadyRunningError` is raised.** Additionally, a job log entry\nin the database will be created with `blocked` state.\n\nFor example, you might have a cronjob that runs every minute and looks for open tasks in the \ndatabase. If you wrap the task in a `JobLogger` you can be sure, that the tasks are not \nworked on in parallel:\n\n```python\nfrom django_joblog import JobLogger, JobIsAlreadyRunningError\n\ndef cronjob_open_task_worker():\n if open_tasks():\n with JobLogger(\"work-open-tasks\") as log:\n work_open_tasks(log)\n \n# to avoid the error message on multiple invocation:\ndef cronjob_open_task_worker():\n if open_tasks():\n try:\n with JobLogger(\"work-open-tasks\") as log:\n work_open_tasks(log)\n except JobIsAlreadyRunningError:\n pass\n```\n\n### Context\n\nTo change the logging-context within a job, use `JobLoggerContext`. \nIt might help to spot at which point an output is generated or an exception is thrown.\n\n```python\nfrom django_joblog import JobLogger, JobLoggerContext\n\nwith JobLogger(\"pull-the-api\") as log:\n \n credentials = get_credentials()\n log.log(\"using user %s\" % credentials.name)\n \n with JobLoggerContext(log, \"api\"):\n api = Api(credentials)\n log.log(\"connected\")\n \n with JobLoggerContext(log, \"submit\"):\n api.submit(data)\n log.log(\"%s items submitted\" % len(data))\n \n with JobLoggerContext(log, \"check result\"):\n log.log(api.check_result())\n``` \n\nThe log output in database will look like this:\n```\nusing user Herbert\napi: connected\napi:submit: 42 items submitted\napi:check result: 23 items updated\n```\n\nAn exception caught by the error log might look like this:\n```\napi:submit: IOError - Status code 404 returned for url https://my.api.com/submit\n File \"/home/user/python/awesome_project/api/Api.py, line 178, in Api._make_request\n self.session.post(url, data=params)\n File \"/home/user/python/awesome_project/api/Api.py, line 66, in Api.submit\n self._make_request(url, params)\n File \"/home/user/python/awesome_project/main.py, line 12\n api.submit(data) \n```\n\nAs can be seen, a `JobLoggerContext` does not pop it's name from the context stack \nin case of an exception! Which means, catching exceptions within higher context levels \nthan where those exceptions where raised does not leave a valid context stack if \nyou resume work after the caught exception. \n\n\n### DummyJobLogger\n\nYou can use the `DummyJobLogger` class to provide logging without storing stuff to the database. \nThis might be useful for debugging purposes, or if you run a function as a `manage.py`-task but\nneed database logging only for cronjobs.\n\nIn general, functions can be designed to work with a `JobLogger` but do not *require* it.\n\n```python\nfrom django_joblog import JobLogger, DummyJobLogger\n\ndef buy_eggs(log=None):\n log = log or DummyJobLogger()\n \n log.log(\"Gonna buy some eggs!\")\n ...\n\ndef cronjob_invokation():\n with JobLogger(\"buy-eggs\") as log:\n buy_eggs(log)\n \ndef debug_invokation():\n buy_eggs()\n\n```\n\n### Using the model\n\nBy default, there is a django admin view for the `JobLogModel`. \nYou can find the model, as usual, in `django_joblog.models`. \nPlease check the file [django_joblog/models.py](https://github.com/defgsus/django-joblog/blob/master/django_joblog/models.py)\nfor the specific fields. It's nothing special.\n\n![admin changelist screenshot](./docs/admin-changelist.png)\n\n\n# Configuration\n\nThe `django_joblog` app can be configured with an object in your django project `settings.py`, \nfor example:\n\n```python\nJOBLOG_CONFIG = {\n # name of alternate database connection, to circumvent transactions on default connection\n \"db_alias\": \"joblog\",\n # enable .log and .error to write to database immediately\n \"live_updates\": True,\n # enable a constant update of job state - to check for jobs which went away without notice\n \"ping\": True,\n \"ping_interval\": 1,\n # always print to console during jobs\n \"print_to_console\": True\n}\n```\n\nThe whole object and all of it's fields are optional. \n\n### db_alias\n\n`db_alias` defines an alternative name for the database connection. \nThis name must be present in the `DATABASE` setting. \n\nOne does not normally need to define this setting unless you want to make sure that \n[Live updates](#live-updates) or using the [Ping mode](#ping) always work even when transactions\nare used inside the job body. Consider this example:\n\n```python\nfrom django.db import transaction\nfrom django_joblog import JobLogger\n\nwith JobLogger(\"my-job\") as job:\n job.log(\"Outside transaction\")\n with transaction.atomic():\n job.log(\"Inside transaction\")\n # ...other stuff...\n``` \n\nIf you are using [Live updates](#live-updates) and need to make sure that the second log \n(\"Inside transaction\") is immediately stored to the database you need to define a second \ndatabase connection. It can just be a copy of the `'default'` database setting. \n\n### live updates\n\nSetting `live_updates` to `True` will store the current log and error texts as long with the current\njob duration to database whenever `JobLogger.log()` or `JobLogger.error()` are called.\n\n### ping\n\nSetting `ping` to `True` will spawn a separate thread when calling `with JobLogger(...)` that will \nconstantly update the joblog database with the current log text, error text and duration. \nThe update-interval is configured with `ping_interval` in seconds. \n\nNormally, if a job exits unexpectedly (segfault, power-off, restart of vm, etc..) it's state in \nthe database will stay `running` forever. \nNew jobs with the same name will be blocked from execution.\n\nHowever, enabling `ping` mode will make sure, that if a job (in database) who's `duration` is \nyet undefined or larger than the `ping_interval` can be considered stopped. \n\nTo set those dangling job's database state to `vanished` use: \n```bash\n./manage.py joblog_cleanup\n```\nor \n```python\nfrom django_joblog.models import JobLogModel\nJobLogModel.cleanup()\n```\n\n\n# Testing\n\nUnit-tests are [Django-style](https://docs.djangoproject.com/en/2.0/topics/testing/overview/#running-tests) \nand are placed in [django_joblog/tests/](https://github.com/defgsus/django-joblog/blob/master/django_joblog/tests/).\n\nNote that the *parallel* tests will fail with the **Sqlite** backend, because of database-locking.\n\n## The repository\n\n[The repo](https://github.com/defgsus/django-joblog) contains a whole django project (`django_joblog_project`) \nfor ease of development. `setup.py` only exports the `django_joblog` app. \n\nThe default database backend is configured to **MySQL**.\n\nTo start the runserver or run the tests within the repo, open mysql console:\n\n```mysql\nCREATE USER 'django_logs_user'@'localhost' IDENTIFIED BY 'django_logs_pwd';\n\nCREATE DATABASE django_logs_test CHARACTER SET utf8 COLLATE utf8_general_ci;\n\nGRANT ALL ON django_logs_test.* TO 'django_logs_user'@'localhost';\nGRANT ALL ON django_logs_test_test.* TO 'django_logs_user'@'localhost';\n``` \n\nThen alternatively, depending on the python version:\n```bash\npip install MySQL-python # for python 2\n\npip install PyMySQL # for python 3\npip install mysqlclient # or alternatively\n```\n\nAnd finally:\n```bash\n./manage.py test\n\n# or\n./manage.py migrate\n./manage.py runserver\n```\n\n\n## Changelog\n## v0.2.4 - Apr/2019\n\n- add 'blocked' state entry on failed parallel job runs \n\n## v0.2.3 \n\n- fix joblog model access\n\n## v0.2.2 \n\n- add `JOBLOG_CONFIG` setting\n- add `live_updates` and `ping` mode\n\n## v0.1.1 - Aug/2018\n\n- add search and filter to admin view\n\n## v0.1.0 - Jul/2018\n\n- make printing to console optional\n- add django unit-tests\n\n## v0.0.1 \n\n- Copy-pasted together from various private projects, 'sanitized' and repackaged for PyPi", "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/defgsus/django-joblog", "keywords": "django database logging", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-joblog", "package_url": "https://pypi.org/project/django-joblog/", "platform": "", "project_url": "https://pypi.org/project/django-joblog/", "project_urls": { "Homepage": "https://github.com/defgsus/django-joblog" }, "release_url": "https://pypi.org/project/django-joblog/0.2.4/", "requires_dist": null, "requires_python": ">=2.6, <4", "summary": "A generic django-utility that helps to log stuff to the database.", "version": "0.2.4" }, "last_serial": 5204987, "releases": { "0.1.1": [ { "comment_text": "", "digests": { "md5": "f401cf779d3f2cd1d36132b3cbd3d58b", "sha256": "ec29974cd7e1bb0dd786081140b7bab621de072edb0667e4426cf6b8a129816b" }, "downloads": -1, "filename": "django-joblog-0.1.1.tar.gz", "has_sig": false, "md5_digest": "f401cf779d3f2cd1d36132b3cbd3d58b", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 12224, "upload_time": "2018-08-01T15:58:17", "url": "https://files.pythonhosted.org/packages/f7/87/6b829f0848a83efa22f7c184e77070f1259ca814e6686fc48fc2380ad716/django-joblog-0.1.1.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "224f3c570459f5b553d90c8e6a80f077", "sha256": "70e5de3d649398e6bdb32f315933264e9e1593012c654eeaa9a403781fe6860b" }, "downloads": -1, "filename": "django-joblog-0.2.1.tar.gz", "has_sig": false, "md5_digest": "224f3c570459f5b553d90c8e6a80f077", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 17319, "upload_time": "2019-04-15T20:22:16", "url": "https://files.pythonhosted.org/packages/c0/bd/d4afbd70b0dbd249198eb182edaa61df3c775816144f5579c906a07f160a/django-joblog-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "f916d2b7c0ef98e53ce50c1f0591b5de", "sha256": "2183dd81bddcecbbcf813e188f3144892dced8c90677581dbb14a614be9ba060" }, "downloads": -1, "filename": "django-joblog-0.2.2.tar.gz", "has_sig": false, "md5_digest": "f916d2b7c0ef98e53ce50c1f0591b5de", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 17337, "upload_time": "2019-04-15T21:30:38", "url": "https://files.pythonhosted.org/packages/f0/d1/54dfe0d4f79439ee4ba0fd6978254830ddd357186ee989131daf620e14c4/django-joblog-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "a83a058e41601f4607bd27dc506a2bd7", "sha256": "8b669d798f13cf6b5b5b85f20fc2520f91b80bcb61947c9bfd3a5e45e90cd73d" }, "downloads": -1, "filename": "django-joblog-0.2.3.tar.gz", "has_sig": false, "md5_digest": "a83a058e41601f4607bd27dc506a2bd7", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 17428, "upload_time": "2019-04-29T11:09:27", "url": "https://files.pythonhosted.org/packages/c8/56/c685ce988dc5982ce85c41636afd8d5d1f80b56617417e2237505832c376/django-joblog-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "a66f7fbcbf227c2237b4d2f1add10f81", "sha256": "498609e8c3246c259340db7d93b67d5b373cc5da7757e9f456ce3139d7b09271" }, "downloads": -1, "filename": "django-joblog-0.2.4.tar.gz", "has_sig": false, "md5_digest": "a66f7fbcbf227c2237b4d2f1add10f81", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 18199, "upload_time": "2019-04-29T20:54:17", "url": "https://files.pythonhosted.org/packages/bf/cb/e847a6a983d5ee13d91a4795da0f9a93bac1e49b40b9e937c0e31f62d428/django-joblog-0.2.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a66f7fbcbf227c2237b4d2f1add10f81", "sha256": "498609e8c3246c259340db7d93b67d5b373cc5da7757e9f456ce3139d7b09271" }, "downloads": -1, "filename": "django-joblog-0.2.4.tar.gz", "has_sig": false, "md5_digest": "a66f7fbcbf227c2237b4d2f1add10f81", "packagetype": "sdist", "python_version": "source", "requires_python": ">=2.6, <4", "size": 18199, "upload_time": "2019-04-29T20:54:17", "url": "https://files.pythonhosted.org/packages/bf/cb/e847a6a983d5ee13d91a4795da0f9a93bac1e49b40b9e937c0e31f62d428/django-joblog-0.2.4.tar.gz" } ] }