sendlog

Plugins

Overview

In sendlog, the term plugin refers to any section of code that is imported dynamically based on the contents of the configuration file. They allows users to define custom behaviours for alert handling. There are two broad types:

Logs

Log plugins have three components:

Transformers belong to Rules, which belong to LogTypes. This hierarchy is fundamental to the program’s operation, and should be consistently represented in both the configuration file and the plugins themselves.

These parts are represented as classes, and must inherit from the corresponding LogType, Rule and Transformer base classes. A simple template is shown below:

from plugin import LogType, Rule, Transformer

class MyLogType(LogType):

    class __call__(Rule):
        def evaluate(self, line):
            # Return `True` to send the alert, or `False` to drop it.
            return True 

        class MyTransformer(Transformer):
            def __call__(self, line):
                # Do something with line
                return line

Shared Functions

In some cases, you might want to “share” a function between multiple rules or transformers. This is simple to do with multiple inheritance:

from plugin import LogType, Rule, Transformer

class MyTransformerExtension(): # Must not inherit from Transformer
    def my_shared_function(self, line):
        # Do something with line
        return line

class MyLogType(LogType):

    class MyRule(Rule):
        def __call__(self, line):
            return True 

        class MyTransformer(Transformer, MyTransformerExtension):
            def __call__(self, line):
                # Can use methods of MyTransformerExtension
                self.my_shared_function(line)
                return line

    class MyOtherRule(Rule):
        def __call__(self, line):
            return True 

        class MyOtherTransformer(Transformer, MyTransformerExtension):
            def __call__(self, line):
                # Can also use methods of MyTransformerExtension
                self.my_shared_function(line)
                return line
        
        class MyOtherTransformer2(Transformer):
            def __call__(self, line):
                # Has no access to MyTransformerExtension
                return line

Pattern Recognition

sendlog supports basic pattern recognition using properties. You can define an __init__ method to set these.

class MyLogType(LogType):

    class FailedLogin(Rule):

        def __init__(self):
            self.login_attempts = 0

        def __call__(self, line):
            # Check if login failed or succeeded
            if "Failed login" in line:
                self.login_attempts += 1
            elif "Login success" in line:
                self.login_attempts = 0
            # Trigger after 3 failed attempts
            if self.login_attempts >=3:
                trigger = True
            else:
                trigger = False
            return trigger

Channels

Channels are represented by classes with a mandatory __call__ function that acts as an entrypoint for sending the alert. They must inherit from the Endpoint superclass.

An example of an endpoint is shown below:

from plugin import Endpoint
import requests

class Telegram(Endpoint):
    # Explicitly state required variables here
    required_vars = ["chat_id", "token"]

    # Entrypoint method
    def __call__(self, msg):
        url = f"https://api.telegram.org/bot{self.token}/sendMessage"
        params = {"chat_id": self.chat_id, "text": msg}
        response = requests.post(url, params=params,)
        if response.status_code == 200:
            print(f"{msg} -> {self}")

Next: Configuration file