Skip to content

Commit 9f54dce

Browse files
authored
Merge pull request #83 from eladamitpxi/statsd_and_multi_imports
Statsd and multi imports
2 parents 92f84f7 + 93309ae commit 9f54dce

File tree

9 files changed

+84
-14
lines changed

9 files changed

+84
-14
lines changed

config.yaml.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ es_port: 9200
3030
# Optional URL prefix for Elasticsearch
3131
#es_url_prefix: elasticsearch
3232

33+
# Optional prefix for statsd metrics
34+
#statsd_instance_tag: elastalert
35+
36+
# Optional statsd host
37+
#statsd_host: dogstatsd
38+
3339
# Connect with TLS to Elasticsearch
3440
#use_ssl: True
3541

docs/source/ruletypes.rst

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ Rule Configuration Cheat Sheet
4040
+--------------------------------------------------------------+ |
4141
| ``es_url_prefix`` (string, no default) | |
4242
+--------------------------------------------------------------+ |
43+
| ``statsd_instance_tag`` (string, no default) | |
44+
+--------------------------------------------------------------+ |
45+
| ``statsd_host`` (string, no default) | |
46+
+--------------------------------------------------------------+ |
4347
| ``es_send_get_body_as`` (string, default "GET") | |
4448
+--------------------------------------------------------------+ |
4549
| ``aggregation`` (time, no default) | |
@@ -247,8 +251,8 @@ import
247251

248252
``import``: If specified includes all the settings from this yaml file. This allows common config options to be shared. Note that imported files that aren't
249253
complete rules should not have a ``.yml`` or ``.yaml`` suffix so that ElastAlert doesn't treat them as rules. Filters in imported files are merged (ANDed)
250-
with any filters in the rule. You can only have one import per rule, though the imported file can import another file, recursively. The filename
251-
can be an absolute path or relative to the rules directory. (Optional, string, no default)
254+
with any filters in the rule. You can only have one import per rule, though the imported file can import another file or multiple files, recursively.
255+
The filename can be an absolute path or relative to the rules directory. (Optional, string or array of strings, no default)
252256

253257
use_ssl
254258
^^^^^^^
@@ -291,6 +295,17 @@ es_url_prefix
291295

292296
``es_url_prefix``: URL prefix for the Elasticsearch endpoint. (Optional, string, no default)
293297

298+
statsd_instance_tag
299+
^^^^^^^^^^^^^^^^^^^
300+
301+
``statsd_instance_tag``: prefix for statsd metrics. (Optional, string, no default)
302+
303+
304+
statsd_host
305+
^^^^^^^^^^^^^
306+
307+
``statsd_host``: statsd host. (Optional, string, no default)
308+
294309
es_send_get_body_as
295310
^^^^^^^^^^^^^^^^^^^
296311

docs/source/running_elastalert.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ Next, open up config.yaml.example. In it, you will find several configuration op
6969

7070
``es_url_prefix``: Optional; URL prefix for the Elasticsearch endpoint.
7171

72+
``statsd_instance_tag``: Optional; prefix for statsd metrics.
73+
74+
``statsd_host``: Optional; statsd host.
75+
7276
``es_send_get_body_as``: Optional; Method for querying Elasticsearch - ``GET``, ``POST`` or ``source``. The default is ``GET``
7377

7478
``writeback_index`` is the name of the index in which ElastAlert will store data. We will create this index later.

elastalert/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
'ES_USERNAME': 'es_username',
2121
'ES_HOST': 'es_host',
2222
'ES_PORT': 'es_port',
23-
'ES_URL_PREFIX': 'es_url_prefix'}
23+
'ES_URL_PREFIX': 'es_url_prefix',
24+
'STATSD_INSTANCE_TAG': 'statsd_instance_tag',
25+
'STATSD_HOST': 'statsd_host'}
2426

2527
env = Env(ES_USE_SSL=bool)
2628

elastalert/elastalert.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from smtplib import SMTP
1717
from smtplib import SMTPException
1818
from socket import error
19+
import statsd
20+
1921

