charts.d.plugin.in 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: GPL-3.0-or-later
  3. # netdata
  4. # real-time performance and health monitoring, done right!
  5. # (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
  6. # GPL v3+
  7. #
  8. # charts.d.plugin allows easy development of BASH plugins
  9. #
  10. # if you need to run parallel charts.d processes, link this file to a different name
  11. # in the same directory, with a .plugin suffix and netdata will start both of them,
  12. # each will have a different config file and modules configuration directory.
  13. #
  14. export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:@sbindir_POST@"
  15. PROGRAM_FILE="$0"
  16. MODULE_NAME="main"
  17. # -----------------------------------------------------------------------------
  18. # logging
  19. PROGRAM_NAME="$(basename "${0}")"
  20. SHORT_PROGRAM_NAME="${PROGRAM_NAME/.plugin/}"
  21. # these should be the same with syslog() priorities
  22. NDLP_EMERG=0 # system is unusable
  23. NDLP_ALERT=1 # action must be taken immediately
  24. NDLP_CRIT=2 # critical conditions
  25. NDLP_ERR=3 # error conditions
  26. NDLP_WARN=4 # warning conditions
  27. NDLP_NOTICE=5 # normal but significant condition
  28. NDLP_INFO=6 # informational
  29. NDLP_DEBUG=7 # debug-level messages
  30. # the max (numerically) log level we will log
  31. LOG_LEVEL=$NDLP_INFO
  32. set_log_min_priority() {
  33. case "${NETDATA_LOG_LEVEL,,}" in
  34. "emerg" | "emergency")
  35. LOG_LEVEL=$NDLP_EMERG
  36. ;;
  37. "alert")
  38. LOG_LEVEL=$NDLP_ALERT
  39. ;;
  40. "crit" | "critical")
  41. LOG_LEVEL=$NDLP_CRIT
  42. ;;
  43. "err" | "error")
  44. LOG_LEVEL=$NDLP_ERR
  45. ;;
  46. "warn" | "warning")
  47. LOG_LEVEL=$NDLP_WARN
  48. ;;
  49. "notice")
  50. LOG_LEVEL=$NDLP_NOTICE
  51. ;;
  52. "info")
  53. LOG_LEVEL=$NDLP_INFO
  54. ;;
  55. "debug")
  56. LOG_LEVEL=$NDLP_DEBUG
  57. ;;
  58. esac
  59. }
  60. set_log_min_priority
  61. log() {
  62. local level="${1}"
  63. shift 1
  64. [[ -n "$level" && -n "$LOG_LEVEL" && "$level" -gt "$LOG_LEVEL" ]] && return
  65. systemd-cat-native --log-as-netdata --newline="--NEWLINE--" <<EOFLOG
  66. INVOCATION_ID=${NETDATA_INVOCATION_ID}
  67. SYSLOG_IDENTIFIER=${PROGRAM_NAME}
  68. PRIORITY=${level}
  69. THREAD_TAG=charts.d.plugin
  70. ND_LOG_SOURCE=collector
  71. MESSAGE=${MODULE_NAME}: ${*//\\n/--NEWLINE--}
  72. EOFLOG
  73. # AN EMPTY LINE IS NEEDED ABOVE
  74. }
  75. info() {
  76. log "$NDLP_INFO" "${@}"
  77. }
  78. warning() {
  79. log "$NDLP_WARN" "${@}"
  80. }
  81. error() {
  82. log "$NDLP_ERR" "${@}"
  83. }
  84. fatal() {
  85. log "$NDLP_ALERT" "${@}"
  86. echo "DISABLE"
  87. exit 1
  88. }
  89. debug() {
  90. [ "$debug" = "1" ] && log "$NDLP_DEBUG" "${@}"
  91. }
  92. # -----------------------------------------------------------------------------
  93. # check for BASH v4+ (required for associative arrays)
  94. if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
  95. echo >&2 "BASH version 4 or later is required (this is ${BASH_VERSION})."
  96. exit 1
  97. fi
  98. # -----------------------------------------------------------------------------
  99. # create temp dir
  100. debug=0
  101. TMP_DIR=
  102. chartsd_cleanup() {
  103. trap '' EXIT QUIT HUP INT TERM
  104. if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]; then
  105. [ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
  106. rm -rf "$TMP_DIR"
  107. fi
  108. echo "EXIT"
  109. exit 0
  110. }
  111. trap chartsd_cleanup EXIT QUIT HUP INT TERM
  112. if [ $UID = "0" ]; then
  113. TMP_DIR="$(mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
  114. else
  115. TMP_DIR="$(mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
  116. fi
  117. logdate() {
  118. date "+%Y-%m-%d %H:%M:%S"
  119. }
  120. # -----------------------------------------------------------------------------
  121. # check a few commands
  122. require_cmd() {
  123. local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null)
  124. if [ -z "${x}" -o ! -x "${x}" ]; then
  125. warning "command '${1}' is not found in ${PATH}."
  126. eval "${1^^}_CMD=\"\""
  127. return 1
  128. fi
  129. eval "${1^^}_CMD=\"${x}\""
  130. return 0
  131. }
  132. require_cmd date || exit 1
  133. require_cmd sed || exit 1
  134. require_cmd basename || exit 1
  135. require_cmd dirname || exit 1
  136. require_cmd cat || exit 1
  137. require_cmd grep || exit 1
  138. require_cmd egrep || exit 1
  139. require_cmd mktemp || exit 1
  140. require_cmd awk || exit 1
  141. require_cmd timeout || exit 1
  142. require_cmd curl || exit 1
  143. # -----------------------------------------------------------------------------
  144. [ $((BASH_VERSINFO[0])) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade."
  145. info "started from '$PROGRAM_FILE' with options: $*"
  146. # -----------------------------------------------------------------------------
  147. # internal defaults
  148. # netdata exposes a few environment variables for us
  149. [ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
  150. [ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="@configdir_POST@"
  151. [ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="@libconfigdir_POST@"
  152. pluginsd="${NETDATA_PLUGINS_DIR}"
  153. stockconfd="${NETDATA_STOCK_CONFIG_DIR}/${PROGRAM_NAME}"
  154. userconfd="${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}"
  155. olduserconfd="${NETDATA_USER_CONFIG_DIR}"
  156. chartsd="$pluginsd/../charts.d"
  157. minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
  158. update_every=${minimum_update_frequency} # this will be overwritten by the command line
  159. # work around for non BASH shells
  160. charts_create="_create"
  161. charts_update="_update"
  162. charts_check="_check"
  163. charts_underscore="_"
  164. # when making iterations, charts.d can loop more frequently
  165. # to prevent plugins missing iterations.
  166. # this is a percentage relative to update_every to align its
  167. # iterations.
  168. # The minimum is 10%, the maximum 100%.
  169. # So, if update_every is 1 second and time_divisor is 50,
  170. # charts.d will iterate every 500ms.
  171. # Charts will be called to collect data only if the time
  172. # passed since the last time the collected data is equal or
  173. # above their update_every.
  174. time_divisor=50
  175. # number of seconds to run without restart
  176. # after this time, charts.d.plugin will exit
  177. # netdata will restart it
  178. restart_timeout=$((3600 * 4))
  179. # check if the charts.d plugins are using global variables
  180. # they should not.
  181. # It does not currently support BASH v4 arrays, so it is
  182. # disabled
  183. dryrunner=0
  184. # check for timeout command
  185. check_for_timeout=1
  186. # the default enable/disable value for all charts
  187. enable_all_charts="yes"
  188. # -----------------------------------------------------------------------------
  189. # parse parameters
  190. check=0
  191. chart_only=
  192. while [ ! -z "$1" ]; do
  193. if [ "$1" = "check" ]; then
  194. check=1
  195. shift
  196. continue
  197. fi
  198. if [ "$1" = "debug" -o "$1" = "all" ]; then
  199. debug=1
  200. LOG_LEVEL=$NDLP_DEBUG
  201. shift
  202. continue
  203. fi
  204. if [ -f "$chartsd/$1.chart.sh" ]; then
  205. debug=1
  206. LOG_LEVEL=$NDLP_DEBUG
  207. chart_only="$(echo $1.chart.sh | sed "s/\.chart\.sh$//g")"
  208. shift
  209. continue
  210. fi
  211. if [ -f "$chartsd/$1" ]; then
  212. debug=1
  213. LOG_LEVEL=$NDLP_DEBUG
  214. chart_only="$(echo $1 | sed "s/\.chart\.sh$//g")"
  215. shift
  216. continue
  217. fi
  218. # number check
  219. n="$1"
  220. x=$((n))
  221. if [ "$x" = "$n" ]; then
  222. shift
  223. update_every=$x
  224. [ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
  225. continue
  226. fi
  227. fatal "Cannot understand parameter $1. Aborting."
  228. done
  229. # -----------------------------------------------------------------------------
  230. # loop control
  231. # default sleep function
  232. LOOPSLEEPMS_HIGHRES=0
  233. now_ms=
  234. current_time_ms_default() {
  235. now_ms="$(date +'%s')000"
  236. }
  237. current_time_ms="current_time_ms_default"
  238. current_time_ms_accuracy=1
  239. mysleep="sleep"
  240. # if found and included, this file overwrites loopsleepms()
  241. # and current_time_ms() with a high resolution timer function
  242. # for precise looping.
  243. source "$pluginsd/loopsleepms.sh.inc"
  244. [ $? -ne 0 ] && error "Failed to load '$pluginsd/loopsleepms.sh.inc'."
  245. # -----------------------------------------------------------------------------
  246. # load my configuration
  247. for myconfig in "${NETDATA_STOCK_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf" "${NETDATA_USER_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf"; do
  248. if [ -f "$myconfig" ]; then
  249. source "$myconfig"
  250. if [ $? -ne 0 ]; then
  251. error "Config file '$myconfig' loaded with errors."
  252. else
  253. info "Configuration file '$myconfig' loaded."
  254. fi
  255. else
  256. warning "Configuration file '$myconfig' not found."
  257. fi
  258. done
  259. # make sure time_divisor is right
  260. time_divisor=$((time_divisor))
  261. [ $time_divisor -lt 10 ] && time_divisor=10
  262. [ $time_divisor -gt 100 ] && time_divisor=100
  263. # we check for the timeout command, after we load our
  264. # configuration, so that the user may overwrite the
  265. # timeout command we use, providing a function that
  266. # can emulate the timeout command we need:
  267. # > timeout SECONDS command ...
  268. if [ $check_for_timeout -eq 1 ]; then
  269. require_cmd timeout || exit 1
  270. fi
  271. # -----------------------------------------------------------------------------
  272. # internal checks
  273. # netdata passes the requested update frequency as the first argument
  274. update_every=$((update_every + 1 - 1)) # makes sure it is a number
  275. test $update_every -eq 0 && update_every=1 # if it is zero, make it 1
  276. # check the charts.d directory
  277. [ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'"
  278. # -----------------------------------------------------------------------------
  279. # library functions
  280. fixid() {
  281. echo "$*" |
  282. tr -c "[A-Z][a-z][0-9]" "_" |
  283. sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |
  284. tr "[A-Z]" "[a-z]"
  285. }
  286. isvarset() {
  287. [ -n "$1" ] && [ "$1" != "unknown" ] && [ "$1" != "none" ]
  288. return $?
  289. }
  290. getosid() {
  291. if isvarset "${NETDATA_CONTAINER_OS_ID}"; then
  292. echo "${NETDATA_CONTAINER_OS_ID}"
  293. else
  294. echo "${NETDATA_SYSTEM_OS_ID}"
  295. fi
  296. }
  297. run() {
  298. local ret pid="${BASHPID}" t
  299. if [ "z${1}" = "z-t" -a "${2}" != "0" ]; then
  300. t="${2}"
  301. shift 2
  302. timeout "${t}" "${@}" 2>"${TMP_DIR}/run.${pid}"
  303. ret=$?
  304. else
  305. "${@}" 2>"${TMP_DIR}/run.${pid}"
  306. ret=$?
  307. fi
  308. if [ ${ret} -ne 0 ]; then
  309. {
  310. printf "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: command '"
  311. printf "%q " "${@}"
  312. printf "' failed with code ${ret}:\n --- BEGIN TRACE ---\n"
  313. cat "${TMP_DIR}/run.${pid}"
  314. printf " --- END TRACE ---\n"
  315. } >&2
  316. fi
  317. rm -f "${TMP_DIR}/run.${pid}"
  318. return ${ret}
  319. }
  320. # convert any floating point number
  321. # to integer, give a multiplier
  322. # the result is stored in ${FLOAT2INT_RESULT}
  323. # so that no fork is necessary
  324. # the multiplier must be a power of 10
  325. float2int() {
  326. local f m="$2" a b l v=($1)
  327. f=${v[0]}
  328. # the length of the multiplier - 1
  329. l=$((${#m} - 1))
  330. # check if the number is in scientific notation
  331. if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]; then
  332. # convert it to decimal
  333. # unfortunately, this fork cannot be avoided
  334. # if you know of a way to avoid it, please let me know
  335. f=$(printf "%0.${l}f" ${f})
  336. fi
  337. # split the floating point number
  338. # in integer (a) and decimal (b)
  339. a=${f/.*/}
  340. b=${f/*./}
  341. # if the integer part is missing
  342. # set it to zero
  343. [ -z "${a}" ] && a="0"
  344. # strip leading zeros from the integer part
  345. # base 10 conversion
  346. a=$((10#$a))
  347. # check the length of the decimal part
  348. # against the length of the multiplier
  349. if [ ${#b} -gt ${l} ]; then
  350. # too many digits - take the most significant
  351. b=${b:0:l}
  352. elif [ ${#b} -lt ${l} ]; then
  353. # too few digits - pad with zero on the right
  354. local z="00000000000000000000000" r=$((l - ${#b}))
  355. b="${b}${z:0:r}"
  356. fi
  357. # strip leading zeros from the decimal part
  358. # base 10 conversion
  359. b=$((10#$b))
  360. # store the result
  361. FLOAT2INT_RESULT=$(((a * m) + b))
  362. }
  363. # -----------------------------------------------------------------------------
  364. # charts check functions
  365. all_charts() {
  366. cd "$chartsd"
  367. [ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1
  368. ls *.chart.sh | sed "s/\.chart\.sh$//g"
  369. }
  370. declare -A charts_enable_keyword=(
  371. ['apache']="force"
  372. ['cpu_apps']="force"
  373. ['cpufreq']="force"
  374. ['example']="force"
  375. ['exim']="force"
  376. ['hddtemp']="force"
  377. ['load_average']="force"
  378. ['mem_apps']="force"
  379. ['mysql']="force"
  380. ['nginx']="force"
  381. ['phpfpm']="force"
  382. ['postfix']="force"
  383. ['sensors']="force"
  384. ['squid']="force"
  385. ['tomcat']="force"
  386. )
  387. declare -A obsolete_charts=(
  388. ['apache']="python.d.plugin module"
  389. ['cpu_apps']="apps.plugin"
  390. ['cpufreq']="proc plugin"
  391. ['exim']="python.d.plugin module"
  392. ['hddtemp']="python.d.plugin module"
  393. ['load_average']="proc plugin"
  394. ['mem_apps']="proc plugin"
  395. ['mysql']="python.d.plugin module"
  396. ['nginx']="python.d.plugin module"
  397. ['phpfpm']="python.d.plugin module"
  398. ['postfix']="python.d.plugin module"
  399. ['squid']="python.d.plugin module"
  400. ['tomcat']="python.d.plugin module"
  401. )
  402. all_enabled_charts() {
  403. local charts enabled required
  404. # find all enabled charts
  405. for chart in $(all_charts); do
  406. MODULE_NAME="${chart}"
  407. if [ -n "${obsolete_charts["$MODULE_NAME"]}" ]; then
  408. debug "is replaced by ${obsolete_charts["$MODULE_NAME"]}, skipping it."
  409. continue
  410. fi
  411. eval "enabled=\$$chart"
  412. if [ -z "${enabled}" ]; then
  413. enabled="${enable_all_charts}"
  414. fi
  415. required="${charts_enable_keyword[${chart}]}"
  416. [ -z "${required}" ] && required="yes"
  417. if [ ! "${enabled}" = "${required}" ]; then
  418. info "is disabled. Add a line with $chart=$required in '${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf' to enable it (or remove the line that disables it)."
  419. else
  420. debug "is enabled for auto-detection."
  421. local charts="$charts $chart"
  422. fi
  423. done
  424. MODULE_NAME="main"
  425. local charts2=
  426. for chart in $charts; do
  427. MODULE_NAME="${chart}"
  428. # check the enabled charts
  429. local check="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()")"
  430. if [ -z "$check" ]; then
  431. error "module '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
  432. continue
  433. fi
  434. local create="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()")"
  435. if [ -z "$create" ]; then
  436. error "module '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
  437. continue
  438. fi
  439. local update="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()")"
  440. if [ -z "$update" ]; then
  441. error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
  442. continue
  443. fi
  444. # check its config
  445. #if [ -f "$userconfd/$chart.conf" ]
  446. #then
  447. # if [ ! -z "$( cat "$userconfd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_underscore" )" ]
  448. # then
  449. # error "module's $chart config $userconfd/$chart.conf should only have lines starting with $chart$charts_underscore . Disabling it."
  450. # continue
  451. # fi
  452. #fi
  453. #if [ $dryrunner -eq 1 ]
  454. # then
  455. # "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$userconfd/$chart.conf" >/dev/null
  456. # if [ $? -ne 0 ]
  457. # then
  458. # error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
  459. # continue
  460. # fi
  461. #fi
  462. local charts2="$charts2 $chart"
  463. done
  464. MODULE_NAME="main"
  465. echo $charts2
  466. debug "enabled charts: $charts2"
  467. }
  468. # -----------------------------------------------------------------------------
  469. # load the charts
  470. suffix_retries="_retries"
  471. suffix_update_every="_update_every"
  472. active_charts=
  473. for chart in $(all_enabled_charts); do
  474. MODULE_NAME="${chart}"
  475. debug "loading module: '$chartsd/$chart.chart.sh'"
  476. source "$chartsd/$chart.chart.sh"
  477. [ $? -ne 0 ] && warning "Module '$chartsd/$chart.chart.sh' loaded with errors."
  478. # first load the stock config
  479. if [ -f "$stockconfd/$chart.conf" ]; then
  480. debug "loading module configuration: '$stockconfd/$chart.conf'"
  481. source "$stockconfd/$chart.conf"
  482. [ $? -ne 0 ] && warning "Config file '$stockconfd/$chart.conf' loaded with errors."
  483. else
  484. debug "not found module configuration: '$stockconfd/$chart.conf'"
  485. fi
  486. # then load the user config (it overwrites the stock)
  487. if [ -f "$userconfd/$chart.conf" ]; then
  488. debug "loading module configuration: '$userconfd/$chart.conf'"
  489. source "$userconfd/$chart.conf"
  490. [ $? -ne 0 ] && warning "Config file '$userconfd/$chart.conf' loaded with errors."
  491. else
  492. debug "not found module configuration: '$userconfd/$chart.conf'"
  493. if [ -f "$olduserconfd/$chart.conf" ]; then
  494. # support for very old netdata that had the charts.d module configs in /etc/netdata
  495. info "loading module configuration from obsolete location: '$olduserconfd/$chart.conf'"
  496. source "$olduserconfd/$chart.conf"
  497. [ $? -ne 0 ] && warning "Config file '$olduserconfd/$chart.conf' loaded with errors."
  498. fi
  499. fi
  500. eval "dt=\$$chart$suffix_update_every"
  501. dt=$((dt + 1 - 1)) # make sure it is a number
  502. if [ $dt -lt $update_every ]; then
  503. eval "$chart$suffix_update_every=$update_every"
  504. fi
  505. $chart$charts_check
  506. if [ $? -eq 0 ]; then
  507. debug "module '$chart' activated"
  508. active_charts="$active_charts $chart"
  509. else
  510. error "module's '$chart' check() function reports failure."
  511. fi
  512. done
  513. MODULE_NAME="main"
  514. debug "activated modules: $active_charts"
  515. # -----------------------------------------------------------------------------
  516. # check overwrites
  517. # enable work time reporting
  518. debug_time=
  519. test $debug -eq 1 && debug_time=tellwork
  520. # if we only need a specific chart, remove all the others
  521. if [ ! -z "${chart_only}" ]; then
  522. debug "requested to run only for: '${chart_only}'"
  523. check_charts=
  524. for chart in $active_charts; do
  525. if [ "$chart" = "$chart_only" ]; then
  526. check_charts="$chart"
  527. break
  528. fi
  529. done
  530. active_charts="$check_charts"
  531. fi
  532. debug "activated charts: $active_charts"
  533. # stop if we just need a pre-check
  534. if [ $check -eq 1 ]; then
  535. info "CHECK RESULT"
  536. info "Will run the charts: $active_charts"
  537. exit 0
  538. fi
  539. # -----------------------------------------------------------------------------
  540. cd "${TMP_DIR}" || exit 1
  541. # -----------------------------------------------------------------------------
  542. # create charts
  543. run_charts=
  544. for chart in $active_charts; do
  545. MODULE_NAME="${chart}"
  546. debug "calling '$chart$charts_create()'..."
  547. $chart$charts_create
  548. if [ $? -eq 0 ]; then
  549. run_charts="$run_charts $chart"
  550. debug "'$chart' initialized."
  551. else
  552. error "module's '$chart' function '$chart$charts_create()' reports failure."
  553. fi
  554. done
  555. MODULE_NAME="main"
  556. debug "run_charts='$run_charts'"
  557. # -----------------------------------------------------------------------------
  558. # update dimensions
  559. [ -z "$run_charts" ] && fatal "No charts to collect data from."
  560. keepalive() {
  561. if [ ! -t 1 ] && ! printf "\n"; then
  562. chartsd_cleanup
  563. fi
  564. }
  565. declare -A charts_last_update=() charts_update_every=() charts_retries=() charts_next_update=() charts_run_counter=() charts_serial_failures=()
  566. global_update() {
  567. local exit_at \
  568. c=0 dt ret last_ms exec_start_ms exec_end_ms \
  569. chart now_charts=() next_charts=($run_charts) \
  570. next_ms x seconds millis
  571. # return the current time in ms in $now_ms
  572. ${current_time_ms}
  573. exit_at=$((now_ms + (restart_timeout * 1000)))
  574. for chart in $run_charts; do
  575. eval "charts_update_every[$chart]=\$$chart$suffix_update_every"
  576. test -z "${charts_update_every[$chart]}" && charts_update_every[$chart]=$update_every
  577. eval "charts_retries[$chart]=\$$chart$suffix_retries"
  578. test -z "${charts_retries[$chart]}" && charts_retries[$chart]=10
  579. charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000))))
  580. charts_next_update[$chart]=$((charts_last_update[$chart] + (charts_update_every[$chart] * 1000)))
  581. charts_run_counter[$chart]=0
  582. charts_serial_failures[$chart]=0
  583. echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]} '' '' '$chart'"
  584. echo "DIMENSION run_time 'run time' absolute 1 1"
  585. done
  586. # the main loop
  587. while [ "${#next_charts[@]}" -gt 0 ]; do
  588. keepalive
  589. c=$((c + 1))
  590. now_charts=("${next_charts[@]}")
  591. next_charts=()
  592. # return the current time in ms in $now_ms
  593. ${current_time_ms}
  594. for chart in "${now_charts[@]}"; do
  595. MODULE_NAME="${chart}"
  596. if [ ${now_ms} -ge ${charts_next_update[$chart]} ]; then
  597. last_ms=${charts_last_update[$chart]}
  598. dt=$((now_ms - last_ms))
  599. charts_last_update[$chart]=${now_ms}
  600. while [ ${charts_next_update[$chart]} -lt ${now_ms} ]; do
  601. charts_next_update[$chart]=$((charts_next_update[$chart] + (charts_update_every[$chart] * 1000)))
  602. done
  603. # the first call should not give a duration
  604. # so that netdata calibrates to current time
  605. dt=$((dt * 1000))
  606. charts_run_counter[$chart]=$((charts_run_counter[$chart] + 1))
  607. if [ ${charts_run_counter[$chart]} -eq 1 ]; then
  608. dt=
  609. fi
  610. exec_start_ms=$now_ms
  611. $chart$charts_update $dt
  612. ret=$?
  613. # return the current time in ms in $now_ms
  614. ${current_time_ms}
  615. exec_end_ms=$now_ms
  616. echo "BEGIN netdata.plugin_chartsd_$chart $dt"
  617. echo "SET run_time = $((exec_end_ms - exec_start_ms))"
  618. echo "END"
  619. if [ $ret -eq 0 ]; then
  620. charts_serial_failures[$chart]=0
  621. next_charts+=($chart)
  622. else
  623. charts_serial_failures[$chart]=$((charts_serial_failures[$chart] + 1))
  624. if [ ${charts_serial_failures[$chart]} -gt ${charts_retries[$chart]} ]; then
  625. error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
  626. else
  627. error "module's '$chart' update() function reports failure. Will keep trying for a while."
  628. next_charts+=($chart)
  629. fi
  630. fi
  631. else
  632. next_charts+=($chart)
  633. fi
  634. done
  635. MODULE_NAME="${chart}"
  636. # wait the time you are required to
  637. next_ms=$((now_ms + (update_every * 1000 * 100)))
  638. for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done
  639. next_ms=$((next_ms - now_ms))
  640. if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]; then
  641. next_ms=$((next_ms + current_time_ms_accuracy))
  642. seconds=$((next_ms / 1000))
  643. millis=$((next_ms % 1000))
  644. if [ ${millis} -lt 10 ]; then
  645. millis="00${millis}"
  646. elif [ ${millis} -lt 100 ]; then
  647. millis="0${millis}"
  648. fi
  649. debug "sleeping for ${seconds}.${millis} seconds."
  650. ${mysleep} ${seconds}.${millis}
  651. else
  652. debug "sleeping for ${update_every} seconds."
  653. ${mysleep} $update_every
  654. fi
  655. test ${now_ms} -ge ${exit_at} && exit 0
  656. done
  657. fatal "nothing left to do, exiting..."
  658. }
  659. global_update