Skip to content

Instantly share code, notes, and snippets.

@kristjanvalur
Last active November 13, 2017 16:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kristjanvalur/1715e7df2392134595ebe7d122c51099 to your computer and use it in GitHub Desktop.
Save kristjanvalur/1715e7df2392134595ebe7d122c51099 to your computer and use it in GitHub Desktop.
unattended loggly config file for Amazon linux, such as used by ElasticBeanstalk.
#!/usr/bin/env python
# loggly_eb.py
'''
For configuring loggly on AWD elastic beanstalk instances.
Tested on the following EB configurations:
64 bit Amazon Linux 2017.03 v.2.5.2 running Python 3.4
This is a RHEL-like linux, with rsyslog and no journald.
No checks are made at runtime to test the validity of the account/token or to verify
log delivery. Use the regular 'configure-linux.sh' script interactively for that.
Settings are taken from environment variables, overridable with command line arguments:
- LOGGLY_ACCOUNT - the account name at Loggly
- LOGGLY_TOKEN - the loggly authentication token (a UUID) for submitting data
- LOGGLY_TAGS - a space-separated string of additional tags to apply to the log
- LOGGLY_HOSTNAME - the hostname to report in syslog. If not specified, the default
as reported by gethostname() is used.
- LOGGLY_DOMAIN - domain name appended to hostname. Useful to name a cluster of
virtual hosts.
'''
from __future__ import print_function
import os
import os.path
import sys
import argparse
import subprocess
import shlex
import textwrap
import shutil
import socket
import six
LOGGLY_DISTRIBUTION_ID = '41058'
LOGS_01_HOST = 'logs-01.loggly.com'
LOGGLY_SYSLOG_PORT = 514
# directory location for syslog
RSYSLOG_ETCDIR_CONF = '/etc/rsyslog.d'
# name and location of loggly syslog file
LOGGLY_RSYSLOG_CONFFILE = RSYSLOG_ETCDIR_CONF + '/22-loggly.conf'
# name and location of loggly syslog backup file
LOGGLY_RSYSLOG_CONFFILE_BACKUP = LOGGLY_RSYSLOG_CONFFILE + '.loggly.bk'
# rsyslog service name
RSYSLOG_SERVICE = 'rsyslog'
def main():
# parse args
parser = argparse.ArgumentParser(description='Initialize loggly logging')
parser.add_argument('--account', '-a', default=os.environ.get('LOGGLY_ACCOUNT'), help='The Loggly account name (default from LOGGLY_ACCOUNT)')
parser.add_argument('--token', '-t', default=os.environ.get('LOGGLY_TOKEN'), help='The Loggly authentication token (default from LOGGLY_TOKEN)')
parser.add_argument('--tags', nargs='*', help='Additional tags (default from LOGGLY_TAGS)')
parser.add_argument('--hostname', default=os.environ.get('LOGGLY_HOSTNAME'))
parser.add_argument('--domain', default=os.environ.get('LOGGLY_DOMAIN'))
parser.add_argument('--remove', '-r', action='store_true', help='remove loggly configuration')
args = parser.parse_args()
if args.remove:
unconfigure()
return
if not args.account or not args.token:
# No loggly configuration
print("No Loggly credentials. '%s -h' for help" % parser.prog)
unconfigure(quiet=True)
return
if args.tags is not None:
tags = args.tags
elif os.environ.get('LOGGLY_TAGS'):
tags = shlex.split(os.environ['LOGGLY_TAGS'])
else:
tags = None
config = {
'account': args.account,
'token': args.token,
'tags': tags,
'hostname': args.hostname,
'domain' : args.domain,
}
configure(config)
def configure(config):
log("INFO: Initiating Configure Loggly for Linux.")
# if all the above check passes, write the 22-loggly.conf file
write_rsyslog_conf(config)
# restart rsyslog service
restart_rsyslog()
log("SUCCESS: Linux system successfully configured to send logs via Loggly.")
def write_rsyslog_conf(config):
'''write the contents to 22-loggly.conf file'''
contents = get_loggly_conf(config)
write_script = False
if os.path.exists(LOGGLY_RSYSLOG_CONFFILE):
log("INFO: Loggly rsyslog file %r already exist." % LOGGLY_RSYSLOG_CONFFILE)
with open(LOGGLY_RSYSLOG_CONFFILE, "rb") as fd:
old_contents = fd.read()
if six.b(contents) != old_contents:
log("WARN: Loggly rsyslog file %r content has changed." % LOGGLY_RSYSLOG_CONFFILE)
log("INFO: Going to back up the conf file: %r to %r" % (LOGGLY_RSYSLOG_CONFFILE, LOGGLY_RSYSLOG_CONFFILE_BACKUP))
shutil.move(LOGGLY_RSYSLOG_CONFFILE, LOGGLY_RSYSLOG_CONFFILE_BACKUP)
write_script = True
else:
write_script = True
if write_script:
with open(LOGGLY_RSYSLOG_CONFFILE, "wb") as fd:
fd.write(six.b(contents))
log("INFO: Loggly rsyslog file %r written." % LOGGLY_RSYSLOG_CONFFILE)
def unconfigure(quiet=False):
# in quiet mode, don't log anything if not configured
if quiet and not os.path.exists(LOGGLY_RSYSLOG_CONFFILE):
return
log("INFO: Initiating uninstall Loggly for Linux.")
# remove 22-loggly.conf file
remove_loggly_conf()
# restart rsyslog service
restart_rsyslog()
log("SUCCESS: Uninstalled Loggly configuration from Linux system.")
def remove_loggly_conf():
'''delete 22-loggly.conf file'''
if os.path.exists(LOGGLY_RSYSLOG_CONFFILE):
os.unlink(LOGGLY_RSYSLOG_CONFFILE)
return True
def restart_rsyslog():
'''restart rsyslog'''
log("INFO: Restarting the %s service." % RSYSLOG_SERVICE)
code = subprocess.call(['service', RSYSLOG_SERVICE, 'restart'])
if code:
log("WARNING: %s did not restart gracefully. Please restart %s manually." % (RSYSLOG_SERVICE, RSYSLOG_SERVICE))
def log(msg):
print(msg)
if msg.startswith('ERROR'):
sys.exit(1)
def get_loggly_conf(config):
template = r'''
# -------------------------------------------------------
# Syslog Logging Directives for Loggly (%(account)s.loggly.com)
# -------------------------------------------------------
# Define the template used for sending logs to Loggly. Do not change this format.
$template LogglyFormat,"<%%pri%%>%%protocol-version%% %%timestamp:::date-rfc3339%% %(hostname)s %%app-name%% %%procid%% %%msgid%% [%(token)s@%(distribution_id)s %(tags)s] %%msg%%\n"
$WorkDirectory /var/spool/rsyslog # where to place spool files
$ActionQueueFileName fwdRule1 # unique name prefix for spool files
$ActionQueueMaxDiskSpace 1g # 1gb space limit (use as much as possible)
$ActionQueueSaveOnShutdown on # save messages to disk on shutdown
$ActionQueueType LinkedList # run asynchronously
$ActionResumeRetryCount -1 # infinite retries if host is down
# Send messages to Loggly over TCP using the template.
*.* @@%(logs_host)s:%(logs_port)d;LogglyFormat
# -------------------------------------------------------
'''
# preprocess data
args = dict(config)
if not args.get('hostname') and not args.get('domain'):
# simply use the rsyslog macro
args['hostname'] = '%HOSTNAME%'
else:
# construct a fixed hostname to send
if not args.get('hostname'):
args['hostname'] = socket.gethostname()
if args.get('domain'):
args['hostname'] = args['hostname'].split('.')[0] + '.' + args['domain']
tags = ['Rsyslog']
if args.get('tags'):
tags += args['tags']
args['tags'] = ' '.join((r'tag=\"%s\"' % tag) for tag in tags)
args['distribution_id'] = LOGGLY_DISTRIBUTION_ID
args['logs_host'] = LOGS_01_HOST
args['logs_port'] = LOGGLY_SYSLOG_PORT
return textwrap.dedent(template[1:]) % args
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment