Skip to content

Instantly share code, notes, and snippets.

@grahampugh
Last active October 22, 2019 10:54
Show Gist options
  • Save grahampugh/58c73bb3e46681d3d0d25dbb15126bd1 to your computer and use it in GitHub Desktop.
Save grahampugh/58c73bb3e46681d3d0d25dbb15126bd1 to your computer and use it in GitHub Desktop.
Generate hourly Slack error notifications from JAMFSoftwareServer.log files in a multi-context environment
#!/usr/bin/python
#
# Copyright 2019 Graham Pugh
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This script can be run from a cron tab to analyse Jamf Pro server logs for errors.
This particular version looks for any errors.
If it finds any in any Jamf databases, it reports to Slack.
A Slack webhook URL needs to be provided.
"""
import subprocess
import requests
import socket
from datetime import datetime, date, timedelta
# Set the webhook_url to the one provided by Slack when you create the webhook at https://my.slack.com/services/new/incoming-webhook/
slack_webhook_url = 'https://hooks.slack.com/services/REPLACE-WITH-A-SLACK-WEBHOOK'
# hostname automatically generated
hostname = socket.gethostname()
def report_via_slack(slack_text):
'''Take a message and report it via Slack'''
slack_data = {'text': slack_text, 'username': hostname}
response = requests.post(slack_webhook_url, json=slack_data)
if response.status_code != 200:
raise ValueError('Request to slack returned an error %s, the response '
'is:\n{}'.format(response.status_code, response.text))
def check_logs(path, logname):
'''Check the JSS logs for error messages'''
today = date.today().strftime("%Y-%m-%d")
yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d")
current_hour = datetime.now().strftime("%H")
last_hour = (datetime.now() - timedelta(hours=1)).strftime("%H")
if last_hour == '23':
today = yesterday
# nowdate = '2019-01-21' ## use this for testing only
df = subprocess.Popen(['find', path, '-name', logname, '-exec',
'grep', '-E', '(\[ERROR\]|\[SEVERE\])', '{}', '+'],
stdout=subprocess.PIPE)
alerts = []
for line in df.stdout:
# filter by date
if today in line:
# get the instance name from the path
path, splitline = line.decode().split(":", 1)
splitpath = path.decode().split("/")
instance = splitpath[4]
## Get each error line
error_time = splitline.split()[1].split(',')[0]
error_hour = error_time.split(':')[0]
if error_hour == last_hour:
if '[ERROR]' in line:
error_level = 'ERROR'
elif '[SEVERE]' in line:
error_level = 'SEVERE'
else:
error_level = ''
error = "[{}".format(splitline.split('[')[-1])
alert = [instance, error_time, error_level, error]
if error not in alerts:
alerts.append(alert)
return alerts
def main():
'''Do the main thing'''
path = '/var/log/JSS'
logname = 'JAMFSoftwareServer.log'
alerts = check_logs(path, logname)
slack_text = ''
# send a message for each item in the alerts list
instance_check = ''
for alert in alerts:
instance, error_time, error_level, error = alert
if instance_check != instance:
if instance_check != '':
slack_text += "\n"
slack_text += "*{}:*\n".format(str(instance).upper())
instance_check = instance
if error_level == 'SEVERE':
error = "*[SEVERE] {}*".format(error)
slack_text += ("{0} {1}".format(error_time, error))
if slack_text:
# print(slack_text)
report_via_slack(slack_text)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment