cysystemd


Namecysystemd JSON
Version 1.6.0 PyPI version JSON
download
home_pagehttp://github.com/mosquito/cysystemd
Summarysystemd wrapper in Cython
upload_time2023-12-04 14:28:11
maintainer
docs_urlNone
authorDmitry Orlov <me@mosquito.su>
requires_python>3.6, <4
licenseApache
keywords systemd python daemon sd_notify cython
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            systemd wrapper in Cython
=========================

.. image:: https://img.shields.io/pypi/v/cysystemd.svg
    :target: https://pypi.python.org/pypi/cysystemd/
    :alt: Latest Version

.. image:: https://img.shields.io/pypi/wheel/cysystemd.svg
    :target: https://pypi.python.org/pypi/cysystemd/

.. image:: https://img.shields.io/pypi/pyversions/cysystemd.svg
    :target: https://pypi.python.org/pypi/cysystemd/

.. image:: https://img.shields.io/pypi/l/cysystemd.svg
    :target: https://pypi.python.org/pypi/cysystemd/


Python systemd wrapper using Cython


.. contents:: Table of contents


Installation
------------

All packages available on
`github releases <https://github.com/mosquito/cysystemd/releases>`_.

Installation from binary wheels
+++++++++++++++++++++++++++++++

* wheels is now available for Python 3.7, 3.8, 3.9, 3.10, 3.11
  for `x86_64` and `arm64`

.. code-block:: bash

   pythonn3.9 -m pip install \
      https://github.com/mosquito/cysystemd/releases/download/1.4.8/cysystemd-1.4.8-cp39-cp39-manylinux2014_x86_64.whl

Installation from sources
+++++++++++++++++++++++++

You **must** install **systemd headers**

For Debian/Ubuntu users:

.. code-block:: bash

    apt install build-essential libsystemd-dev

On older versions of Debian/Ubuntu, you might also need to install:

.. code-block:: bash

    apt install libsystemd-daemon-dev libsystemd-journal-dev

For CentOS/RHEL

.. code-block:: bash

    yum install gcc systemd-devel


And install it from pypi

.. code-block:: bash

    pip install cysystemd


Usage examples
--------------

Writing to journald
+++++++++++++++++++

Logging handler for python logger
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python

    from cysystemd import journal
    import logging
    import uuid

    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger()
    logger.addHandler(journal.JournaldLogHandler())

    try:
        logger.info("Trying to do something")
        raise Exception('foo')
    except:
        logger.exception("Test Exception %s", 1)


systemd daemon notification
~~~~~~~~~~~~~~~~~~~~~~~~~~~


.. code-block:: python

    from cysystemd.daemon import notify, Notification

    # Send READY=1
    notify(Notification.READY)

    # Send status
    notify(Notification.STATUS, "I'm fine.")

    # Send stopping
    notify(Notification.STOPPING)


Write message into systemd journal


.. code-block:: python

    from cysystemd import journal


    journal.write("Hello Lennart")

    # Or send structured data
    journal.send(
        message="Hello Lennart",
        priority=journal.Priority.INFO,
        some_field='some value',
    )


Reading journald
++++++++++++++++

Reading all systemd records
~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python

   from cysystemd.reader import JournalReader, JournalOpenMode

   journal_reader = JournalReader()
   journal_reader.open(JournalOpenMode.SYSTEM)
   journal_reader.seek_head()

   for record in journal_reader:
      print(record.data['MESSAGE'])


Read only cron logs
~~~~~~~~~~~~~~~~~~~

.. _read-only-cron-logs:

.. code-block:: python

   from cysystemd.reader import JournalReader, JournalOpenMode, Rule


   rules = (
      Rule("SYSLOG_IDENTIFIER", "CRON") &
      Rule("_SYSTEMD_UNIT", "crond.service") |
      Rule("_SYSTEMD_UNIT", "cron.service")
   )

   cron_reader = JournalReader()
   cron_reader.open(JournalOpenMode.SYSTEM)
   cron_reader.seek_head()
   cron_reader.add_filter(rules)

   for record in cron_reader:
      print(record.data['MESSAGE'])


Polling records
~~~~~~~~~~~~~~~

