From e4a6527be989469ab26307344ac81aa6654d848b Mon Sep 17 00:00:00 2001 From: Woody Yang Date: Fri, 13 Oct 2017 11:53:33 +0800 Subject: [PATCH] Add rsyslog collector --- docs/collectors/RsyslogCollector.md | 35 ++++++++ src/collectors/rsyslog/rsyslog.py | 120 ++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 docs/collectors/RsyslogCollector.md create mode 100755 src/collectors/rsyslog/rsyslog.py diff --git a/docs/collectors/RsyslogCollector.md b/docs/collectors/RsyslogCollector.md new file mode 100644 index 000000000..fb8652b3c --- /dev/null +++ b/docs/collectors/RsyslogCollector.md @@ -0,0 +1,35 @@ + +RsyslogCollector +===== + +Collects stats from rsyslog server with impstats module loaded +Impstats formats are json/json-elasticsearch/cee/legacy, but +only json and legacy formats are supported + +#### Dependencies + * Rsyslog Plugin – impstats (rsyslog 7.5.3+) + (http://www.rsyslog.com/rsyslog-statistic-counter-plugin-impstats/) + (http://www.rsyslog.com/doc/v8-stable/configuration/modules/impstats.html) + +#### Metrics + * [Rsyslog statistic counter ](http://www.rsyslog.com/rsyslog-statistic-counter/) + +#### Options + +Setting | Default | Description | Type +--------|---------|-------------|----- +byte_unit | byte | Default numeric output(s) | str +enabled | False | Enable collecting these metrics | bool +measure_collector_time | False | Collect the collector run time in ms | bool +metrics_blacklist | None | Regex to match metrics to block. Mutually exclusive with metrics_whitelist | NoneType +metrics_whitelist | None | Regex to match metrics to transmit. Mutually exclusive with metrics_blacklist | NoneType +pstats_path | /var/log/rsyslog_stats.log | Path to get syslog stats. | str + +#### Example Output + +``` +__EXAMPLESHERE__ +``` + diff --git a/src/collectors/rsyslog/rsyslog.py b/src/collectors/rsyslog/rsyslog.py new file mode 100755 index 000000000..ad756ff8c --- /dev/null +++ b/src/collectors/rsyslog/rsyslog.py @@ -0,0 +1,120 @@ +# coding=utf-8 + +""" +Collects stats from rsyslog server with impstats module loaded +Impstats formats are json/json-elasticsearch/cee/legacy, but +only json and legacy formats are supported + +#### Dependencies + * Rsyslog Plugin – impstats (rsyslog 7.5.3+) + (http://www.rsyslog.com/rsyslog-statistic-counter-plugin-impstats/) + (http://www.rsyslog.com/doc/v8-stable/configuration/modules/impstats.html) + +#### Metrics + * [Rsyslog statistic counter ] + (http://www.rsyslog.com/rsyslog-statistic-counter/) +""" + +from collections import deque +import re +import diamond.collector +import socket +import time +import json + + +class RsyslogCollector(diamond.collector.Collector): + def get_default_config_help(self): + config_help = super(RsyslogCollector, self).get_default_config_help() + config_help.update({ + 'pstats_path': "Path to get syslog stats.", + }) + return config_help + + def get_default_config(self): + """ + Returns the RsyslogCollector settings + """ + config = super(RsyslogCollector, self).get_default_config() + config.update({ + 'pstats_path': '/var/log/rsyslog_stats.log', + 'path': 'rsyslog' + }) + return config + + def _get_summary(self): + count = 0 + with open(self.config['pstats_path']) as f: + for line in reversed(f.readlines()): + if "global" in line.rstrip(): + if count > 1: + break + count += 1 + elif count > 0: + count += 1 + f.seek(0) + summary_fp = deque(f, count) + return summary_fp + + def legacyformat(self, stat_lines): + self.rsyslog_stats = [] + for line in stat_lines: + metrics = {} + parsed = line.split(': ', 2) + if parsed[1].count("(") > 0: + name = re.sub(r"(^im[ut][dc]p)\(\*?\:?(\d?[\d|\w]?\d)\)", + r'\1_\2', parsed[1]) + else: + name = parsed[1].replace(' ', '_') + _metrics = parsed[2].split(' ', -1) + metrics.update({"name": name}) + for m in _metrics: + if "\n" not in m: + metrics.update({m.split('=', 1)[0]: m.split('=', 1)[1]}) + self.rsyslog_stats.append(metrics) + return self.rsyslog_stats + + def jsonformat(self, stat_lines): + self.rsyslog_stats = [] + for line in stat_lines: + parsed = line.split('rsyslogd-pstats: ', 2) + parsed = json.loads(parsed[1]) + if parsed['name'].count("(") > 0: + parsed['name'] = re.sub( + r"(^im[ut][dc]p)\(\*?\:?(\d?[\d|\w]?\d)\)", + r'\1_\2', parsed['name'] + ) + else: + parsed['name'] = parsed['name'].replace(' ', '_') + self.rsyslog_stats.append(parsed) + return self.rsyslog_stats + + def is_json(self, str): + try: + json_object = json.loads(str) + except ValueError, e: + return False + return True + + def collect(self): + legacy_format = False + stats = self._get_summary() + for line in stats: + parsed = line.split('rsyslogd-pstats: ', 2) + if self.is_json(parsed[1]): + rsyslog_stats = self.jsonformat(stats) + else: + rsyslog_stats = self.legacyformat(stats) + legacy_format = True + break + for metrics in self.rsyslog_stats: + metric_prefix = metrics['name'] + for k in metrics: + if k != 'origin' and k != 'name': + metric_name = metric_prefix + '.' + k + metric_value = metrics[k] + if legacy_format is True: + self.publish(metric_name, metric_value) + else: + if isinstance(metric_value, int): + self.publish(metric_name, metric_value)