health_log.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "health.h"
  3. // ----------------------------------------------------------------------------
  4. // health alarm log load/save
  5. // no need for locking - only one thread is reading / writing the alarms log
  6. inline int health_alarm_log_open(RRDHOST *host) {
  7. if(host->health_log_fp)
  8. fclose(host->health_log_fp);
  9. host->health_log_fp = fopen(host->health_log_filename, "a");
  10. if(host->health_log_fp) {
  11. if (setvbuf(host->health_log_fp, NULL, _IOLBF, 0) != 0)
  12. error("HEALTH [%s]: cannot set line buffering on health log file '%s'.", rrdhost_hostname(host), host->health_log_filename);
  13. return 0;
  14. }
  15. error("HEALTH [%s]: cannot open health log file '%s'. Health data will be lost in case of netdata or server crash.", rrdhost_hostname(host), host->health_log_filename);
  16. return -1;
  17. }
  18. static inline void health_alarm_log_close(RRDHOST *host) {
  19. if(host->health_log_fp) {
  20. fclose(host->health_log_fp);
  21. host->health_log_fp = NULL;
  22. }
  23. }
  24. static inline void health_log_rotate(RRDHOST *host) {
  25. static size_t rotate_every = 0;
  26. if(unlikely(rotate_every == 0)) {
  27. rotate_every = (size_t)config_get_number(CONFIG_SECTION_HEALTH, "rotate log every lines", 2000);
  28. if(rotate_every < 100) rotate_every = 100;
  29. }
  30. if(unlikely(host->health_log_entries_written > rotate_every)) {
  31. if(unlikely(host->health_log_fp)) {
  32. health_alarm_log_close(host);
  33. char old_filename[FILENAME_MAX + 1];
  34. snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename);
  35. if(unlink(old_filename) == -1 && errno != ENOENT)
  36. error("HEALTH [%s]: cannot remove old alarms log file '%s'", rrdhost_hostname(host), old_filename);
  37. if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT)
  38. error("HEALTH [%s]: cannot move file '%s' to '%s'.", rrdhost_hostname(host), host->health_log_filename, old_filename);
  39. if(unlink(host->health_log_filename) == -1 && errno != ENOENT)
  40. error("HEALTH [%s]: cannot remove old alarms log file '%s'", rrdhost_hostname(host), host->health_log_filename);
  41. // open it with truncate
  42. host->health_log_fp = fopen(host->health_log_filename, "w");
  43. if(host->health_log_fp)
  44. fclose(host->health_log_fp);
  45. else
  46. error("HEALTH [%s]: cannot truncate health log '%s'", rrdhost_hostname(host), host->health_log_filename);
  47. host->health_log_fp = NULL;
  48. host->health_log_entries_written = 0;
  49. health_alarm_log_open(host);
  50. }
  51. }
  52. }
  53. inline void health_label_log_save(RRDHOST *host) {
  54. health_log_rotate(host);
  55. if(unlikely(host->health_log_fp)) {
  56. BUFFER *wb = buffer_create(1024);
  57. rrdlabels_to_buffer(localhost->rrdlabels, wb, "", "=", "", "\t ", NULL, NULL, NULL, NULL);
  58. char *write = (char *) buffer_tostring(wb);
  59. if (unlikely(fprintf(host->health_log_fp, "L\t%s", write) < 0))
  60. error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.",
  61. rrdhost_hostname(host), host->health_log_filename);
  62. else
  63. host->health_log_entries_written++;
  64. buffer_free(wb);
  65. }
  66. }
  67. inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
  68. health_log_rotate(host);
  69. if(unlikely(host->health_log_fp)) {
  70. if(unlikely(fprintf(host->health_log_fp
  71. , "%c\t%s"
  72. "\t%08x\t%08x\t%08x\t%08x\t%08x"
  73. "\t%08x\t%08x\t%08x"
  74. "\t%08x\t%08x\t%08x"
  75. "\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s"
  76. "\t%d\t%d\t%d\t%d"
  77. "\t" NETDATA_DOUBLE_FORMAT_AUTO "\t" NETDATA_DOUBLE_FORMAT_AUTO
  78. "\t%016"PRIx64""
  79. "\t%s\t%s\t%s"
  80. "\n"
  81. , (ae->flags & HEALTH_ENTRY_FLAG_SAVED)?'U':'A'
  82. , rrdhost_hostname(host)
  83. , ae->unique_id
  84. , ae->alarm_id
  85. , ae->alarm_event_id
  86. , ae->updated_by_id
  87. , ae->updates_id
  88. , (uint32_t)ae->when
  89. , (uint32_t)ae->duration
  90. , (uint32_t)ae->non_clear_duration
  91. , (uint32_t)ae->flags
  92. , (uint32_t)ae->exec_run_timestamp
  93. , (uint32_t)ae->delay_up_to_timestamp
  94. , ae_name(ae)
  95. , ae_chart_name(ae)
  96. , ae_family(ae)
  97. , ae_exec(ae)
  98. , ae_recipient(ae)
  99. , ae_source(ae)
  100. , ae_units(ae)
  101. , ae_info(ae)
  102. , ae->exec_code
  103. , ae->new_status
  104. , ae->old_status
  105. , ae->delay
  106. , ae->new_value
  107. , ae->old_value
  108. , (uint64_t)ae->last_repeat
  109. , (ae->classification)?ae_classification(ae):"Unknown"
  110. , (ae->component)?ae_component(ae):"Unknown"
  111. , (ae->type)?ae_type(ae):"Unknown"
  112. ) < 0))
  113. error("HEALTH [%s]: failed to save alarm log entry to '%s'. Health data may be lost in case of abnormal restart.", rrdhost_hostname(host), host->health_log_filename);
  114. else {
  115. ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
  116. host->health_log_entries_written++;
  117. }
  118. }else
  119. sql_health_alarm_log_save(host, ae);
  120. #ifdef ENABLE_ACLK
  121. if (netdata_cloud_setting) {
  122. sql_queue_alarm_to_aclk(host, ae, 0);
  123. }
  124. #endif
  125. }
  126. static uint32_t is_valid_alarm_id(RRDHOST *host, const char *chart, const char *name, uint32_t alarm_id)
  127. {
  128. STRING *chart_string = string_strdupz(chart);
  129. STRING *name_string = string_strdupz(name);
  130. uint32_t ret = 1;
  131. ALARM_ENTRY *ae;
  132. for(ae = host->health_log.alarms; ae ;ae = ae->next) {
  133. if (unlikely(ae->alarm_id == alarm_id && (!(chart_string == ae->chart && name_string == ae->name)))) {
  134. ret = 0;
  135. break;
  136. }
  137. }
  138. string_freez(chart_string);
  139. string_freez(name_string);
  140. return ret;
  141. }
  142. static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) {
  143. errno = 0;
  144. char *s, *buf = mallocz(65536 + 1);
  145. size_t line = 0, len = 0;
  146. ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0;
  147. DICTIONARY *all_rrdcalcs = dictionary_create(
  148. DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE);
  149. RRDCALC *rc;
  150. foreach_rrdcalc_in_rrdhost_read(host, rc) {
  151. dictionary_set(all_rrdcalcs, rrdcalc_name(rc), rc, sizeof(*rc));
  152. }
  153. foreach_rrdcalc_in_rrdhost_done(rc);
  154. netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
  155. while((s = fgets_trim_len(buf, 65536, fp, &len))) {
  156. host->health_log_entries_written++;
  157. line++;
  158. int max_entries = 33, entries = 0;
  159. char *pointers[max_entries];
  160. pointers[entries++] = s++;
  161. while(*s) {
  162. if(unlikely(*s == '\t')) {
  163. *s = '\0';
  164. pointers[entries++] = ++s;
  165. if(entries >= max_entries) {
  166. error("HEALTH [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", rrdhost_hostname(host), line, filename, max_entries);
  167. break;
  168. }
  169. }
  170. else s++;
  171. }
  172. if(likely(*pointers[0] == 'L'))
  173. continue;
  174. if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) {
  175. ALARM_ENTRY *ae = NULL;
  176. if(entries < 27) {
  177. error("HEALTH [%s]: line %zu of file '%s' should have at least 27 entries, but it has %d. Ignoring it.", rrdhost_hostname(host), line, filename, entries);
  178. errored++;
  179. continue;
  180. }
  181. // check that we have valid ids
  182. uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16);
  183. if(!unique_id) {
  184. error("HEALTH [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", rrdhost_hostname(host), line, filename, unique_id, pointers[2]);
  185. errored++;
  186. continue;
  187. }
  188. uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16);
  189. if(!alarm_id) {
  190. error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", rrdhost_hostname(host), line, filename, alarm_id, pointers[3]);
  191. errored++;
  192. continue;
  193. }
  194. // Check if we got last_repeat field
  195. time_t last_repeat = 0;
  196. if(entries > 27) {
  197. char* alarm_name = pointers[13];
  198. last_repeat = (time_t)strtoul(pointers[27], NULL, 16);
  199. rc = dictionary_get(all_rrdcalcs, alarm_name);
  200. if(unlikely(rc)) {
  201. if (rrdcalc_isrepeating(rc)) {
  202. rc->last_repeat = last_repeat;
  203. // We iterate through repeating alarm entries only to
  204. // find the latest last_repeat timestamp. Otherwise,
  205. // there is no need to keep them in memory.
  206. continue;
  207. }
  208. }
  209. }
  210. if(unlikely(*pointers[0] == 'A')) {
  211. // make sure it is properly numbered
  212. if(unlikely(host->health_log.alarms && unique_id < host->health_log.alarms->unique_id)) {
  213. error( "HEALTH [%s]: line %zu of file '%s' has alarm log entry %u in wrong order. Ignoring it."
  214. , rrdhost_hostname(host), line, filename, unique_id);
  215. errored++;
  216. continue;
  217. }
  218. ae = callocz(1, sizeof(ALARM_ENTRY));
  219. }
  220. else if(unlikely(*pointers[0] == 'U')) {
  221. // find the original
  222. for(ae = host->health_log.alarms; ae ; ae = ae->next) {
  223. if(unlikely(unique_id == ae->unique_id)) {
  224. if(unlikely(*pointers[0] == 'A')) {
  225. error("HEALTH [%s]: line %zu of file '%s' adds duplicate alarm log entry %u. Using the later."
  226. , rrdhost_hostname(host), line, filename, unique_id);
  227. *pointers[0] = 'U';
  228. duplicate++;
  229. }
  230. break;
  231. }
  232. else if(unlikely(unique_id > ae->unique_id)) {
  233. // no need to continue
  234. // the linked list is sorted
  235. ae = NULL;
  236. break;
  237. }
  238. }
  239. }
  240. // if not found, skip this line
  241. if(unlikely(!ae)) {
  242. // error("HEALTH [%s]: line %zu of file '%s' updates alarm log entry with unique id %u, but it is not found.", host->hostname, line, filename, unique_id);
  243. continue;
  244. }
  245. // check for a possible host mismatch
  246. //if(strcmp(pointers[1], host->hostname))
  247. // error("HEALTH [%s]: line %zu of file '%s' provides an alarm for host '%s' but this is named '%s'.", host->hostname, line, filename, pointers[1], host->hostname);
  248. ae->unique_id = unique_id;
  249. if (!is_valid_alarm_id(host, pointers[14], pointers[13], alarm_id)) {
  250. STRING *chart = string_strdupz(pointers[14]);
  251. STRING *name = string_strdupz(pointers[13]);
  252. alarm_id = rrdcalc_get_unique_id(host, chart, name, NULL);
  253. string_freez(chart);
  254. string_freez(name);
  255. }
  256. ae->alarm_id = alarm_id;
  257. ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16);
  258. ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16);
  259. ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16);
  260. ae->when = (uint32_t)strtoul(pointers[7], NULL, 16);
  261. ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16);
  262. ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16);
  263. ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16);
  264. ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
  265. ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16);
  266. ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16);
  267. string_freez(ae->name);
  268. ae->name = string_strdupz(pointers[13]);
  269. string_freez(ae->chart);
  270. ae->chart = string_strdupz(pointers[14]);
  271. string_freez(ae->family);
  272. ae->family = string_strdupz(pointers[15]);
  273. string_freez(ae->exec);
  274. ae->exec = string_strdupz(pointers[16]);
  275. string_freez(ae->recipient);
  276. ae->recipient = string_strdupz(pointers[17]);
  277. string_freez(ae->source);
  278. ae->source = string_strdupz(pointers[18]);
  279. string_freez(ae->units);
  280. ae->units = string_strdupz(pointers[19]);
  281. string_freez(ae->info);
  282. ae->info = string_strdupz(pointers[20]);
  283. ae->exec_code = str2i(pointers[21]);
  284. ae->new_status = str2i(pointers[22]);
  285. ae->old_status = str2i(pointers[23]);
  286. ae->delay = str2i(pointers[24]);
  287. ae->new_value = str2l(pointers[25]);
  288. ae->old_value = str2l(pointers[26]);
  289. ae->last_repeat = last_repeat;
  290. if (likely(entries > 30)) {
  291. string_freez(ae->classification);
  292. ae->classification = string_strdupz(pointers[28]);
  293. string_freez(ae->component);
  294. ae->component = string_strdupz(pointers[29]);
  295. string_freez(ae->type);
  296. ae->type = string_strdupz(pointers[30]);
  297. }
  298. char value_string[100 + 1];
  299. string_freez(ae->old_value_string);
  300. string_freez(ae->new_value_string);
  301. ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1));
  302. ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1));
  303. // add it to host if not already there
  304. if(unlikely(*pointers[0] == 'A')) {
  305. ae->next = host->health_log.alarms;
  306. host->health_log.alarms = ae;
  307. sql_health_alarm_log_insert(host, ae);
  308. loaded++;
  309. }
  310. else {
  311. sql_health_alarm_log_update(host, ae);
  312. updated++;
  313. }
  314. if(unlikely(ae->unique_id > host->health_max_unique_id))
  315. host->health_max_unique_id = ae->unique_id;
  316. if(unlikely(ae->alarm_id >= host->health_max_alarm_id))
  317. host->health_max_alarm_id = ae->alarm_id;
  318. }
  319. else {
  320. error("HEALTH [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", rrdhost_hostname(host), line, filename, pointers[0]);
  321. errored++;
  322. }
  323. }
  324. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  325. dictionary_destroy(all_rrdcalcs);
  326. all_rrdcalcs = NULL;
  327. freez(buf);
  328. if(!host->health_max_unique_id) host->health_max_unique_id = (uint32_t)now_realtime_sec();
  329. if(!host->health_max_alarm_id) host->health_max_alarm_id = (uint32_t)now_realtime_sec();
  330. host->health_log.next_log_id = host->health_max_unique_id + 1;
  331. if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id))
  332. host->health_log.next_alarm_id = host->health_max_alarm_id + 1;
  333. debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", rrdhost_hostname(host), filename, loaded, updated, errored, duplicate);
  334. return loaded;
  335. }
  336. inline void health_alarm_log_load(RRDHOST *host) {
  337. health_alarm_log_close(host);
  338. char filename[FILENAME_MAX + 1];
  339. snprintfz(filename, FILENAME_MAX, "%s.old", host->health_log_filename);
  340. FILE *fp = fopen(filename, "r");
  341. if(!fp)
  342. error("HEALTH [%s]: cannot open health file: %s", rrdhost_hostname(host), filename);
  343. else {
  344. health_alarm_log_read(host, fp, filename);
  345. fclose(fp);
  346. }
  347. host->health_log_entries_written = 0;
  348. fp = fopen(host->health_log_filename, "r");
  349. if(!fp)
  350. error("HEALTH [%s]: cannot open health file: %s", rrdhost_hostname(host), host->health_log_filename);
  351. else {
  352. health_alarm_log_read(host, fp, host->health_log_filename);
  353. fclose(fp);
  354. }
  355. }
  356. // ----------------------------------------------------------------------------
  357. // health alarm log management
  358. inline ALARM_ENTRY* health_create_alarm_entry(
  359. RRDHOST *host,
  360. uint32_t alarm_id,
  361. uint32_t alarm_event_id,
  362. const uuid_t config_hash_id,
  363. time_t when,
  364. STRING *name,
  365. STRING *chart,
  366. STRING *chart_context,
  367. STRING *family,
  368. STRING *class,
  369. STRING *component,
  370. STRING *type,
  371. STRING *exec,
  372. STRING *recipient,
  373. time_t duration,
  374. NETDATA_DOUBLE old_value,
  375. NETDATA_DOUBLE new_value,
  376. RRDCALC_STATUS old_status,
  377. RRDCALC_STATUS new_status,
  378. STRING *source,
  379. STRING *units,
  380. STRING *info,
  381. int delay,
  382. uint32_t flags
  383. ) {
  384. debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
  385. ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
  386. ae->name = string_dup(name);
  387. ae->chart = string_dup(chart);
  388. ae->chart_context = string_dup(chart_context);
  389. uuid_copy(ae->config_hash_id, *((uuid_t *) config_hash_id));
  390. ae->family = string_dup(family);
  391. ae->classification = string_dup(class);
  392. ae->component = string_dup(component);
  393. ae->type = string_dup(type);
  394. ae->exec = string_dup(exec);
  395. ae->recipient = string_dup(recipient);
  396. ae->source = string_dup(source);
  397. ae->units = string_dup(units);
  398. ae->unique_id = host->health_log.next_log_id++;
  399. ae->alarm_id = alarm_id;
  400. ae->alarm_event_id = alarm_event_id;
  401. ae->when = when;
  402. ae->old_value = old_value;
  403. ae->new_value = new_value;
  404. char value_string[100 + 1];
  405. ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1));
  406. ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1));
  407. ae->info = string_dup(info);
  408. ae->old_status = old_status;
  409. ae->new_status = new_status;
  410. ae->duration = duration;
  411. ae->delay = delay;
  412. ae->delay_up_to_timestamp = when + delay;
  413. ae->flags |= flags;
  414. ae->last_repeat = 0;
  415. if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
  416. ae->non_clear_duration += ae->duration;
  417. return ae;
  418. }
  419. inline void health_alarm_log_add_entry(
  420. RRDHOST *host,
  421. ALARM_ENTRY *ae
  422. ) {
  423. debug(D_HEALTH, "Health adding alarm log entry with id: %u", ae->unique_id);
  424. // link it
  425. netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
  426. ae->next = host->health_log.alarms;
  427. host->health_log.alarms = ae;
  428. host->health_log.count++;
  429. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  430. // match previous alarms
  431. netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
  432. ALARM_ENTRY *t;
  433. for(t = host->health_log.alarms ; t ; t = t->next) {
  434. if(t != ae && t->alarm_id == ae->alarm_id) {
  435. if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) {
  436. t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
  437. t->updated_by_id = ae->unique_id;
  438. ae->updates_id = t->unique_id;
  439. if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
  440. (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
  441. ae->non_clear_duration += t->non_clear_duration;
  442. health_alarm_log_save(host, t);
  443. }
  444. // no need to continue
  445. break;
  446. }
  447. }
  448. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  449. health_alarm_log_save(host, ae);
  450. }
  451. inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) {
  452. string_freez(ae->name);
  453. string_freez(ae->chart);
  454. string_freez(ae->chart_context);
  455. string_freez(ae->family);
  456. string_freez(ae->classification);
  457. string_freez(ae->component);
  458. string_freez(ae->type);
  459. string_freez(ae->exec);
  460. string_freez(ae->recipient);
  461. string_freez(ae->source);
  462. string_freez(ae->units);
  463. string_freez(ae->info);
  464. string_freez(ae->old_value_string);
  465. string_freez(ae->new_value_string);
  466. freez(ae);
  467. }
  468. inline void health_alarm_log_free(RRDHOST *host) {
  469. netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
  470. ALARM_ENTRY *ae;
  471. while((ae = host->health_log.alarms)) {
  472. host->health_log.alarms = ae->next;
  473. health_alarm_log_free_one_nochecks_nounlink(ae);
  474. }
  475. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  476. }