Last active
June 30, 2016 12:19
-
-
Save andkirby/0526613849d76e46081d190cb1f0a9e1 to your computer and use it in GitHub Desktop.
Magento Exceptions Log Monitor for Slack.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# This script can send Magento 1.x exception messages to Slack | |
# | |
# This file can be added to the root of a project | |
# Please be sure that you have a file var/log/exception.log in your project | |
# | |
# Run this file in background via command with "&" in the end | |
# $ slack-log-monitor.sh [OPTIONS] & | |
# See more options by command: $ slack-log-monitor.sh --help | |
# Slack settings | |
slack_webhook_url='' | |
# The channel option can be empty if it's defined in webhook | |
slack_channel='' | |
# It uses directory name if it's empty | |
project_name='' | |
# Replace project path in path to file of the thrown exception | |
replace_base_path=1 | |
# Relative path to log file for watching | |
log_file_relative='var/log/exception.log' | |
# Project directory, it will use this file directory if it's empty | |
prj_dir='' | |
# Regular expression for matching line log file stream | |
regular_log_line="^exception '" | |
# Enable parsing message. It will find class name, message, and path to file. | |
parse_message='1' | |
# Log CURL errors | |
log_curl_error='0' | |
# Filter messages by string | |
grep_string='' | |
# Invert grep result | |
grep_invert='0' | |
# Do not post grep string with message | |
no_grep_message='1' | |
# Ignored messages list (regular, use "|" (pipe) as a separator) | |
ignore_messages='' | |
CURRENT_DIR=$(pwd) | |
readonly CURRENT_DIR | |
# Show help | |
function show_me_help { | |
echo "This script can send Magento 1.x exception messages to Slack. | |
Run this file in background via command with '&' in the end: | |
$ bash slack-log-monitor.sh [OPTIONS] & | |
-p, --project-dir DIRECTORY | |
Path to Magento root directory. | |
If it's empty it will use path to this file by default. | |
-m, --project-name NAME | |
Project name, used in the output message. | |
If it's empty it will use project directory name by default. | |
-l, --webhook-url URL | |
Slack service URL. | |
-c, --channel CHANNEL | |
Slack channel name, @username or #group | |
-g, --grep REGEXP | |
Filter messages by regexp. | |
--grep-invert | |
Invert grep match. | |
--no-grep-in-message | |
Do not add filter in Slack message. | |
--ignore-messages-file | |
File which contains message examples (by lines) which should be ignored. | |
E.g. 'Invalid token' - in file | |
Similar message 'Invalid token #234' will be ignored. | |
-f, --log-file FILE | |
Relative path to the 'exception.log' file. | |
Default: var/log/exception.log | |
--use-absolute-path | |
Keep absolute path to files in the output message. | |
-r, --regular-line REGULAR_EXPR | |
Regular expression for matching log line. | |
Not matched lines will be omitted. | |
Default: ^exception ' | |
--not-parse-message | |
Not parse log line but send it as is. | |
-d, --log-curl-errors | |
Log CURL errors into slack_curl.log in a project dir. | |
" | |
} | |
# Parse input params | |
while [[ $# > 0 ]]; do | |
key="$1" | |
case ${key} in | |
-m|--project-name) | |
project_name="$2" | |
shift # past argument | |
;; | |
-p|--project-dir) | |
# Argument PROJECT_DIR | |
# Set path to this file location | |
prj_dir="$2" | |
shift # past argument | |
;; | |
-l|--webhook-url) | |
slack_webhook_url="$2" | |
shift # past argument | |
;; | |
-c|--channel) | |
slack_channel="$2" | |
shift # past argument | |
;; | |
-g|--grep) | |
grep_string="$2" | |
shift # past argument | |
;; | |
--grep-in-message) | |
no_grep_message='0' | |
;; | |
--grep-invert) | |
grep_invert='1' | |
;; | |
-f|--log-file) | |
log_file_relative="$2" | |
shift # past argument | |
;; | |
-i|--ignore-messages-file) | |
ignore_messages_file="$2" | |
if [ ! -f "${ignore_messages_file}" ]; then | |
show_me_help | |
echo "error: File '${ignore_messages_file}' not found." | |
exit 2 | |
fi | |
shift # past argument | |
;; | |
-r|--regular-line) | |
regular_log_line="$2" | |
shift # past argument | |
;; | |
--absolute-path) | |
replace_base_path='0' | |
# no value | |
;; | |
--not-parse-message) | |
parse_message='0' | |
;; | |
-d|--log-curl-errors) | |
log_curl_error='1' | |
;; | |
-h|--help) | |
show_me_help | |
exit | |
;; | |
*) | |
show_me_help | |
echo "error: Unknown option '${key}'" | |
exit 2 | |
;; | |
esac | |
shift # past argument or value | |
done | |
# Code | |
if [ -z "${prj_dir}" ];then | |
PRJ_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd -P)" | |
elif [ ! -d "${prj_dir}" ]; then | |
echo "error: Project directory '${prj_dir}' not found." | |
exit 1 | |
else | |
PRJ_DIR="$( cd "${prj_dir}" && pwd -P)" | |
fi | |
readonly PRJ_DIR | |
# Full path to log file | |
if [[ ${log_file_relative:0:1} == '/' ]]; then | |
# use absolute path | |
log_file=${log_file_relative} | |
else | |
# use path within a project directory | |
log_file=${PRJ_DIR}/${log_file_relative#/} | |
fi | |
# CURL errors log file | |
curl_log_file="$( cd "$( dirname "${log_file}" )" && pwd -P)"'/slack_curl.log' | |
# Check Slack webhook URL | |
if [ -z "${slack_webhook_url}" ]; then | |
show_me_help | |
echo "error: Slack webhook URL cannot be empty." | |
exit 1 | |
fi | |
# Set project name if it's an empty | |
if [ '' == "${project_name}" ]; then | |
# Project name will be taken from a project directory name | |
# it will use real path instead symlink | |
project_name="$( cd ${PRJ_DIR} && basename $(pwd -P))" | |
fi | |
if [ ! -f ${log_file} ]; then | |
echo "error: File '${log_file}' not found." | |
exit 1 | |
fi | |
slack_head_message='File: '${log_file_relative} | |
catch_full_message='0' | |
catch_trace='0' | |
tail -fn0 ${log_file} | \ | |
while read line ; do | |
if [ "${catch_trace}" == '1' ]; then | |
trace=${trace}"\n"${line} | |
if [ ${line} == *'{main}'* ]; then | |
# End line, send a message | |
catch_trace='0' | |
fi | |
else | |
# check ignored messages | |
if [ -n "${ignore_messages_file}" ]; then | |
ignore_messages=$(cat ${ignore_messages_file} | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/|/g') | |
printf "2 ${ignore_messages}" | |
if [ -n "${ignore_messages}" ]; then | |
line=$(echo "${line}" | grep -Ev "(${ignore_messages})" 2>&1) | |
if [ -z "${line}" ]; then | |
continue | |
fi | |
fi | |
fi | |
echo "${line}" | grep -E "${regular_log_line}" 2>&1 | |
if [ $? != 0 ]; then | |
# line doesn't matched | |
continue | |
fi | |
# Apply custom filter | |
if [ -n "${grep_string}" ]; then | |
if [ '1' == "${grep_invert}" ]; then | |
# invert match | |
line=$(echo "${line}" | grep -Ev "${grep_string}" 2>&1) | |
else | |
line=$(echo "${line}" | grep -E "${grep_string}" 2>&1) | |
fi | |
if [ -z "${line}" ]; then | |
# line doesn't matched with filter | |
continue | |
fi | |
fi | |
head_line=${line} | |
# Catch trace strings... | |
if [ "${catch_full_message}" == '1' ]; then | |
catch_trace='1' | |
trace_lines='' | |
fi | |
echo 'Sending...' | |
fi | |
if [ '1' == ${parse_message} ]; then | |
# Parse message | |
exception_class=$(echo ${head_line} | grep -Eo "exception '[^']*'" | sed "s|exception '||" | tr -d "'") | |
exception_message=$(echo ${head_line} | grep -Eo "' with message '.*' in /" | sed "s|' with message '||" | sed "s|' in /||") | |
exception_path='/'$(echo ${head_line} | grep -Eo "' in /.+" | sed "s|' in /||") | |
# Cut project path | |
if [ "${replace_base_path}" == '1' ]; then | |
exception_path=$(echo ${exception_path} | sed "s|${PRJ_DIR}/||") | |
fi | |
message="${exception_message}\n${exception_class}\n${exception_path}\n" | |
else | |
# Show message line as is | |
message=${head_line} | |
# Cut project path | |
if [ "${replace_base_path}" == '1' ]; then | |
message=$(echo ${message} | sed "s|${PRJ_DIR}/||") | |
fi | |
if [ -n "${trace}" ]; then | |
trace=$(echo ${trace} | sed "s|${PRJ_DIR}/||") | |
message="${message}\n${trace}" | |
fi | |
fi | |
# escape quotes | |
message=$(echo ${message} | sed 's|"|\\"|g') | |
message=$(echo ${message} | sed "s|'|\\'|g") | |
# ---- Send message to Slack ---- | |
curl_body='payload={' | |
# Prepare message attachments | |
attachments='{"color": "danger", "text": "'${message}'"}' | |
# Add showing filter string | |
if [ -n "${grep_string}" ] && [ '1' != ${no_grep_message} ]; then | |
grep_string=$(echo ${grep_string} | sed 's|"|\\"|g') | |
grep_string=$(echo ${grep_string} | sed "s|'|\\'|g") | |
attachments+=',{"color": "#439fe0", "text": "Filter: '${grep_string}'"}' | |
fi | |
# Add attachments into cURL body | |
curl_body=${curl_body}'"attachments":['${attachments}'],' | |
curl_body=${curl_body}'"username": "'${project_name}' log",' | |
if [ -n "${slack_channel}" ]; then | |
curl_body=${curl_body}'"channel":"'${slack_channel}'",' | |
fi | |
curl_body=${curl_body}'"text": "'${slack_head_message}'"' | |
curl_body=${curl_body}'}' | |
# Send message to Slack | |
result=$(curl -s --data "${curl_body}" ${slack_webhook_url} 2>&1) | |
if [ "${result}" != 'ok' ]; then | |
echo 'error: Could not make request to slack. Please check your options.' | |
# Log Slack errors | |
if [ "${log_curl_error}" == '1' ]; then | |
err=$(date)" : CURL error" | |
err="${err}\nCURL Response: ${result}" | |
err="${err}\nCURL body: ${curl_body}" | |
err="${err}\nService URL: ${slack_webhook_url}" | |
if [ -n "${slack_channel}" ]; then | |
err="${err}\nChannel: ${slack_channel}" | |
fi | |
exist_log='' | |
if [ -f ${curl_log_file} ]; then | |
exist_log=$(cat ${curl_log_file}) | |
fi | |
printf "${err}\n${exist_log}" > ${curl_log_file} | |
echo 'See log file: '${curl_log_file} | |
fi | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment