Sending alerts to Discord and others from syslog-ng using Apprise: blocks and Python templates

A returning question I get is: “I see, that you can send alerts from syslog-ng to Slack and Telegram, but do you happen to support XYZ?” Replace XYZ with Discord and countless others. So, last week I showed you how to send alerts to Discord using the http() destination of syslog-ng and introduced you to Apprise, a notification library for Python.

From this blog, you can learn how to make your syslog-ng destination developed in Python more flexible using templates, and how to make it easier to use with using blocks. We use the Apprise notification library again. On the feature side, the Python code is a lot closer to being production ready. It has most features a user would want to use. Still, it is rather just an inspiration, not something production ready, as among others, it is missing error handling and reporting.

Before you begin

To use the Python destination, you need to compile syslog-ng yourself on FreeBSD. On RPM distros, install the syslog-ng-python package and on Debian/Ubuntu, syslog-ng-mod-python.

When I wrote this blog, I used the latest syslog-ng version available, which is 3.31.2. Even though any recent syslog-ng version should work from the past two years, so from about 3.20 and probably even earlier, still, I’d recommend using the latest version. Check https://www.syslog-ng.com/products/open-source-log-management/3rd-party-binaries.aspx if up-to-date 3rd-party packages are available for your operating system.

Before using Apprise, you have to install it. On Fedora, it is easy, as it is a part of the distribution:

dnf install apprise

Installation on other operating systems is similarly easy. The major difference is that when you install a Python module using pip, it is not tracked by the package manager of the operating system:

pip3 install apprise

If you want to use Discord, check the first part of this blog for instructions on setting up the service for syslog-ng.

The configuration

This configuration is considerably longer than the one in the previous blog, so here we examine it in smaller parts.

Source

In this case, syslog-ng is listening on TCP port 514 and expects to see RFC5424 formatted syslog messages. This allows you to send extra name-value pairs for testing.

source s_net {
  tcp(port(514) flags(syslog-protocol));
};

Block

The block is the most interesting part of this configuration. It makes configuring syslog-ng easier as it hides away implementation details from the user and makes possible to set default values, where suitable. In this case, setting url() from the configuration is mandatory, syslog-ng cannot guess where you want to send log messages. But title() and body() both have sensible defaults. Title is what appears in the subject line if you send e-mails using Apprise, or the first line in other cases. Body is the rest of the message.

Note, that the Python destination configuration does not include the value-pairs() option here. You cannot use LogTemplate in the Python code and the value-pairs() option together (the send() method passes name-value pairs in a different format).

block destination apprise(
  url()
  title("${PROGRAM}")
  body("${MESSAGE}")
  ...)
{
    python(
        class("AppRise")
        options("url" "`url`" "title" "`title`" "body" "`body`")
    );

};

Destination

As you can see, the destination became a lot shorter. You do not have to know that the given destination uses Python or the class name to use. If you are happy with the defaults for title() and body(), all you have to fill is the url() option. Here we override the title() only to see that it works. Unlike the sample code in the previous blog, the macros used are not hardcoded into the Python code, but you can configure templates in the syslog-ng configuration.

destination d_python_apprise {
  apprise( url("https://discord.com/api/webhooks/xxx/yyy")
    title("${PROGRAM} at ${DATE}")
  );
};

Log statement

Here we send all incoming logs from the network source to the new apprise() destination. In a real-life situation, you want to filter log messages and make sure that only relevant messages are sent to you by syslog-ng as alerts. For example, when someone runs a command as root using sudo.

log {
    source(s_net);
    destination(d_python_apprise);
};

Python block

Finally, here is the Python source code to send alerts to various services using Apprise. Compared to the Python code in the previous blog, it is using Python templates. It makes this destination a lot more flexible, as you can customize both the title (what appears in the Subject or first line of a message) and the body as well.

The init() method creates and configures the Apprise object and the templates for the title and body. When called, the send() method sets the value for the title and body, based on the log message and sends it to the configured service.

As mentioned in the introduction: this code is for inspiration only. There is no error handling or reporting.

python {
import apprise
import syslogng
class AppRise(object):
    def init(self, options):
        print(options)
        self.apobj = apprise.Apprise()
        self.apobj.add(options["url"])
        self.title_template = syslogng.LogTemplate(options["title"],self.template_options)
        self.body_template = syslogng.LogTemplate(options["body"],self.template_options)
        return True
    def send(self, msg):
        self.title = self.title_template.format(msg)
        self.body = self.body_template.format(msg)
        self.apobj.notify(
            body=self.body,
            title=self.title,
        )
        return True
};

Testing

Testing is as easy as:

logger -n 127.0.0.1 -T -P 514 This is a test

And based on the template, you will see a similar message in your Discord client:

root at Apr 19 17:31:27
This is a test

And here is everything together for a better copy & paste experience

Save it with a .conf extension in the /etc/syslog-ng/conf.d/ directory if syslog-ng in your Linux distribution of choice supports it or append the configuration to syslog-ng.conf.

source s_net {
  tcp(port(514) flags(syslog-protocol));
};
block destination apprise(
  url()
  title("${PROGRAM}")
  body("${MESSAGE}")
  ...)
{
    python(
        class("AppRise")
        options("url" "`url`" "title" "`title`" "body" "`body`")
    );

};

destination d_python_apprise {
  apprise( url("https://discord.com/api/webhooks/xxx/yyy")
    title("${PROGRAM} at ${DATE}")
  );
};

log {
    source(s_net);
    destination(d_python_apprise);
};

python {
import apprise
import syslogng
class AppRise(object):
    def init(self, options):
        print(options)
        self.apobj = apprise.Apprise()
        self.apobj.add(options["url"])
        self.title_template = syslogng.LogTemplate(options["title"],self.template_options)
        self.body_template = syslogng.LogTemplate(options["body"],self.template_options)
        return True
    def send(self, msg):
        self.title = self.title_template.format(msg)
        self.body = self.body_template.format(msg)
        self.apobj.notify(
            body=self.body,
            title=self.title,
        )
        return True
};

What is next?

The above code and configuration make a good starting point. It has most features to get you started with sending alerts to a wide variety of online services. If time and my Python knowledge permits, I will add error handling and reporting and submit it as a notification destination for syslog-ng. But you better not wait for me, but implement is yourself!

If you have questions or comments related to syslog-ng, do not hesitate to contact us. You can reach us by email or even chat with us. For a list of possibilities, check our GitHub page under the “Community” section at https://github.com/syslog-ng/syslog-ng. On Twitter, I am available as @Pczanik.

Related Content