Skip to content
This repository was archived by the owner on Feb 23, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ Imagine you want to take down the server `web01` for maintenance. Just SSH to it
### Configuration

`hacheck` accepts a `-c` flag which should point to a YAML-formatted configuration file. Some notable properties of this file:
* `cache_time`: The duration for which check results may be cached
* `service_name_header`: If set, the name of a header which will be populated with the service name on HTTP checks
* `log_path`: Either the string `"stdout"`, the string `"stderr"`, or a fully-qualified path to a file to write logs to. Uses a [WatchedFileHandler](http://docs.python.org/2/library/logging.handlers.html#watchedfilehandler) and ought to play nicely with logrotate
* `mysql_username`: username to use when logging into mysql for checks
* `mysql_password`: password to use when logging into mysql for checks
* `rlimit_nofile`: set the NOFILE rlimit. If the string "max", will set the rlimit to the hard rlimit; otherwise, will be interpreted as an integer and set to that value.

* `cache_time`: The duration for which check results may be cached
* `service_name_header`: If set, the name of a header which will be populated with the service name on HTTP checks
* `log_path`: Either the string `"stdout"`, the string `"stderr"`, the string "syslog", or a fully-qualified path to a file to write logs to.
* If passed `syslog`, will write datagram messages to `/dev/log`. Can be passed `syslog,address,socktype` to write to other kinds of syslog. For example, `syslog,127.0.0.1:514,dgram` will write UDP to localhost:514; `syslog,/dev/log_stream,stream` will write stream (TCP) to `/dev/log`
* Files use a [WatchedFileHandler](http://docs.python.org/2/library/logging.handlers.html#watchedfilehandler) and ought to play nicely with logrotate
* `mysql_username`: username to use when logging into mysql for checks
* `mysql_password`: password to use when logging into mysql for checks
* `rlimit_nofile`: set the NOFILE rlimit. If the string "max", will set the rlimit to the hard rlimit; otherwise, will be interpreted as an integer and set to that value.

The values in this configuration may contain shell variables, written as ${VARNAME}. These will be expanded at runtime.

### Monitoring

Expand Down
20 changes: 19 additions & 1 deletion hacheck/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import yaml
import os
import re

import six


def max_or_int(some_str_value):
Expand All @@ -23,11 +27,25 @@ def max_or_int(some_str_value):
config[key] = default


def expand_shell_variables(key, value):
if not isinstance(value, six.string_types):
return value

def get_env(vmd):
variable = vmd.group(1)
if variable in os.environ:
return os.environ[variable]
else:
raise KeyError('${0} not in environment (from config {1}={2})'.format(variable, key, value))

return re.sub(r'\$\{([^}]+)\}', get_env, value)


def load_from(path):
with open(path, 'r') as f:
c = yaml.safe_load(f)
for key, value in c.items():
if key in DEFAULTS:
constructor, default = DEFAULTS[key]
config[key] = constructor(value)
config[key] = constructor(expand_shell_variables(key, value))
return config
31 changes: 30 additions & 1 deletion hacheck/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import signal
import time
import sys
import socket
import resource

import tornado.ioloop
Expand Down Expand Up @@ -42,7 +43,7 @@ def get_app():
(r'/recent', handlers.ListRecentHandler),
(r'/status/count', handlers.ServiceCountHandler),
(r'/status', handlers.StatusHandler),
], start_time=time.time(), log_function=log_request)
], start_time=time.time(), log_function=log_request)


def setrlimit_nofile(soft_target):
Expand Down Expand Up @@ -105,6 +106,34 @@ def main():
handler = logging.StreamHandler(sys.stdout)
elif log_path == 'stderr':
handler = logging.StreamHandler(sys.stderr)
elif log_path.startswith('syslog'):
socktype = socket.SOCK_DGRAM
if log_path == 'syslog':
address = '/dev/log'
else:
syslog_address_parts = log_path.split(',')
if len(syslog_address_parts) == 3:
_, syslog_address, socktype = syslog_address_parts
socktypes = {
'stream': socket.SOCK_STREAM,
'dgram': socket.SOCK_DGRAM
}
if socktype not in socktypes:
raise ValueError('Unrecognized socket type {0}'.format(socktype))
socktype = socktypes[socktype]
elif len(syslog_address_parts) == 2:
_, syslog_address = syslog_address_parts
else:
raise ValueError('Unrecognized syslog address format {0}'.format(log_path))
if ':' in syslog_address:
host, port = syslog_address.split(':')
port = int(port)
address = (host, port)
else:
address = syslog_address
handler = logging.handlers.SysLogHandler(
address, facility=logging.handlers.SysLogHandler.LOG_DAEMON, socktype=socktype
)
else:
handler = logging.handlers.WatchedFileHandler(log_path)
fmt = logging.Formatter(logging.BASIC_FORMAT, None)
Expand Down