2022
import dateutil.tz
2123
import pytz
@@ -172,6 +174,12 @@ def __init__(self, args):
172174
self.thread_data.num_dupes = 0
173175
self.scheduler = BackgroundScheduler()
174176
self.string_multi_field_name = self.conf.get('string_multi_field_name', False)
177+
self.statsd_instance_tag = self.conf.get('statsd_instance_tag', '')
178+
self.statsd_host = self.conf.get('statsd_host', '')
179+
if self.statsd_host and len(self.statsd_host) > 0:
180+
self.statsd = statsd.StatsClient(host=self.statsd_host, port=8125)
181+
else:
182+
self.statsd = None
175183
self.add_metadata_alert = self.conf.get('add_metadata_alert', False)
176184
self.prometheus_port = self.args.prometheus_port
177185
self.show_disabled_rules = self.conf.get('show_disabled_rules', True)
@@ -1306,6 +1314,25 @@ def handle_rule_execution(self, rule):
13061314
" %s alerts sent" % (rule['name'], old_starttime, pretty_ts(endtime, rule.get('use_local_time')),
13071315
self.thread_data.num_hits, self.thread_data.num_dupes, num_matches,
13081316
self.thread_data.alerts_sent))
1317+
rule_duration = seconds(endtime - rule.get('original_starttime'))
1318+
elastalert_logger.info("%s range %s" % (rule['name'], rule_duration))
1319+
if self.statsd:
1320+
try:
1321+
self.statsd.gauge(
1322+
'query.hits', self.thread_data.num_hits,
1323+
tags={"elastalert_instance": self.statsd_instance_tag, "rule_name": rule['name']})
1324+
self.statsd.gauge(
1325+
'already_seen.hits', self.thread_data.num_dupes,
1326+
tags={"elastalert_instance": self.statsd_instance_tag, "rule_name": rule['name']})
1327+
self.statsd.gauge(
1328+
'query.matches', num_matches,
1329+
tags={"elastalert_instance": self.statsd_instance_tag, "rule_name": rule['name']})
1330+
self.statsd.gauge(
1331+
'query.alerts_sent', self.thread_data.alerts_sent,
1332+
tags={"elastalert_instance": self.statsd_instance_tag, "rule_name": rule['name']})
1333+
except BaseException as e:
1334+
elastalert_logger.error("unable to send metrics:\n%s" % str(e))
1335+
13091336
self.thread_data.alerts_sent = 0
13101337

13111338
if next_run < datetime.datetime.utcnow():

elastalert/loaders.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ def load_yaml(self, filename):
201201
}
202202

203203
self.import_rules.pop(filename, None) # clear `filename` dependency
204+
files_to_import = []
204205
while True:
205206
loaded = self.get_yaml(filename)
206207

@@ -211,14 +212,16 @@ def load_yaml(self, filename):
211212
loaded.update(rule)
212213
rule = loaded
213214
if 'import' in rule:
214-
# Find the path of the next file.
215-
import_filename = self.get_import_rule(rule)
216-
# set dependencies
215+
# add all of the files to load into the load queue
216+
files_to_import += self.get_import_rule(rule)
217+
del (rule['import']) # or we could go on forever!
218+
if len(files_to_import) > 0:
219+
# set the next file to load
220+
next_file_to_import = files_to_import.pop()
217221
rules = self.import_rules.get(filename, [])
218-
rules.append(import_filename)
222+
rules.append(next_file_to_import)
219223
self.import_rules[filename] = rules
220-
filename = import_filename
221-
del (rule['import']) # or we could go on forever!
224+
filename = next_file_to_import
222225
else:
223226
break
224227

@@ -545,10 +548,16 @@ def get_import_rule(self, rule):
545548
:return: Path the import rule
546549
:rtype: str
547550
"""
548-
if os.path.isabs(rule['import']):
549-
return rule['import']
550-
else:
551-
return os.path.join(os.path.dirname(rule['rule_file']), rule['import'])
551+
rule_imports = rule['import']
552+
if type(rule_imports) is str:
553+
rule_imports = [rule_imports]
554+
expanded_imports = []
555+
for rule_import in rule_imports:
556+
if os.path.isabs(rule_import):
557+
expanded_imports.append(rule_import)
558+
else:
559+
expanded_imports.append(os.path.join(os.path.dirname(rule['rule_file']), rule_import))
560+
return expanded_imports
552561

553562
def get_rule_file_hash(self, rule_file):
554563
rule_file_hash = ''

elastalert/schema.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,12 @@ properties:
183183
use_strftime_index: {type: boolean}
184184

185185
# Optional Settings
186-
import: {type: string}
186+
import:
187+
anyOf:
188+
- type: array
189+
items:
190+
type: string
191+
- type: string
187192
aggregation: *timeframe
188193
realert: *timeframe
189194
exponential_realert: *timeframe

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ PyYAML>=5.1
2121
requests>=2.10.0
2222
stomp.py>=4.1.17
2323
texttable>=0.8.8
24+
statsd-tags==3.2.1.post1
2425
twilio>=6.0.0,<6.1
2526
tzlocal<3.0

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'texttable>=0.8.8',
5050
'twilio>=6.0.0,<6.1',
5151
'cffi>=1.11.5',
52+
'statsd-tags==3.2.1.post1',
5253
'tzlocal<3.0'
5354
]
5455
)

0 commit comments

Comments
 (0)