.. code-block:: python

   from cysystemd.reader import JournalReader, JournalOpenMode


   reader = JournalReader()
   reader.open(JournalOpenMode.SYSTEM)
   reader.seek_tail()

   poll_timeout = 255

   while True:
      reader.wait(poll_timeout)

      for record in reader:
         print(record.data['MESSAGE'])


journald open modes
~~~~~~~~~~~~~~~~~~~

* CURRENT_USER
* LOCAL_ONLY
* RUNTIME_ONLY
* SYSTEM
* SYSTEM_ONLY


.. code-block:: python

   from cysystemd.reader import JournalReader, JournalOpenMode

   reader = JournalReader()
   reader.open(JournalOpenMode.CURRENT_USER)


journald entry
~~~~~~~~~~~~~~

JournalEntry class has some special properties and methods:

* ``data`` - journal entry content (``dict``)
* ``date`` - entry timestamp (``datetime`` instance)
* ``cursor`` - systemd identification bytes for this entry
* ``boot_id()`` - returns bootid
* ``get_realtime_sec()`` - entry epoch (``float``)
* ``get_realtime_usec()`` - entry epoch (``int`` microseconds)
* ``get_monotonic_sec()`` - entry monotonic time (``float``)
* ``get_monotonic_usec()`` - entry monotonic time (``int`` microseconds)
* ``__getitem__(key)`` - shoutcut for ``entry.data[key]``


journald reader
~~~~~~~~~~~~~~~

JournalReader class has some special properties and methods:

* ``open(flags=JournalOpenMode.CURRENT_USER)`` - opening journald
  with selected mode
* ``open_directory(path)`` - opening journald from path
* ``open_files(*filename)`` - opening journald from files
* ``data_threshold`` - may be used to get or set the data field size threshold
  for data returned by fething entry data.
* ``closed`` - returns True when journal reader closed
* ``locked`` - returns True when journal reader locked
* ``idle`` - returns True when journal reader opened
* ``seek_head`` - move reader pointer to the first entry
* ``seek_tail`` - move reader pointer to the last entry
* ``seek_monotonic_usec`` - seeks to the entry with the specified monotonic
  timestamp, i.e. CLOCK_MONOTONIC. Since monotonic time restarts on every
  reboot a boot ID needs to be specified as well.
* ``seek_realtime_usec`` - seeks to the entry with the specified realtime
  (wallclock) timestamp, i.e. CLOCK_REALTIME. Note that the realtime clock
  is not necessarily monotonic. If a realtime timestamp is ambiguous, it is
  not defined which position is sought to.
* ``seek_cursor`` - seeks to the entry located at the specified cursor
  (see ``JournalEntry.cursor``).
* ``wait(timeout)`` - It will synchronously wait until the journal gets
  changed. The maximum time this call sleeps may be controlled with the
  timeout_usec parameter.
* ``__iter__`` - returns JournalReader object
* ``__next__`` - calls ``next()`` or raise ``StopIteration``
* ``next(skip=0)`` - returns the next ``JournalEntry``. The ``skip``
  parameter skips some entries.
* ``previous(skip=0)`` - returns the previous ``JournalEntry``.
  The ``skip`` parameter skips some entries.
* ``skip_next(skip)`` - skips next entries.
* ``skip_previous(skip)`` - skips next entries.
* ``add_filter(rule)`` - adding filter rule.
  See `read-only-cron-logs`_ as example.
* ``clear_filter`` - reset all filters
* ``fd`` - returns a special file descriptor
* ``events`` - returns ``EPOLL`` events
* ``timeout`` - returns internal timeout
* ``process_events()`` - After each poll() wake-up process_events() needs
  to be called to process events. This call will also indicate what kind of
  change has been detected.
* ``get_catalog()`` - retrieves a message catalog entry for the current
  journal entry. This will look up an entry in the message catalog by using
  the "MESSAGE_ID=" field of the current journal entry. Before returning
  the entry all journal field names in the catalog entry text enclosed in
  "@" will be replaced by the respective field values of the current entry.
  If a field name referenced in the message catalog entry does not exist,
  in the current journal entry, the "@" will be removed, but the field name
  otherwise left untouched.
* ``get_catalog_for_message_id(message_id: UUID)`` - works similar to
  ``get_catalog()`` but the entry is looked up by the specified
  message ID (no open journal context is necessary for this),
  and no field substitution is performed.


Asyncio support
+++++++++++++++

Initial ``asyncio`` support for reading journal asynchronously.

AsyncJournalReader
~~~~~~~~~~~~~~~~~~

Blocking methods were wrapped by threads.
Method ``wait()`` use epoll on journald file descriptor.

.. code-block:: python

   import asyncio
   import json

   from cysystemd.reader import JournalOpenMode
   from cysystemd.async_reader import AsyncJournalReader


   async def main():
       reader = AsyncJournalReader()
       await reader.open(JournalOpenMode.SYSTEM)
       await reader.seek_tail()

       while await reader.wait():
           async for record in reader:
               print(
                   json.dumps(
                       record.data,
                       indent=1,
                       sort_keys=True
                   )
               )

   if __name__ == '__main__':
       asyncio.run(main())



            

Raw data

            {
    "_id": null,
    "home_page": "http://github.com/mosquito/cysystemd",
    "name": "cysystemd",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">3.6, <4",
    "maintainer_email": "",
    "keywords": "systemd,python,daemon,sd_notify,cython",
    "author": "Dmitry Orlov <me@mosquito.su>",
    "author_email": "me@mosquito.su",
    "download_url": "https://files.pythonhosted.org/packages/02/53/352dd6725329b2ed58ec20525e765d273e2ad6b2af76c0a7684b0c03b5d8/cysystemd-1.6.0.tar.gz",
    "platform": "POSIX",
    "description": "systemd wrapper in Cython\n=========================\n\n.. image:: https://img.shields.io/pypi/v/cysystemd.svg\n    :target: https://pypi.python.org/pypi/cysystemd/\n    :alt: Latest Version\n\n.. image:: https://img.shields.io/pypi/wheel/cysystemd.svg\n    :target: https://pypi.python.org/pypi/cysystemd/\n\n.. image:: https://img.shields.io/pypi/pyversions/cysystemd.svg\n    :target: https://pypi.python.org/pypi/cysystemd/\n\n.. image:: https://img.shields.io/pypi/l/cysystemd.svg\n    :target: https://pypi.python.org/pypi/cysystemd/\n\n\nPython systemd wrapper using Cython\n\n\n.. contents:: Table of contents\n\n\nInstallation\n------------\n\nAll packages available on\n`github releases <https://github.com/mosquito/cysystemd/releases>`_.\n\nInstallation from binary wheels\n+++++++++++++++++++++++++++++++\n\n* wheels is now available for Python 3.7, 3.8, 3.9, 3.10, 3.11\n  for `x86_64` and `arm64`\n\n.. code-block:: bash\n\n   pythonn3.9 -m pip install \\\n      https://github.com/mosquito/cysystemd/releases/download/1.4.8/cysystemd-1.4.8-cp39-cp39-manylinux2014_x86_64.whl\n\nInstallation from sources\n+++++++++++++++++++++++++\n\nYou **must** install **systemd headers**\n\nFor Debian/Ubuntu users:\n\n.. code-block:: bash\n\n    apt install build-essential libsystemd-dev\n\nOn older versions of Debian/Ubuntu, you might also need to install:\n\n.. code-block:: bash\n\n    apt install libsystemd-daemon-dev libsystemd-journal-dev\n\nFor CentOS/RHEL\n\n.. code-block:: bash\n\n    yum install gcc systemd-devel\n\n\nAnd install it from pypi\n\n.. code-block:: bash\n\n    pip install cysystemd\n\n\nUsage examples\n--------------\n\nWriting to journald\n+++++++++++++++++++\n\nLogging handler for python logger\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n    from cysystemd import journal\n    import logging\n    import uuid\n\n    logging.basicConfig(level=logging.DEBUG)\n    logger = logging.getLogger()\n    logger.addHandler(journal.JournaldLogHandler())\n\n    try:\n        logger.info(\"Trying to do something\")\n        raise Exception('foo')\n    except:\n        logger.exception(\"Test Exception %s\", 1)\n\n\nsystemd daemon notification\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n.. code-block:: python\n\n    from cysystemd.daemon import notify, Notification\n\n    # Send READY=1\n    notify(Notification.READY)\n\n    # Send status\n    notify(Notification.STATUS, \"I'm fine.\")\n\n    # Send stopping\n    notify(Notification.STOPPING)\n\n\nWrite message into systemd journal\n\n\n.. code-block:: python\n\n    from cysystemd import journal\n\n\n    journal.write(\"Hello Lennart\")\n\n    # Or send structured data\n    journal.send(\n        message=\"Hello Lennart\",\n        priority=journal.Priority.INFO,\n        some_field='some value',\n    )\n\n\nReading journald\n++++++++++++++++\n\nReading all systemd records\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n   from cysystemd.reader import JournalReader, JournalOpenMode\n\n   journal_reader = JournalReader()\n   journal_reader.open(JournalOpenMode.SYSTEM)\n   journal_reader.seek_head()\n\n   for record in journal_reader:\n      print(record.data['MESSAGE'])\n\n\nRead only cron logs\n~~~~~~~~~~~~~~~~~~~\n\n.. _read-only-cron-logs:\n\n.. code-block:: python\n\n   from cysystemd.reader import JournalReader, JournalOpenMode, Rule\n\n\n   rules = (\n      Rule(\"SYSLOG_IDENTIFIER\", \"CRON\") &\n      Rule(\"_SYSTEMD_UNIT\", \"crond.service\") |\n      Rule(\"_SYSTEMD_UNIT\", \"cron.service\")\n   )\n\n   cron_reader = JournalReader()\n   cron_reader.open(JournalOpenMode.SYSTEM)\n   cron_reader.seek_head()\n   cron_reader.add_filter(rules)\n\n   for record in cron_reader:\n      print(record.data['MESSAGE'])\n\n\nPolling records\n~~~~~~~~~~~~~~~\n\n.. code-block:: python\n\n   from cysystemd.reader import JournalReader, JournalOpenMode\n\n\n   reader = JournalReader()\n   reader.open(JournalOpenMode.SYSTEM)\n   reader.seek_tail()\n\n   poll_timeout = 255\n\n   while True:\n      reader.wait(poll_timeout)\n\n      for record in reader:\n         print(record.data['MESSAGE'])\n\n\njournald open modes\n~~~~~~~~~~~~~~~~~~~\n\n* CURRENT_USER\n* LOCAL_ONLY\n* RUNTIME_ONLY\n* SYSTEM\n* SYSTEM_ONLY\n\n\n.. code-block:: python\n\n   from cysystemd.reader import JournalReader, JournalOpenMode\n\n   reader = JournalReader()\n   reader.open(JournalOpenMode.CURRENT_USER)\n\n\njournald entry\n~~~~~~~~~~~~~~\n\nJournalEntry class has some special properties and methods:\n\n* ``data`` - journal entry content (``dict``)\n* ``date`` - entry timestamp (``datetime`` instance)\n* ``cursor`` - systemd identification bytes for this entry\n* ``boot_id()`` - returns bootid\n* ``get_realtime_sec()`` - entry epoch (``float``)\n* ``get_realtime_usec()`` - entry epoch (``int`` microseconds)\n* ``get_monotonic_sec()`` - entry monotonic time (``float``)\n* ``get_monotonic_usec()`` - entry monotonic time (``int`` microseconds)\n* ``__getitem__(key)`` - shoutcut for ``entry.data[key]``\n\n\njournald reader\n~~~~~~~~~~~~~~~\n\nJournalReader class has some special properties and methods:\n\n* ``open(flags=JournalOpenMode.CURRENT_USER)`` - opening journald\n  with selected mode\n* ``open_directory(path)`` - opening journald from path\n* ``open_files(*filename)`` - opening journald from files\n* ``data_threshold`` - may be used to get or set the data field size threshold\n  for data returned by fething entry data.\n* ``closed`` - returns True when journal reader closed\n* ``locked`` - returns True when journal reader locked\n* ``idle`` - returns True when journal reader opened\n* ``seek_head`` - move reader pointer to the first entry\n* ``seek_tail`` - move reader pointer to the last entry\n* ``seek_monotonic_usec`` - seeks to the entry with the specified monotonic\n  timestamp, i.e. CLOCK_MONOTONIC. Since monotonic time restarts on every\n  reboot a boot ID needs to be specified as well.\n* ``seek_realtime_usec`` - seeks to the entry with the specified realtime\n  (wallclock) timestamp, i.e. CLOCK_REALTIME. Note that the realtime clock\n  is not necessarily monotonic. If a realtime timestamp is ambiguous, it is\n  not defined which position is sought to.\n* ``seek_cursor`` - seeks to the entry located at the specified cursor\n  (see ``JournalEntry.cursor``).\n* ``wait(timeout)`` - It will synchronously wait until the journal gets\n  changed. The maximum time this call sleeps may be controlled with the\n  timeout_usec parameter.\n* ``__iter__`` - returns JournalReader object\n* ``__next__`` - calls ``next()`` or raise ``StopIteration``\n* ``next(skip=0)`` - returns the next ``JournalEntry``. The ``skip``\n  parameter skips some entries.\n* ``previous(skip=0)`` - returns the previous ``JournalEntry``.\n  The ``skip`` parameter skips some entries.\n* ``skip_next(skip)`` - skips next entries.\n* ``skip_previous(skip)`` - skips next entries.\n* ``add_filter(rule)`` - adding filter rule.\n  See `read-only-cron-logs`_ as example.\n* ``clear_filter`` - reset all filters\n* ``fd`` - returns a special file descriptor\n* ``events`` - returns ``EPOLL`` events\n* ``timeout`` - returns internal timeout\n* ``process_events()`` - After each poll() wake-up process_events() needs\n  to be called to process events. This call will also indicate what kind of\n  change has been detected.\n* ``get_catalog()`` - retrieves a message catalog entry for the current\n  journal entry. This will look up an entry in the message catalog by using\n  the \"MESSAGE_ID=\" field of the current journal entry. Before returning\n  the entry all journal field names in the catalog entry text enclosed in\n  \"@\" will be replaced by the respective field values of the current entry.\n  If a field name referenced in the message catalog entry does not exist,\n  in the current journal entry, the \"@\" will be removed, but the field name\n  otherwise left untouched.\n* ``get_catalog_for_message_id(message_id: UUID)`` - works similar to\n  ``get_catalog()`` but the entry is looked up by the specified\n  message ID (no open journal context is necessary for this),\n  and no field substitution is performed.\n\n\nAsyncio support\n+++++++++++++++\n\nInitial ``asyncio`` support for reading journal asynchronously.\n\nAsyncJournalReader\n~~~~~~~~~~~~~~~~~~\n\nBlocking methods were wrapped by threads.\nMethod ``wait()`` use epoll on journald file descriptor.\n\n.. code-block:: python\n\n   import asyncio\n   import json\n\n   from cysystemd.reader import JournalOpenMode\n   from cysystemd.async_reader import AsyncJournalReader\n\n\n   async def main():\n       reader = AsyncJournalReader()\n       await reader.open(JournalOpenMode.SYSTEM)\n       await reader.seek_tail()\n\n       while await reader.wait():\n           async for record in reader:\n               print(\n                   json.dumps(\n                       record.data,\n                       indent=1,\n                       sort_keys=True\n                   )\n               )\n\n   if __name__ == '__main__':\n       asyncio.run(main())\n\n\n",
    "bugtrack_url": null,
    "license": "Apache",
    "summary": "systemd wrapper in Cython",
    "version": "1.6.0",
    "project_urls": {
        "Homepage": "http://github.com/mosquito/cysystemd"
    },
    "split_keywords": [
        "systemd",
        "python",
        "daemon",
        "sd_notify",
        "cython"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0253352dd6725329b2ed58ec20525e765d273e2ad6b2af76c0a7684b0c03b5d8",
                "md5": "0c60a93210541e86dd79487f7cd994f9",
                "sha256": "5224dd8fee146de08528bbf685edb177568246c7728bbb548b2257c9a44a2454"
            },
            "downloads": -1,
            "filename": "cysystemd-1.6.0.tar.gz",
            "has_sig": false,
            "md5_digest": "0c60a93210541e86dd79487f7cd994f9",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">3.6, <4",
            "size": 296041,
            "upload_time": "2023-12-04T14:28:11",
            "upload_time_iso_8601": "2023-12-04T14:28:11.916206Z",
            "url": "https://files.pythonhosted.org/packages/02/53/352dd6725329b2ed58ec20525e765d273e2ad6b2af76c0a7684b0c03b5d8/cysystemd-1.6.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-04 14:28:11",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "mosquito",
    "github_project": "cysystemd",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "cysystemd"
}
        
Elapsed time: 0.14625s