fix: Improve log fetching by filtering timestamps

This commit is contained in:
2026-09-06 13:11:31 +02:00
parent a3402c85d6
commit f4c2628e0c
6 changed files with 82 additions and 10 deletions
+18 -3
View File
@@ -26,12 +26,19 @@
"name": "parseable", "name": "parseable",
"filters": { "filters": {
"labels": { "labels": {
"container_name": "/openssh-server" "container_name": "openssh-server"
}, },
"text": "Accepted" "text": "Accepted",
"timestamp": "date"
} }
}, },
"filters": [ "filters": [
{
"type": "timestamp",
"config": {
"source-field": "date"
}
},
{ {
"type": "regexp", "type": "regexp",
"config": { "config": {
@@ -47,7 +54,7 @@
], ],
"alerter": { "alerter": {
"name": "gotify", "name": "gotify",
"title": "Outside SSH login", "title": "SSH login from {country}",
"message": "New SSH login for {username} on {hostname} from ip {ip} (country: {country}, provider: {isp}, method: {method})" "message": "New SSH login for {username} on {hostname} from ip {ip} (country: {country}, provider: {isp}, method: {method})"
} }
}, },
@@ -64,6 +71,14 @@
} }
}, },
"filters": [ "filters": [
{
"type": "timestamp",
"config": {
"source-field": "timestamp",
"timezone": "Europe/Paris",
"timestamp-format": "%b %d %H:%M:%S"
}
},
{ {
"type": "regexp", "type": "regexp",
"config": { "config": {
+1 -1
View File
@@ -46,7 +46,7 @@
"type": "object", "type": "object",
"required": ["type", "config"], "required": ["type", "config"],
"properties": { "properties": {
"type": { "type": "string", "enum": ["regexp", "geolocation"] }, "type": { "type": "string", "enum": ["geolocation", "regexp", "timestamp"] },
"config": { "config": {
"type": "object" "type": "object"
} }
+8
View File
@@ -41,6 +41,10 @@ class ParseableLogFetcher(LogFetcher):
if labelNum > 0: if labelNum > 0:
query += ' AND ' query += ' AND '
query += f'log LIKE \'%{filters["text"]}%\'' query += f'log LIKE \'%{filters["text"]}%\''
if "timestamp" in filters:
if labelNum > 0:
query += ' AND '
query += f'{filters["timestamp"]} >= {start_time} AND {filters["timestamp"]} < {end_time}'
payload = { payload = {
"query": query, "query": query,
"startTime": datetime.datetime.fromtimestamp(start_time).strftime("%Y-%m-%dT%H:%M:%SZ"), "startTime": datetime.datetime.fromtimestamp(start_time).strftime("%Y-%m-%dT%H:%M:%SZ"),
@@ -57,9 +61,13 @@ class ParseableLogFetcher(LogFetcher):
if timestamp >= start_time and timestamp < end_time: if timestamp >= start_time and timestamp < end_time:
logs.append({ logs.append({
"timestamp": item.get("p_timestamp"), "timestamp": item.get("p_timestamp"),
"fetcher-start-time": start_time,
"fetcher-end-time": end_time,
"log": item.get("log"), "log": item.get("log"),
"labels": {k: v for k, v in item.items() if k not in ["p_timestamp", "log"]} "labels": {k: v for k, v in item.items() if k not in ["p_timestamp", "log"]}
}) })
logger.debug(f"Fetched log: {logs[-1]}")
logger.debug(f"Fetched {len(logs)} logs from Parseable between {start_time} and {end_time}")
return logs return logs
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"Error fetching logs from Parseable: {e}") logger.error(f"Error fetching logs from Parseable: {e}")
+3 -2
View File
@@ -1,7 +1,8 @@
"""Filters package for log-alert.""" """Filters package for log-alert."""
from .base import Filter from .base import Filter
from .regexp import RegexpFilter
from .geolocation import GeolocationFilter from .geolocation import GeolocationFilter
from .regexp import RegexpFilter
from .timestamp import TimestampFilter
__all__ = ["Filter", "RegexpFilter", "GeolocationFilter"] __all__ = ["Filter", "GeolocationFilter", "TimestampFilter", "RegexpFilter"]
+46
View File
@@ -0,0 +1,46 @@
"""timestamp filter implementation."""
import logging
import requests
import datetime
from typing import Dict, Any, Optional
from zoneinfo import ZoneInfo
from .base import Filter
logger = logging.getLogger("log-alert")
class TimestampFilter(Filter):
"""Concrete implementation for Timestamp filter."""
def __init__(self, config: Dict[str, Any]):
self.source_field = config["source-field"]
self.timezone = config.get("timezone", "UTC")
self.timestamp_format = config.get("timestamp-format")
def filter(self, log: Dict[str, Any]) -> Optional[Dict[str, Any]]:
timestamp_string = log.get("labels", {}).get(self.source_field)
if timestamp_string is None:
logger.warning(f"No timestamp found in log for field {self.source_field}")
return None
else:
try:
if self.timestamp_format:
logger.debug(f"Parsing timestamp {timestamp_string} with format {self.timestamp_format}")
parsed_time = datetime.datetime.strptime(timestamp_string, self.timestamp_format)
if parsed_time.year == 1900:
# If the year is not specified, assume the current year
parsed_time = parsed_time.replace(year=datetime.datetime.now().year)
timestamp_value = int(parsed_time.replace(tzinfo=ZoneInfo(self.timezone)).timestamp())
else:
timestamp_value = int(timestamp_string)
except Exception as e:
logger.error(f"Error parsing timestamp {timestamp_value} with format {self.timestamp_format}: {e}")
if timestamp_value >= log.get("fetcher-start-time", 0) and timestamp_value < log.get("fetcher-end-time", float('inf')):
logger.debug(f"Log timestamp {timestamp_value} is within the fetcher time range.")
return log
else:
logger.debug(f"Log timestamp {timestamp_value} is outside the fetcher time range.")
return None
+6 -4
View File
@@ -5,7 +5,7 @@ from typing import Dict, Any, Optional
from .base import AlertRule from .base import AlertRule
from fetchers import LogFetcher from fetchers import LogFetcher
from filters import RegexpFilter, GeolocationFilter from filters import GeolocationFilter, RegexpFilter, TimestampFilter
from alerters import Alerter from alerters import Alerter
logger = logging.getLogger("log-alert") logger = logging.getLogger("log-alert")
@@ -20,10 +20,12 @@ class SimpleAlertRule(AlertRule):
self.check_interval = config.get("check-interval", 60) self.check_interval = config.get("check-interval", 60)
self.filters = [] self.filters = []
for filter in config.get("filters", []): for filter in config.get("filters", []):
if filter["type"] == "regexp": if filter["type"] == "geolocation":
self.filters.append(RegexpFilter(filter["config"]))
elif filter["type"] == "geolocation":
self.filters.append(GeolocationFilter(filter["config"])) self.filters.append(GeolocationFilter(filter["config"]))
elif filter["type"] == "regexp":
self.filters.append(RegexpFilter(filter["config"]))
elif filter["type"] == "timestamp":
self.filters.append(TimestampFilter(filter["config"]))
else: else:
raise ValueError(f"Unsupported filter type: {filter['type']}") raise ValueError(f"Unsupported filter type: {filter['type']}")
self.alerter = alerters[config["alerter"]["name"]] self.alerter = alerters[config["alerter"]["name"]]