get rid of some bashisms / clean up. add ping metric. add --metrics cli option
This commit is contained in:
parent
470ed0a828
commit
d018d6ea12
49
README.md
49
README.md
|
@ -1,3 +1,52 @@
|
||||||
|
# metrics.sh
|
||||||
|
|
||||||
|
metrics.sh is a metrics collection and fowarding daemon implemented in portable shell scripts. A simple interface based on hooks allows for writing custom metrics collectors.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: ./metrics.sh [-d] [-h] [-v] [-r reporter] [-i interval]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-r, --reporter <reporter> use specified reporter (default: stdout)
|
||||||
|
-i, --interval <seconds> collect metrics every n seconds (default: 2)
|
||||||
|
-v, --verbose enable verbose mode
|
||||||
|
-d, --docs show documentation
|
||||||
|
-h, --help show this text
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git clone git@github.com:pstadler/metrics.sh.git
|
||||||
|
```
|
||||||
|
|
||||||
|
TODO: /etc/init.d
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
metrics.sh has been tested on Ubuntu and Mac OS X but is supposed to run on most *NIX-line operating systems. Some of the provided metrics require [procfs](http://en.wikipedia.org/wiki/Procfs) to be available (check with `[ -d /proc ] && echo available || echo not available`).
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Metric | Description
|
||||||
|
------------- | -------------
|
||||||
|
cpu | CPU usage in %
|
||||||
|
memory | Memory usage in %
|
||||||
|
swap | Swap usage in %
|
||||||
|
network_io | Network I/O in kB/s
|
||||||
|
disk_io | Disk I/O in MB/s
|
||||||
|
disk_usage | Disk usage in %
|
||||||
|
heartbeat | System heartbeat
|
||||||
|
ping | Check if remote host is reachable
|
||||||
|
|
||||||
|
TODO: how to write custom metrics
|
||||||
|
|
||||||
|
## Reporters
|
||||||
|
|
||||||
|
TODO: how to write custom reporters
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- README
|
- README
|
||||||
|
|
66
lib/main.sh
66
lib/main.sh
|
@ -1,9 +1,13 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
# load utils
|
# load utils
|
||||||
for util in ./lib/utils/*.sh; do source $util; done
|
for util in ./lib/utils/*.sh; do
|
||||||
|
. $util
|
||||||
|
done
|
||||||
|
|
||||||
# init
|
# init
|
||||||
__METRICS=()
|
__AVAILABLE_METRICS=
|
||||||
__REPORTERS=()
|
__AVAILABLE_REPORTERS=
|
||||||
|
|
||||||
main_load () {
|
main_load () {
|
||||||
# load reporter
|
# load reporter
|
||||||
|
@ -12,14 +16,14 @@ main_load () {
|
||||||
local reporter=${filename%.*}
|
local reporter=${filename%.*}
|
||||||
|
|
||||||
# source reporter and copy functions
|
# source reporter and copy functions
|
||||||
source $file
|
. $file
|
||||||
copy_function init __r_${reporter}_init
|
copy_function init __r_${reporter}_init
|
||||||
copy_function report __r_${reporter}_report
|
copy_function report __r_${reporter}_report
|
||||||
copy_function terminate __r_${reporter}_terminate
|
copy_function terminate __r_${reporter}_terminate
|
||||||
copy_function docs __r_${reporter}_docs
|
copy_function docs __r_${reporter}_docs
|
||||||
unset -f init report terminate docs
|
unset -f init report terminate docs
|
||||||
|
|
||||||
__REPORTERS+=($reporter)
|
__AVAILABLE_REPORTERS=$(trim "$__AVAILABLE_REPORTERS $reporter")
|
||||||
done
|
done
|
||||||
|
|
||||||
# load metrics
|
# load metrics
|
||||||
|
@ -28,7 +32,7 @@ main_load () {
|
||||||
local metric=${filename%.*}
|
local metric=${filename%.*}
|
||||||
|
|
||||||
# soruce metric and copy functions
|
# soruce metric and copy functions
|
||||||
source $file
|
. $file
|
||||||
copy_function init __m_${metric}_init
|
copy_function init __m_${metric}_init
|
||||||
copy_function collect __m_${metric}_collect
|
copy_function collect __m_${metric}_collect
|
||||||
copy_function terminate __m_${metric}_terminate
|
copy_function terminate __m_${metric}_terminate
|
||||||
|
@ -36,11 +40,16 @@ main_load () {
|
||||||
unset -f init collect terminate docs
|
unset -f init collect terminate docs
|
||||||
|
|
||||||
# register metric
|
# register metric
|
||||||
__METRICS+=($metric)
|
__AVAILABLE_METRICS=$(trim "$__AVAILABLE_METRICS $metric")
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
main_init () {
|
main_init () {
|
||||||
|
# handle args
|
||||||
|
__METRICS=$(echo $1 | sed 's/,/ /g')
|
||||||
|
__REPORTER=$2
|
||||||
|
|
||||||
|
# create temp dir
|
||||||
TEMP_DIR=$(make_temp_dir)
|
TEMP_DIR=$(make_temp_dir)
|
||||||
|
|
||||||
# register trap
|
# register trap
|
||||||
|
@ -49,13 +58,27 @@ main_init () {
|
||||||
trap - SIGTERM && kill -- -$$ SIGINT SIGTERM EXIT
|
trap - SIGTERM && kill -- -$$ SIGINT SIGTERM EXIT
|
||||||
' SIGINT SIGTERM EXIT
|
' SIGINT SIGTERM EXIT
|
||||||
|
|
||||||
|
# check if reporter exists
|
||||||
|
if ! in_array $__REPORTER "$__AVAILABLE_REPORTERS"; then
|
||||||
|
echo "Error: reporter '$__REPORTER' is not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if metrics exist
|
||||||
|
for metric in $__METRICS; do
|
||||||
|
if ! in_array $metric "$__AVAILABLE_METRICS"; then
|
||||||
|
echo "Error: metric '$metric' is not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# init reporter
|
# init reporter
|
||||||
if is_function __r_${REPORTER}_init; then
|
if is_function __r_${__REPORTER}_init; then
|
||||||
__r_${REPORTER}_init
|
__r_${__REPORTER}_init
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# init metrics
|
# init metrics
|
||||||
for metric in ${__METRICS[@]}; do
|
for metric in $__METRICS; do
|
||||||
if ! is_function __m_${metric}_init; then
|
if ! is_function __m_${metric}_init; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
@ -76,32 +99,33 @@ main_collect () {
|
||||||
_r_result="$2"
|
_r_result="$2"
|
||||||
fi
|
fi
|
||||||
if is_number $_r_result; then
|
if is_number $_r_result; then
|
||||||
__r_${REPORTER}_report $_r_label $_r_result
|
__r_${__REPORTER}_report $_r_label $_r_result
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# collect metrics
|
# collect metrics
|
||||||
for metric in ${__METRICS[@]}; do
|
for metric in $__METRICS; do
|
||||||
if ! is_function __m_${metric}_collect; then
|
if ! is_function __m_${metric}_collect; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fork () {
|
fork () {
|
||||||
__m_${metric}_collect
|
while true; do
|
||||||
sleep $INTERVAL
|
__m_${metric}_collect
|
||||||
fork
|
sleep $INTERVAL
|
||||||
|
done
|
||||||
}
|
}
|
||||||
fork &
|
fork &
|
||||||
unset -f fork
|
unset -f fork
|
||||||
done
|
done
|
||||||
|
|
||||||
# run forever
|
# run forever
|
||||||
tail -f /dev/null # `sleep infinity` is not portable
|
sleep 2147483647 # `sleep infinity` is not portable
|
||||||
}
|
}
|
||||||
|
|
||||||
main_terminate () {
|
main_terminate () {
|
||||||
# terminate metrics
|
# terminate metrics
|
||||||
for metric in ${__METRICS[@]}; do
|
for metric in $__METRICS; do
|
||||||
if ! is_function __m_${metric}_terminate; then
|
if ! is_function __m_${metric}_terminate; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
@ -109,8 +133,8 @@ main_terminate () {
|
||||||
done
|
done
|
||||||
|
|
||||||
# terminate reporter
|
# terminate reporter
|
||||||
if is_function __r_${REPORTER}_terminate; then
|
if is_function __r_${__REPORTER}_terminate; then
|
||||||
__r_${REPORTER}_terminate
|
__r_${__REPORTER}_terminate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# delete temporary directory
|
# delete temporary directory
|
||||||
|
@ -121,7 +145,7 @@ main_terminate () {
|
||||||
|
|
||||||
main_docs () {
|
main_docs () {
|
||||||
echo "# Metrics"
|
echo "# Metrics"
|
||||||
for metric in ${__METRICS[@]}; do
|
for metric in $__AVAILABLE_METRICS; do
|
||||||
if ! is_function __m_${metric}_docs; then
|
if ! is_function __m_${metric}_docs; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
@ -133,7 +157,7 @@ main_docs () {
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "# REPORTERS"
|
echo "# REPORTERS"
|
||||||
for reporter in ${__REPORTERS[@]}; do
|
for reporter in $__AVAILABLE_REPORTERS; do
|
||||||
if ! is_function __r_${reporter}_docs; then
|
if ! is_function __r_${reporter}_docs; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
is_function () {
|
is_function () {
|
||||||
[ "`type -t $1`" == 'function' ]
|
declare -f -F $1 > /dev/null; return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
# http://stackoverflow.com/a/1369211/183097
|
# http://stackoverflow.com/a/1369211/183097
|
||||||
copy_function () {
|
copy_function () {
|
||||||
declare -F $1 > /dev/null || return 1
|
is_function $1 || return 1
|
||||||
eval "$(echo "${2}()"; declare -f ${1} | tail -n +2)"
|
eval "$(echo "${2}()"; declare -f ${1} | tail -n +2)"
|
||||||
}
|
}
|
|
@ -1,7 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
is_number () {
|
is_number () {
|
||||||
[ ! -z "$1" ] && printf '%f' "$1" &>/dev/null
|
[ ! -z "$1" ] && printf '%f' "$1" > /dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
iso_date () {
|
iso_date () {
|
||||||
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
date -u +"%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
}
|
||||||
|
|
||||||
|
in_array () {
|
||||||
|
local item=$1
|
||||||
|
local arr=$2
|
||||||
|
[[ " $2 " = *" $1 "* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
trim () {
|
||||||
|
echo $1 | sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
declare -r OS_TYPE=$(case "$OSTYPE" in
|
readonly OS_TYPE=$(case "$OSTYPE" in
|
||||||
(solaris*) echo solaris;;
|
(solaris*) echo solaris;;
|
||||||
(darwin*) echo osx;;
|
(darwin*) echo osx;;
|
||||||
(linux*) echo linux;;
|
(linux*) echo linux;;
|
||||||
|
@ -8,11 +8,11 @@ declare -r OS_TYPE=$(case "$OSTYPE" in
|
||||||
(*) echo unknown;;
|
(*) echo unknown;;
|
||||||
esac)
|
esac)
|
||||||
|
|
||||||
is_solaris () { [ $OS_TYPE == 'solaris' ]; }
|
is_solaris () { [ $OS_TYPE = 'solaris' ]; }
|
||||||
is_osx () { [ $OS_TYPE == 'osx' ]; }
|
is_osx () { [ $OS_TYPE = 'osx' ]; }
|
||||||
is_linux () { [ $OS_TYPE == 'solaris' ]; }
|
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
|
# http://unix.stackexchange.com/a/84980/50905
|
||||||
if is_osx; then
|
if is_osx; then
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
VERBOSE_MODE=false
|
VERBOSE_MODE=false
|
||||||
|
|
||||||
verbose_on () {
|
verbose_on () {
|
||||||
|
|
31
metrics.sh
31
metrics.sh
|
@ -3,6 +3,7 @@
|
||||||
# config
|
# config
|
||||||
INTERVAL=2
|
INTERVAL=2
|
||||||
REPORTER=stdout
|
REPORTER=stdout
|
||||||
|
METRICS=cpu,disk_io,disk_usage,heartbeat,memory,network_io,swap
|
||||||
|
|
||||||
# env
|
# env
|
||||||
LC_ALL=en_US.UTF-8
|
LC_ALL=en_US.UTF-8
|
||||||
|
@ -15,7 +16,7 @@ opt_docs=false
|
||||||
opt_verbose=false
|
opt_verbose=false
|
||||||
|
|
||||||
usage () {
|
usage () {
|
||||||
echo " Usage: $0 [-d] [-h] [-v] [-r reporter] [-i interval]"
|
echo " Usage: $0 [-d] [-h] [-v] [-m metrics] [-r reporter] [-i interval]"
|
||||||
}
|
}
|
||||||
|
|
||||||
help () {
|
help () {
|
||||||
|
@ -24,16 +25,22 @@ help () {
|
||||||
echo
|
echo
|
||||||
echo " Options: "
|
echo " Options: "
|
||||||
echo
|
echo
|
||||||
echo " -r, --reporter <reporter> use specified reporter (default: stdout)"
|
echo " -m, --metrics <metric1,...> use specified metrics"
|
||||||
echo " -i, --interval <seconds> collect metrics every n seconds (default: 2)"
|
echo " -r, --reporter <reporter> use specified reporter (default: stdout)"
|
||||||
echo " -v, --verbose enable verbose mode"
|
echo " -i, --interval <seconds> collect metrics every n seconds (default: 2)"
|
||||||
echo " -d, --docs show documentation"
|
echo " -v, --verbose enable verbose mode"
|
||||||
echo " -h, --help show this text"
|
echo " -d, --docs show documentation"
|
||||||
|
echo " -h, --help show this text"
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
|
-m|--metrics)
|
||||||
|
shift
|
||||||
|
METRICS=$1
|
||||||
|
;;
|
||||||
|
|
||||||
-r|--reporter)
|
-r|--reporter)
|
||||||
shift
|
shift
|
||||||
REPORTER=$1
|
REPORTER=$1
|
||||||
|
@ -66,9 +73,8 @@ while [ $# -gt 0 ]; do
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
# run
|
# run
|
||||||
source ./lib/main.sh
|
. ./lib/main.sh
|
||||||
|
|
||||||
if [ $opt_verbose = "true" ]; then
|
if [ $opt_verbose = "true" ]; then
|
||||||
verbose_on
|
verbose_on
|
||||||
|
@ -77,16 +83,17 @@ fi
|
||||||
verbose "OS detected: $OS_TYPE"
|
verbose "OS detected: $OS_TYPE"
|
||||||
|
|
||||||
main_load
|
main_load
|
||||||
verbose "Metrics loaded: ${__METRICS[@]}"
|
verbose "Available metrics: $__AVAILABLE_METRICS"
|
||||||
verbose "Reporters loaded: ${REPORTER}"
|
verbose "Available reporters: $__AVAILABLE_REPORTERS"
|
||||||
|
|
||||||
if [ "$opt_docs" = true ]; then
|
if [ "$opt_docs" = true ]; then
|
||||||
main_docs
|
main_docs
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
main_init
|
main_init $METRICS $REPORTER
|
||||||
verbose "Metrics initialized"
|
verbose "Using metrics: $__METRICS"
|
||||||
|
verbose "Using reporter: $__REPORTER"
|
||||||
|
|
||||||
verbose "Collecting metrics every $INTERVAL second(s)"
|
verbose "Collecting metrics every $INTERVAL second(s)"
|
||||||
main_collect
|
main_collect
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
collect () {
|
||||||
|
report 1
|
||||||
|
}
|
||||||
|
|
||||||
|
docs () {
|
||||||
|
echo "Send a simple heartbeat in form of an integer '1'."
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ init () {
|
||||||
NETWORK_IO_INTERFACE="eth0"
|
NETWORK_IO_INTERFACE="eth0"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
readonly __network_io_divisor=$[$INTERVAL * 1024]
|
readonly __network_io_divisor=$(($INTERVAL * 1024))
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_osx; then
|
if is_osx; then
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
init () {
|
||||||
|
if [ -z $PING_REMOTE_HOST ]; then
|
||||||
|
echo "Error: ping metric requires \$PING_REMOTE_HOST to be specified"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
collect () {
|
collect () {
|
||||||
report 1
|
ping -c 1 $PING_REMOTE_HOST > /dev/null 2>&1
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
report 1
|
||||||
|
else
|
||||||
|
report 0
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
docs () {
|
docs () {
|
||||||
echo "Send a simple ping in form of an integer '1'."
|
echo "Check if remote host is reachable by sending a single ping."
|
||||||
|
echo "Reports '1' if ping was successful, '0' if not."
|
||||||
|
echo "\$PING_REMOTE_HOST="
|
||||||
}
|
}
|
|
@ -12,6 +12,7 @@ init () {
|
||||||
|
|
||||||
if [ "$INFLUXDB_SEND_HOSTNAME" = true ]; then
|
if [ "$INFLUXDB_SEND_HOSTNAME" = true ]; then
|
||||||
__influxdb_columns="[\"value\",\"host\"]"
|
__influxdb_columns="[\"value\",\"host\"]"
|
||||||
|
__influxdb_hostname=$(hostname)
|
||||||
else
|
else
|
||||||
__influxdb_columns="[\"value\"]"
|
__influxdb_columns="[\"value\"]"
|
||||||
fi
|
fi
|
||||||
|
@ -22,7 +23,7 @@ report () {
|
||||||
local value=$2
|
local value=$2
|
||||||
local points
|
local points
|
||||||
if [ "$INFLUXDB_SEND_HOSTNAME" = true ]; then
|
if [ "$INFLUXDB_SEND_HOSTNAME" = true ]; then
|
||||||
points="[$value,\"$HOSTNAME\"]"
|
points="[$value,\"$__influxdb_hostname\"]"
|
||||||
else
|
else
|
||||||
points="[$value]"
|
points="[$value]"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -12,7 +12,7 @@ init() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $KEEN_IO_EVENT_COLLECTION ]; then
|
if [ -z $KEEN_IO_EVENT_COLLECTION ]; then
|
||||||
KEEN_IO_EVENT_COLLECTION=$HOSTNAME
|
KEEN_IO_EVENT_COLLECTION=$(hostname)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__keen_io_api_url="https://api.keen.io/3.0"
|
__keen_io_api_url="https://api.keen.io/3.0"
|
||||||
|
@ -33,5 +33,5 @@ docs () {
|
||||||
echo "Send data to Keen IO (https://keen.io)."
|
echo "Send data to Keen IO (https://keen.io)."
|
||||||
echo "\$KEEN_IO_WRITE_KEY=<write_key>"
|
echo "\$KEEN_IO_WRITE_KEY=<write_key>"
|
||||||
echo "\$KEEN_IO_PROJECT_ID=<project_id>"
|
echo "\$KEEN_IO_PROJECT_ID=<project_id>"
|
||||||
echo "\$KEEN_IO_EVENT_COLLECTION=$HOSTNAME"
|
echo "\$KEEN_IO_EVENT_COLLECTION=$(hostname)"
|
||||||
}
|
}
|
Loading…
Reference in New Issue