structured main routines. implement file reporter

This commit is contained in:
Patrick Stadler 2015-03-08 20:37:56 +01:00
parent c54c9d73b7
commit 9858beb329
12 changed files with 213 additions and 138 deletions

View File

@ -1,14 +1,12 @@
## TODO ## TODO
- [] README - README
- [] option parsing - option parsing
- [] allow multiple reporters - allow multiple reporters
- [] implement log reporter - better docs, including reporters (--docs)
- [] better docs, including reporters (--docs) - config file support
- [] config file support - config file auto-generation
- [] config file auto-generation - load custom metrics
- [] disk_io metric - async metrics
- [] load custom metrics - extended metric labels?
- [] async metrics - same metric multiple times (e.g. disk_usage for multiple devices)
- [] extended metric labels?
- [] same metric multiple times (e.g. disk_usage for multiple devices)

108
lib/main.sh Normal file
View File

@ -0,0 +1,108 @@
# load utils
for util in ./lib/utils/*.sh; do source $util; done
# init
__METRICS=()
__TEMP_DIR=$(make_temp_dir)
# load reporter
main_load () {
source ./reporters/${REPORTER}.sh
copy_function init __r_${REPORTER}_init
copy_function report __r_${REPORTER}_report
copy_function terminate __r_${REPORTER}_terminate
copy_function docs __r_${REPORTER}_docs
unset -f init report terminate docs
# load metrics
for file in ./metrics/*.sh; do
filename=$(basename $file)
metric=${filename%.*}
# soruce file and copy functions
source $file
copy_function init __m_${metric}_init
copy_function collect __m_${metric}_collect
copy_function terminate __m_${metric}_terminate
copy_function docs __m_${metric}_docs
unset -f init collect terminate docs
# register metric
__METRICS+=($metric)
done
}
main_init () {
# init reporter
if is_function __r_${REPORTER}_init; then
__r_${REPORTER}_init
fi
# init metrics
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_init; then
continue
fi
__m_${metric}_init
done
}
main_docs () {
echo "Available metrics:"
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_docs; then
continue
fi
echo "[$metric]"
__m_${metric}_docs
echo
done
}
main_collect () {
# used by metrics to return results
report () {
local _r_result
if [ -z $2 ]; then
_r_label=$metric
_r_result="$1"
else
_r_label="$metric.$1"
_r_result="$2"
fi
if is_number $_r_result; then
__r_${REPORTER}_report $_r_label $_r_result
fi
}
# collect metrics
while true; do
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_collect; then
continue
fi
__m_${metric}_collect
done
sleep $INTERVAL
done
}
main_terminate () {
# terminate metrics
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_terminate; then
continue
fi
__m_${metric}_terminate
done
# terminate reporter
if is_function __r_${REPORTER}_terminate; then
__r_${REPORTER}_terminate
fi
}

7
lib/utils/helpers.sh Normal file
View File

@ -0,0 +1,7 @@
is_number () {
[ ! -z "$1" ] && printf '%f' "$1" &>/dev/null
}
iso_date () {
date -u +"%Y-%m-%dT%H:%M:%SZ"
}

View File

@ -14,11 +14,13 @@ is_linux () { [ $OS_TYPE == 'solaris' ]; }
is_bsd () { [ $OS_TYPE == 'bsd']; } is_bsd () { [ $OS_TYPE == 'bsd']; }
is_unknown () { [ $OS_TYPE == 'unknown' ]; } is_unknown () { [ $OS_TYPE == 'unknown' ]; }
# http://unix.stackexchange.com/a/84980/50905
make_temp_dir () { if is_osx; then
if is_osx; then make_temp_dir () {
mktemp -d -t 'sysmetrics' mktemp -d -t 'sysmetrics'
else }
else
make_temp_dir () {
mktemp -d mktemp -d
fi }
} fi

View File

@ -1,12 +1,17 @@
#!/bin/sh #!/bin/sh
if [ -z $DISK_IO_MOUNTPOINT ]; then init () {
if is_osx; then if [ -z $DISK_IO_MOUNTPOINT ]; then
DISK_IO_MOUNTPOINT="disk0" if is_osx; then
else DISK_IO_MOUNTPOINT="disk0"
DISK_IO_MOUNTPOINT="/dev/vda" else
DISK_IO_MOUNTPOINT="/dev/vda"
fi
fi fi
fi readonly __disk_io_fifo=$__TEMP_DIR/disk_io
mkfifo $__disk_io_fifo
__disk_io_bgproc &
}
if is_osx; then if is_osx; then
__disk_io_bgproc () { __disk_io_bgproc () {
@ -20,19 +25,14 @@ else
} }
fi fi
__disk_io_fifo=$__TEMP_DIR/disk_io
init () {
__disk_io_bgproc &
mkfifo $__disk_io_fifo
}
collect () { collect () {
report $(cat $__disk_io_fifo) report $(cat $__disk_io_fifo)
} }
terminate () { terminate () {
rm $__disk_io_fifo if [ ! -z $__disk_io_fifo ] && [ -f $__disk_io_fifo ]; then
rm $__disk_io_fifo
fi
} }
docs () { docs () {

View File

@ -1,12 +1,14 @@
#!/bin/sh #!/bin/sh
if [ -z $DISK_USAGE_MOUNTPOINT ]; then init () {
if is_osx; then if [ -z $DISK_USAGE_MOUNTPOINT ]; then
DISK_USAGE_MOUNTPOINT="/dev/disk1" if is_osx; then
else DISK_USAGE_MOUNTPOINT="/dev/disk1"
DISK_USAGE_MOUNTPOINT="/dev/vda" else
DISK_USAGE_MOUNTPOINT="/dev/vda"
fi
fi fi
fi }
collect () { collect () {
report $(df | awk -v disk_regexp="^$DISK_USAGE_MOUNTPOINT" \ report $(df | awk -v disk_regexp="^$DISK_USAGE_MOUNTPOINT" \

View File

@ -1,7 +1,9 @@
#!/bin/sh #!/bin/sh
if is_osx; then if is_osx; then
declare -r __memory_os_memsize=$(sysctl -n hw.memsize) init () {
readonly __memory_os_memsize=$(sysctl -n hw.memsize)
}
collect () { collect () {
report $(vm_stat | awk -v total_memory=$__memory_os_memsize \ report $(vm_stat | awk -v total_memory=$__memory_os_memsize \

View File

@ -1,19 +1,14 @@
#!/bin/sh #!/bin/sh
if [ -z $NETWORK_IO_INTERFACE ]; then init () {
if is_osx; then if [ -z $NETWORK_IO_INTERFACE ]; then
NETWORK_IO_INTERFACE="en0" if is_osx; then
else NETWORK_IO_INTERFACE="en0"
NETWORK_IO_INTERFACE="eth0" else
NETWORK_IO_INTERFACE="eth0"
fi
fi fi
fi readonly __network_io_divisor=$[$INTERVAL * 1024]
declare -r __network_io_divisor=$[$INTERVAL * 1024]
__network_io_sample=(0 0)
__network_io_calc_kBps() {
echo $1 $2 | awk -v divisor=$__network_io_divisor \
'{printf "%.2f", ($1 - $2) / divisor}'
} }
if is_osx; then if is_osx; then
@ -28,9 +23,14 @@ else
} }
fi fi
__network_io_calc_kBps() {
echo $1 $2 | awk -v divisor=$__network_io_divisor \
'{printf "%.2f", ($1 - $2) / divisor}'
}
collect () { collect () {
local sample=( $(__network_io_collect) ) local sample=( $(__network_io_collect) )
if [ ${__network_io_sample[0]} -ne 0 ]; then if [ ! -z $__network_io_sample ]; then
report "in" $(__network_io_calc_kBps ${sample[0]} ${__network_io_sample[0]}) report "in" $(__network_io_calc_kBps ${sample[0]} ${__network_io_sample[0]})
report "out" $(__network_io_calc_kBps ${sample[1]} ${__network_io_sample[1]}) report "out" $(__network_io_calc_kBps ${sample[1]} ${__network_io_sample[1]})
fi fi

9
metrics/ping.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/sh
collect () {
report 1
}
docs () {
echo "Send a simple ping in form of an integer '1'."
}

24
reporters/file.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
report () {
local METRIC=$1
local VALUE=$2
local DATE=$(iso_date)
echo $DATE $METRIC: $VALUE >> $FILE_LOCATION
}
init () {
if [ -z $FILE_LOCATION ]; then
echo "Missing configuration: \$FILE_LOCATION"
return 1
fi
if [ ! -f $FILE_LOCATION ]; then
touch $FILE_LOCATION
fi
}
docs () {
echo "Write to a file or named pipe."
echo "\$FILE_LOCATION=$FILE_LOCATION"
}

View File

@ -3,7 +3,7 @@
report () { report () {
METRIC=$1 METRIC=$1
VALUE=$2 VALUE=$2
curl -d "stat=$METRIC&ezkey=$API_KEY&value=$VALUE" http://api.stathat.com/ez curl -d "stat=$METRIC&ezkey=$STATHAT_API_KEY&value=$VALUE" http://api.stathat.com/ez
} }
docs () { docs () {

View File

@ -2,93 +2,16 @@
# config # config
INTERVAL=1 INTERVAL=1
REPORTER=stdout REPORTER=file
# load utils
for util in ./lib/utils/*.sh; do source $util; done
# init
__METRICS=()
__TEMP_DIR=$(make_temp_dir)
# register trap # register trap
trap ' trap '
for metric in ${__METRICS[@]}; do main_terminate
if ! is_function __m_${metric}_terminate; then
continue
fi
__m_${metric}_terminate
done
trap - SIGTERM && kill -- -$$ SIGINT SIGTERM EXIT trap - SIGTERM && kill -- -$$ SIGINT SIGTERM EXIT
' SIGINT SIGTERM EXIT ' SIGINT SIGTERM EXIT
# load reporter # load and start main routine
source ./reporters/${REPORTER}.sh source ./lib/main.sh
copy_function init __r_${REPORTER}_init main_load
copy_function report __r_${REPORTER}_report main_init
copy_function terminate __r_${REPORTER}_terminate main_collect
copy_function docs __r_${REPORTER}_docs
unset -f init report terminate docs
# load metrics
for file in ./metrics/*.sh; do
filename=$(basename $file)
metric=${filename%.*}
# soruce file and copy functions
source $file
copy_function init __m_${metric}_init
copy_function collect __m_${metric}_collect
copy_function terminate __m_${metric}_terminate
copy_function docs __m_${metric}_docs
unset -f init collect terminate docs
# register metric
__METRICS+=($metric)
done
# init metrics
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_init; then
continue
fi
__m_${metric}_init
done
# print docs for metrics
echo "Available metrics:"
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_docs; then
continue
fi
echo "[$metric]"
__m_${metric}_docs
echo
done
report () {
local result
if [ -z $2 ]; then
label=$metric
result="$1"
else
label="$metric.$1"
result="$2"
fi
__r_${REPORTER}_report $label $result
}
# collect metrics
while true; do
for metric in ${__METRICS[@]}; do
if ! is_function __m_${metric}_collect; then
continue
fi
__m_${metric}_collect
done
sleep $INTERVAL
done