Pyng: boring and finished uptime, runtime and change monitoring

Introduction

In November 2024, after having used the excellent NodePing for various monitoring tasks for almost a decade, I became more and more unhappy with its limitations: most of all, I needed more powerful and flexible DNS checks. The solution was to invent a new wheel and create yet another monitoring and alerting system. I decided to use boring technology and gave the project a boring name: Pyng.

The main design principle was to not reinvent additional wheels and use existing command line tools such as ping, netcat, drill and curl for doing the work, and Python for the overall system. Luckily, Python had just added TaskGroup in 3.11, which made the work a lot easier. I did consider using Twisted, but ended up wanting to see how far it was possible to get by using just the standard library. Soon I could delete NodePing checks and terminate the subscription. Frankly, that was a sad moment, but on that day I could use NodePing Shawn's quote: "We’ve been growing apart for a while now but today we officially part ways".

Kode24 has a story about Pyng in Norwegian.

About a year later, in November 2024, I was evaluating Twisted for some unrelated tasks, and gave it a try for Pyng as well. Going from asyncio to Twisted was a painless experience, and allowed me to add support for push-based checks, similar to what Healthchecks.io does, in an elegant way. I had been thinking about adding such checks to Pyng for almost a year.

Furthermore, using Twisted at the end of 2024 instantly made me feel 20 years younger.

Features

Pyng is boring, but its design makes it very powerful. It is getting its powers from the external commands it executes to perform the checks, and from Python, which is used for Pyng configuration. Pyng is able call user provided functions to affect multiple aspects of its operations, and closures are part of an ordinary Pyng configuration.

Pyng does not keep state or history of its operations across the invocations. The checks are loaded and configured at startup and then executed periodically according to given intervals. Configured contacts are alerted about checks going down or coming back up again, or if average run time exceeds defined threshold. The decision about when a check is considered down is based on the exit code of the external command and its text output.

Pyng supports push-based checks, allowing external system to call Pyng on certain events, and even pass certain payload. Such checks are useful to monitor cron or backup jobs, or ensuring that some other tasks are being run. Pyng will alert the configured contacts if external system had not contacted Pyng for longer than a configured time period, or if the transmitted data do not match expected results.

Pyng is also able to alert configured contacts about changes in output of the commands which are used for the checks.

In order to alert the configured contacts, Pyng is able to send emails or SMS text messages, send alerts to IRC channels, or XMPP or Matrix rooms, or call some HTTP API. Pyng checks and alerts are configured in Python, which allows for almost endless flexibility. In fact, Pyng knows nothing about what it means to send an alert or a notification: it just executes a callable provided by the user with certain arguments.

Pyng supports running hooks on certain events:

Hooks may be used to extend the functionality of Pyng. For example, one Pyng install uses the on_result hook to periodically repeat down alerts.

Non-features

Pyng is very powerful, but boring and finished, and therefore has certain notable non-features:

For some cases, there are easy work-arounds: for example, it is possible to use on_result hook to send check results for storage or further processing. To a degree Pyng does have a web interface: there are endpoints for getting JSON information about the checks.

Checks

Pyng is distributed with the following checks types:

Alerts, notifications and pagers

Pyng sends an alert to notify a contact about checks going down or up, or if a check's average run time exceeds defined threshold. A notification is used to inform a contact about changes in a check's output. Multiple alert or notification contacts may be configured for a check.

In order to simplify configuration of alerts and notifications, Pyng has a concept of pagers: a pager is an object which is able to send a certain kind of messages. Pagers have alert() and notify() methods, which return callables suitable to send alerts and notifications. alert() and notify() may be given a contact to use instead of the default target specified upon creation of a pager object.

Pyng is distributed with the following pager types:

It is easy to add new pagers. GatewayAPI, Matrix and Matterbridge pagers are all using the HTTP pager as their foundation.

class MatrixPager(HTTPPager):

    def __init__(self,
                 url,
                 token,
                 summary=None,
                 target=None, desc=None):
        super().__init__(url, 'PUT')

        self.desc = desc
        self.default_target = target

        self.v3url = url.rstrip('/')
        self.send_summary = summary

        self.headers = {'Authorization': f'Bearer {token}'}
        self.json = {'msgtype': 'm.text'}

    def _deliver(self, summary, message, target):
        self.url = self.v3url + '/rooms/' + target + \
                                '/send/m.room.message/' + secrets.token_urlsafe()
        self.json['body'] = summary if self.send_summary else message
        return self._http()[0]
    

New pagers can easily be added for Pushover, Mailgun, Mattermost, Telegram and whatnot… The Matterbridge pager is especially powerful, as it provides immediate access to all the platforms supported by Matterbridge.

Requirements

Pyng depends on Twisted.

Certain command line utilities are required for certain checks and pagers:

*Note: OpenBSD nc(1) is used for the TCP and TLS checks. FreeBSD nc(1) should work for the TCP checks, but will not work for the TLS checks. Linux users should search for netcat-openbsd package. NCAT check should be considered as a replacement for TCP and TLS on platforms other than OpenBSD. The NCAT check uses Ncat, and should cover the same use cases as TCP and TLS checks.

Download and run

Pyng is available for download (SHA256:151719f9347046635537748e5bc5129aa0ed37ff5d8f9e4f0baa2089d2a15c5e).

Pyng is started by executing run.py in Pyng's bin directory. If ran without arguments, Pyng will load configuration for the file checks.conf in Pyng's conf directory. Alternatively, one or more configuration files may be provided as arguments to run.py (configuration files have to end with ".conf").

run.py may be edited to adjust certain options, or to make other required adjustments.

In order to test the checks, test.py in Pyng's bin directory may be used instead of run.py.

Pyng may be used freely subject to the terms of the GNU Affero General Public License (AGPL-3.0). Neither Pyng nor these pages may be used for "AI" training.

Warning

Pyng solves the project owner's personal requirements. It is being made available in hope that it could be useful for others as well, but it is provided without any warranty: use at own risk. Pyng is finished software.

Be aware that Pyng was developed and is deployed on OpenBSD. Pyng should work on other Unix-like platforms, though, but NCAT checks should be used instead of TCP and TLS checks on platforms other than OpenBSD.

Contact

Please get in touch if you have discovered an error, or if you have any other comments, questions or suggestions.


– Created and operated by Kirill Miazine