Skip to content

Instantly share code, notes, and snippets.

@gburgett
Last active March 9, 2020 16:10
Show Gist options
  • Save gburgett/0dd50fc8a0caabebae656069d88db487 to your computer and use it in GitHub Desktop.
Save gburgett/0dd50fc8a0caabebae656069d88db487 to your computer and use it in GitHub Desktop.
A utility script to download error instances from the Bugsnag API. Automatically paginates and handles rate limits.
#! /bin/bash
COLOR_NC='\033[0m' # No Color
COLOR_LGREEN='\033[1;32m'
COLOR_GRAY='\033[1;30m'
COLOR_LGRAY='\033[0;37m'
COLOR_YELLOW='\033[1;33m'
COLOR_RED='\033[0;31m'
DIR="$( cd "$( dirname "$0" )" && pwd )"
## *** Utility functions ***
logv() {
[[ -z "$VERBOSE" ]] && return 0;
local msg=$@
[[ -z $BUGSNAG_AUTH_TOKEN ]] || msg=$(echo "$@" | sed "s/$BUGSNAG_AUTH_TOKEN/\*\*\*\*\*/" )
>&2 echo -e "${COLOR_GRAY}$msg${COLOR_NC}" || true
}
logerr() {
>&2 echo -e "${COLOR_RED}$@${COLOR_NC}"
}
warn() {
>&2 echo -e "${COLOR_YELLOW}$@${COLOR_NC}"
}
log() {
>&2 echo -e "${COLOR_LGREEN}$@${COLOR_NC}"
}
panic() {
logerr "$@"
exit -1;
}
curlv() {
logv "curl" "$@"
curl "$@"
}
teev() {
[[ "${VERBOSE}" ]] && tee >(cat 1>&2) || cat
}
execv() {
logv "$@"
"$@"
}
confirm() {
while true; do
if [[ -z "$2" ]]; then
read -p $'\033[1;36m'"$1"' (exec/skip): '$'\033[0m' yn
else
# double confirm - extra dangerous.
read -p $'\033[0;31m'"$1"' (exec/skip): '$'\033[0m' yn
fi
case $yn in
[Ee]* ) return 0;;
[Ss]* ) return 1;;
* ) echo "Please answer 'execute' or 'skip'.";;
esac
done
}
HEADERS_FILE=$(mktemp /tmp/bugsnag-headers.XXXXXX)
api_curl() {
local url=${@: -1};
if [[ ! -z "$url" ]]; then set -- "${@:1:${#}-1}"; fi
resp=$(curlv -s -H "Authorization: token $BUGSNAG_AUTH_TOKEN" -D "$HEADERS_FILE" "$@" \
"$url")
if grep -q 'HTTP/2 429' "$HEADERS_FILE"; then
logv "rate limited! Sleeping for 1 minute"
# rate limit resets every 60 seconds
sleep 60
api_curl "$url" "$@"
else
echo "$resp"
fi
}
paginate() {
local url="$1"
while
resp=$(api_curl "$url")
echo "$resp" | jq -c '.[]'
url=$(grep -Eoi 'link: <[^>]+>' "$HEADERS_FILE" | grep -Eo '(http|https)://[^>]+' | head -n1)
[[ ! -z "$url" ]]
do
continue
done
}
require_environment() {
[[ -z "$BUGSNAG_AUTH_TOKEN" ]] && panic "Please set BUGSNAG_AUTH_TOKEN environment variable or use '-a' flag."
export PROJECT_FOLDER="bugsnag/${BUGSNAG_PROJECT_ID}"
mkdir -p "$PROJECT_FOLDER"
command -v jq >/dev/null 2>&1 || panic "Please install jq"
true
}
# Write your usage
usage() {
echo "$0
Utilities for querying Bugsnag via the API
curl
Curls the API with bugsnag auth and handling rate limit errors.
organizations
Lists all the organizations the current user belongs to.
projects [organization ID]?
Lists all projects and project IDs for the given organization or current user.
events [error ID]
Downloads all events for the given error.
requests [error ID]
Downloads all events and prints the requests in JSON format
Flags:" && \
grep " .)\ #" $0
echo "
Examples:" && \
grep -i "#\ example:" $0 | awk '{$1=""; $2=""; print " "$0}'
}
parse_args() {
OPTIND=1
subcommand=''
local s=$(echo "$1" | tr '[:upper:]' '[:lower:]')
case "$s" in
curl|requests|events|organizations|projects|help|h|\?)
export subcommand=$s
OPTIND=2
;;
esac
# Parse flags
while getopts ":hvtja:p:C" arg; do
case $arg in
v) # verbose mode - debug output
VERBOSE=true
FLAGS="$FLAGS -v"
;;
t) # table output
TABLE=true
FLAGS="$FLAGS -t"
;;
j) # JSON output
TABLE=''
FLAGS="$FLAGS -j"
;;
a) # Bugsnag API Token - overrides env var BUGSNAG_AUTH_TOKEN
export BUGSNAG_AUTH_TOKEN=$OPTARG
;;
p) # Bugsnag project ID - overrides env var BUGSNAG_PROJECT_ID
export BUGSNAG_PROJECT_ID=$OPTARG
;;
C) # Skip cache
export SKIP_CACHE=true
FLAGS="$FLAGS -C"
;;
h | *) # Display help.
usage
exit 0
;;
esac
done
export OPTIND
}
parse_args "$@" && shift $(($OPTIND - 1))
# If they put args before the command like 'bin/bugsnag -s 1xab migrate -y', try parsing again
[[ -z "$subcommand" ]] && parse_args "$@" && shift $(($OPTIND - 1))
logv "$0 '$FLAGS' '$subcommand' $@"
# Example: bugsnag events 5c5a18cd11fa7800181b38bb > events.json
get-events() {
local id="$1"
[[ ! -z "$id" ]] || panic "Please provide error ID"
[[ -z "$BUGSNAG_PROJECT_ID" ]] && panic "Please set BUGSNAG_PROJECT_ID environment variable or use '-p' flag."
found=$(api_curl "https://api.bugsnag.com/projects/$BUGSNAG_PROJECT_ID/errors/$id")
if grep -q 'HTTP/2 404' "$HEADERS_FILE"; then
panic "Cannot find error ID ${id}"
fi
events_count=$(echo "$found" | jq -r ".events")
events_url=$(echo "$found" | jq -r ".events_url")
paginate "$events_url?per_page=100" | \
jq -r '.url' | \
xargs -I{} bash -c "$0 curl $FLAGS \"{}\"" | \
pv -l -s "$events_count" | \
tee "${PROJECT_FOLDER}/events_${id}_$(date '+%Y-%m-%d').json"
}
cached-get-events() {
local id="$1"
if [[ -z "$SKIP_CACHE" && -f "${PROJECT_FOLDER}/events_${id}_$(date '+%Y-%m-%d').json" ]]; then
cat "${PROJECT_FOLDER}/events_${id}_$(date '+%Y-%m-%d').json"
return 0
fi
get-events "$id"
}
get-requests() {
cached-get-events "$@" | \
jq -c '{ id: .id, date: .received_at, error_id: .error_id, httpMethod: .request.httpMethod, url: .request.url, referer: .request.referer, params: .request.params }'
}
list-organizations() {
local table="$TABLE"
if [[ "$1" == '-j' ]]; then table=''; fi
api_curl "https://api.bugsnag.com/user/organizations" |
( [[ "${table}" ]] && jq -r ".[] | \"\\(.id)\t\\(.name)\"" || cat )
}
list-projects() {
local organization_id="$1"
logv "list-projects $1"
if [[ ! -z "$organization_id" ]]; then
api_curl "https://api.bugsnag.com/organizations/${organization_id}/projects" |
( [[ "${TABLE}" ]] && jq -r ".[] | \"\\(.id)\t\\(.name)\"" || cat )
return
fi
list-organizations -j | jq -r '.[].id' | xargs -I{} $0 $FLAGS projects {}
}
set -e
case $subcommand in
curl)
require_environment
api_curl "$@"
;;
events)
require_environment
cached-get-events "$@"
;;
requests)
require_environment
get-requests "$@"
;;
organizations)
require_environment
list-organizations "$@"
;;
projects)
require_environment
list-projects "$@"
;;
help|h|\?)
usage
;;
*)
logerr "Unknown command: '$1'"
usage
exit -1
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment