From 9858beb329e69f6889e6084d566ab7d6f57cfb4b Mon Sep 17 00:00:00 2001 From: Patrick Stadler Date: Sun, 8 Mar 2015 20:37:56 +0100 Subject: [PATCH] structured main routines. implement file reporter --- README.md | 22 ++++----- lib/main.sh | 108 ++++++++++++++++++++++++++++++++++++++++++ lib/utils/helpers.sh | 7 +++ lib/utils/os.sh | 14 +++--- metrics/disk_io.sh | 28 +++++------ metrics/disk_usage.sh | 14 +++--- metrics/memory.sh | 4 +- metrics/network_io.sh | 28 +++++------ metrics/ping.sh | 9 ++++ reporters/file.sh | 24 ++++++++++ reporters/stathat.sh | 2 +- sysmetricsd.sh | 91 +++-------------------------------- 12 files changed, 213 insertions(+), 138 deletions(-) create mode 100644 lib/main.sh create mode 100644 lib/utils/helpers.sh create mode 100644 metrics/ping.sh create mode 100644 reporters/file.sh diff --git a/README.md b/README.md index 01d8c1d..bbb4149 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ ## TODO -- [] README -- [] option parsing -- [] allow multiple reporters -- [] implement log reporter -- [] better docs, including reporters (--docs) -- [] config file support -- [] config file auto-generation -- [] disk_io metric -- [] load custom metrics -- [] async metrics -- [] extended metric labels? -- [] same metric multiple times (e.g. disk_usage for multiple devices) \ No newline at end of file +- README +- option parsing +- allow multiple reporters +- better docs, including reporters (--docs) +- config file support +- config file auto-generation +- load custom metrics +- async metrics +- extended metric labels? +- same metric multiple times (e.g. disk_usage for multiple devices) \ No newline at end of file diff --git a/lib/main.sh b/lib/main.sh new file mode 100644 index 0000000..2fbf7a7 --- /dev/null +++ b/lib/main.sh @@ -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 +} \ No newline at end of file diff --git a/lib/utils/helpers.sh b/lib/utils/helpers.sh new file mode 100644 index 0000000..5814c87 --- /dev/null +++ b/lib/utils/helpers.sh @@ -0,0 +1,7 @@ +is_number () { + [ ! -z "$1" ] && printf '%f' "$1" &>/dev/null +} + +iso_date () { + date -u +"%Y-%m-%dT%H:%M:%SZ" +} \ No newline at end of file diff --git a/lib/utils/os.sh b/lib/utils/os.sh index 155e93b..af3a061 100644 --- a/lib/utils/os.sh +++ b/lib/utils/os.sh @@ -14,11 +14,13 @@ is_linux () { [ $OS_TYPE == 'solaris' ]; } is_bsd () { [ $OS_TYPE == 'bsd']; } is_unknown () { [ $OS_TYPE == 'unknown' ]; } - -make_temp_dir () { - if is_osx; then +# http://unix.stackexchange.com/a/84980/50905 +if is_osx; then + make_temp_dir () { mktemp -d -t 'sysmetrics' - else + } +else + make_temp_dir () { mktemp -d - fi -} \ No newline at end of file + } +fi \ No newline at end of file diff --git a/metrics/disk_io.sh b/metrics/disk_io.sh index 80b1b22..1284bfb 100644 --- a/metrics/disk_io.sh +++ b/metrics/disk_io.sh @@ -1,12 +1,17 @@ #!/bin/sh -if [ -z $DISK_IO_MOUNTPOINT ]; then - if is_osx; then - DISK_IO_MOUNTPOINT="disk0" - else - DISK_IO_MOUNTPOINT="/dev/vda" +init () { + if [ -z $DISK_IO_MOUNTPOINT ]; then + if is_osx; then + DISK_IO_MOUNTPOINT="disk0" + else + DISK_IO_MOUNTPOINT="/dev/vda" + fi fi -fi + readonly __disk_io_fifo=$__TEMP_DIR/disk_io + mkfifo $__disk_io_fifo + __disk_io_bgproc & +} if is_osx; then __disk_io_bgproc () { @@ -20,19 +25,14 @@ else } fi -__disk_io_fifo=$__TEMP_DIR/disk_io - -init () { - __disk_io_bgproc & - mkfifo $__disk_io_fifo -} - collect () { report $(cat $__disk_io_fifo) } terminate () { - rm $__disk_io_fifo + if [ ! -z $__disk_io_fifo ] && [ -f $__disk_io_fifo ]; then + rm $__disk_io_fifo + fi } docs () { diff --git a/metrics/disk_usage.sh b/metrics/disk_usage.sh index a06c7aa..3205124 100644 --- a/metrics/disk_usage.sh +++ b/metrics/disk_usage.sh @@ -1,12 +1,14 @@ #!/bin/sh -if [ -z $DISK_USAGE_MOUNTPOINT ]; then - if is_osx; then - DISK_USAGE_MOUNTPOINT="/dev/disk1" - else - DISK_USAGE_MOUNTPOINT="/dev/vda" +init () { + if [ -z $DISK_USAGE_MOUNTPOINT ]; then + if is_osx; then + DISK_USAGE_MOUNTPOINT="/dev/disk1" + else + DISK_USAGE_MOUNTPOINT="/dev/vda" + fi fi -fi +} collect () { report $(df | awk -v disk_regexp="^$DISK_USAGE_MOUNTPOINT" \ diff --git a/metrics/memory.sh b/metrics/memory.sh index ba85739..234b10b 100644 --- a/metrics/memory.sh +++ b/metrics/memory.sh @@ -1,7 +1,9 @@ #!/bin/sh if is_osx; then - declare -r __memory_os_memsize=$(sysctl -n hw.memsize) + init () { + readonly __memory_os_memsize=$(sysctl -n hw.memsize) + } collect () { report $(vm_stat | awk -v total_memory=$__memory_os_memsize \ diff --git a/metrics/network_io.sh b/metrics/network_io.sh index f45e695..4c2eb75 100644 --- a/metrics/network_io.sh +++ b/metrics/network_io.sh @@ -1,19 +1,14 @@ #!/bin/sh -if [ -z $NETWORK_IO_INTERFACE ]; then - if is_osx; then - NETWORK_IO_INTERFACE="en0" - else - NETWORK_IO_INTERFACE="eth0" +init () { + if [ -z $NETWORK_IO_INTERFACE ]; then + if is_osx; then + NETWORK_IO_INTERFACE="en0" + else + NETWORK_IO_INTERFACE="eth0" + fi fi -fi - -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}' + readonly __network_io_divisor=$[$INTERVAL * 1024] } if is_osx; then @@ -28,9 +23,14 @@ else } fi +__network_io_calc_kBps() { + echo $1 $2 | awk -v divisor=$__network_io_divisor \ + '{printf "%.2f", ($1 - $2) / divisor}' +} + 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 "out" $(__network_io_calc_kBps ${sample[1]} ${__network_io_sample[1]}) fi diff --git a/metrics/ping.sh b/metrics/ping.sh new file mode 100644 index 0000000..98e6e28 --- /dev/null +++ b/metrics/ping.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +collect () { + report 1 +} + +docs () { + echo "Send a simple ping in form of an integer '1'." +} \ No newline at end of file diff --git a/reporters/file.sh b/reporters/file.sh new file mode 100644 index 0000000..dec4f17 --- /dev/null +++ b/reporters/file.sh @@ -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" +} \ No newline at end of file diff --git a/reporters/stathat.sh b/reporters/stathat.sh index 1aec1a7..e584153 100644 --- a/reporters/stathat.sh +++ b/reporters/stathat.sh @@ -3,7 +3,7 @@ report () { METRIC=$1 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 () { diff --git a/sysmetricsd.sh b/sysmetricsd.sh index 79d50c7..eb8e473 100755 --- a/sysmetricsd.sh +++ b/sysmetricsd.sh @@ -2,93 +2,16 @@ # config INTERVAL=1 -REPORTER=stdout - -# load utils -for util in ./lib/utils/*.sh; do source $util; done - -# init -__METRICS=() -__TEMP_DIR=$(make_temp_dir) +REPORTER=file # register trap trap ' - for metric in ${__METRICS[@]}; do - if ! is_function __m_${metric}_terminate; then - continue - fi - __m_${metric}_terminate - done + main_terminate trap - SIGTERM && kill -- -$$ SIGINT SIGTERM EXIT ' SIGINT SIGTERM EXIT -# load reporter -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 - -# 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 \ No newline at end of file +# load and start main routine +source ./lib/main.sh +main_load +main_init +main_collect \ No newline at end of file