health_log.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  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'.", host->hostname, 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.", host->hostname, 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'", host->hostname, old_filename);
  37. if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT)
  38. error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, 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'", host->hostname, 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'", host->hostname, 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->host_labels, 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. host->hostname, 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. , host->hostname
  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->name:""
  95. , (ae->chart)?ae->chart:""
  96. , (ae->family)?ae->family:""
  97. , (ae->exec)?ae->exec:""
  98. , (ae->recipient)?ae->recipient:""
  99. , (ae->source)?ae->source:""
  100. , (ae->units)?ae->units:""
  101. , (ae->info)?ae->info:""
  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:"Unknown"
  110. , (ae->component)?ae->component:"Unknown"
  111. , (ae->type)?ae->type:"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.", host->hostname, 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. uint32_t hash_chart = simple_hash(chart);
  129. uint32_t hash_name = simple_hash(name);
  130. ALARM_ENTRY *ae;
  131. for(ae = host->health_log.alarms; ae ;ae = ae->next) {
  132. if (unlikely(
  133. ae->alarm_id == alarm_id && (!(ae->hash_name == hash_name && ae->hash_chart == hash_chart &&
  134. !strcmp(name, ae->name) && !strcmp(chart, ae->chart))))) {
  135. return 0;
  136. }
  137. }
  138. return 1;
  139. }
  140. static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char *filename) {
  141. errno = 0;
  142. char *s, *buf = mallocz(65536 + 1);
  143. size_t line = 0, len = 0;
  144. ssize_t loaded = 0, updated = 0, errored = 0, duplicate = 0;
  145. netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
  146. while((s = fgets_trim_len(buf, 65536, fp, &len))) {
  147. host->health_log_entries_written++;
  148. line++;
  149. int max_entries = 33, entries = 0;
  150. char *pointers[max_entries];
  151. pointers[entries++] = s++;
  152. while(*s) {
  153. if(unlikely(*s == '\t')) {
  154. *s = '\0';
  155. pointers[entries++] = ++s;
  156. if(entries >= max_entries) {
  157. error("HEALTH [%s]: line %zu of file '%s' has more than %d entries. Ignoring excessive entries.", host->hostname, line, filename, max_entries);
  158. break;
  159. }
  160. }
  161. else s++;
  162. }
  163. if(likely(*pointers[0] == 'L'))
  164. continue;
  165. if(likely(*pointers[0] == 'U' || *pointers[0] == 'A')) {
  166. ALARM_ENTRY *ae = NULL;
  167. if(entries < 27) {
  168. error("HEALTH [%s]: line %zu of file '%s' should have at least 27 entries, but it has %d. Ignoring it.", host->hostname, line, filename, entries);
  169. errored++;
  170. continue;
  171. }
  172. // check that we have valid ids
  173. uint32_t unique_id = (uint32_t)strtoul(pointers[2], NULL, 16);
  174. if(!unique_id) {
  175. error("HEALTH [%s]: line %zu of file '%s' states alarm entry with invalid unique id %u (%s). Ignoring it.", host->hostname, line, filename, unique_id, pointers[2]);
  176. errored++;
  177. continue;
  178. }
  179. uint32_t alarm_id = (uint32_t)strtoul(pointers[3], NULL, 16);
  180. if(!alarm_id) {
  181. error("HEALTH [%s]: line %zu of file '%s' states alarm entry for invalid alarm id %u (%s). Ignoring it.", host->hostname, line, filename, alarm_id, pointers[3]);
  182. errored++;
  183. continue;
  184. }
  185. // Check if we got last_repeat field
  186. time_t last_repeat = 0;
  187. if(entries > 27) {
  188. char* alarm_name = pointers[13];
  189. last_repeat = (time_t)strtoul(pointers[27], NULL, 16);
  190. RRDCALC *rc = alarm_max_last_repeat(host, alarm_name,simple_hash(alarm_name));
  191. if (!rc) {
  192. for(rc = host->alarms; rc ; rc = rc->next) {
  193. RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_name, (avl_t *)rc);
  194. if(rdcmp != rc) {
  195. error("Cannot insert the alarm index ID using log %s", rc->name);
  196. }
  197. }
  198. rc = alarm_max_last_repeat(host, alarm_name,simple_hash(alarm_name));
  199. }
  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. , host->hostname, 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. , host->hostname, 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. alarm_id = rrdcalc_get_unique_id(host, pointers[14], pointers[13], NULL);
  251. ae->alarm_id = alarm_id;
  252. ae->alarm_event_id = (uint32_t)strtoul(pointers[4], NULL, 16);
  253. ae->updated_by_id = (uint32_t)strtoul(pointers[5], NULL, 16);
  254. ae->updates_id = (uint32_t)strtoul(pointers[6], NULL, 16);
  255. ae->when = (uint32_t)strtoul(pointers[7], NULL, 16);
  256. ae->duration = (uint32_t)strtoul(pointers[8], NULL, 16);
  257. ae->non_clear_duration = (uint32_t)strtoul(pointers[9], NULL, 16);
  258. ae->flags = (uint32_t)strtoul(pointers[10], NULL, 16);
  259. ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
  260. ae->exec_run_timestamp = (uint32_t)strtoul(pointers[11], NULL, 16);
  261. ae->delay_up_to_timestamp = (uint32_t)strtoul(pointers[12], NULL, 16);
  262. freez(ae->name);
  263. ae->name = strdupz(pointers[13]);
  264. ae->hash_name = simple_hash(ae->name);
  265. freez(ae->chart);
  266. ae->chart = strdupz(pointers[14]);
  267. ae->hash_chart = simple_hash(ae->chart);
  268. freez(ae->family);
  269. ae->family = strdupz(pointers[15]);
  270. freez(ae->exec);
  271. ae->exec = strdupz(pointers[16]);
  272. if(!*ae->exec) { freez(ae->exec); ae->exec = NULL; }
  273. freez(ae->recipient);
  274. ae->recipient = strdupz(pointers[17]);
  275. if(!*ae->recipient) { freez(ae->recipient); ae->recipient = NULL; }
  276. freez(ae->source);
  277. ae->source = strdupz(pointers[18]);
  278. if(!*ae->source) { freez(ae->source); ae->source = NULL; }
  279. freez(ae->units);
  280. ae->units = strdupz(pointers[19]);
  281. if(!*ae->units) { freez(ae->units); ae->units = NULL; }
  282. freez(ae->info);
  283. ae->info = strdupz(pointers[20]);
  284. if(!*ae->info) { freez(ae->info); ae->info = NULL; }
  285. ae->exec_code = str2i(pointers[21]);
  286. ae->new_status = str2i(pointers[22]);
  287. ae->old_status = str2i(pointers[23]);
  288. ae->delay = str2i(pointers[24]);
  289. ae->new_value = str2l(pointers[25]);
  290. ae->old_value = str2l(pointers[26]);
  291. ae->last_repeat = last_repeat;
  292. if (likely(entries > 30)) {
  293. freez(ae->classification);
  294. ae->classification = strdupz(pointers[28]);
  295. if(!*ae->classification) { freez(ae->classification); ae->classification = NULL; }
  296. freez(ae->component);
  297. ae->component = strdupz(pointers[29]);
  298. if(!*ae->component) { freez(ae->component); ae->component = NULL; }
  299. freez(ae->type);
  300. ae->type = strdupz(pointers[30]);
  301. if(!*ae->type) { freez(ae->type); ae->type = NULL; }
  302. }
  303. char value_string[100 + 1];
  304. freez(ae->old_value_string);
  305. freez(ae->new_value_string);
  306. ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
  307. ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
  308. // add it to host if not already there
  309. if(unlikely(*pointers[0] == 'A')) {
  310. ae->next = host->health_log.alarms;
  311. host->health_log.alarms = ae;
  312. sql_health_alarm_log_insert(host, ae);
  313. loaded++;
  314. }
  315. else {
  316. sql_health_alarm_log_update(host, ae);
  317. updated++;
  318. }
  319. if(unlikely(ae->unique_id > host->health_max_unique_id))
  320. host->health_max_unique_id = ae->unique_id;
  321. if(unlikely(ae->alarm_id >= host->health_max_alarm_id))
  322. host->health_max_alarm_id = ae->alarm_id;
  323. }
  324. else {
  325. error("HEALTH [%s]: line %zu of file '%s' is invalid (unrecognized entry type '%s').", host->hostname, line, filename, pointers[0]);
  326. errored++;
  327. }
  328. }
  329. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  330. freez(buf);
  331. if(!host->health_max_unique_id) host->health_max_unique_id = (uint32_t)now_realtime_sec();
  332. if(!host->health_max_alarm_id) host->health_max_alarm_id = (uint32_t)now_realtime_sec();
  333. host->health_log.next_log_id = host->health_max_unique_id + 1;
  334. if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id))
  335. host->health_log.next_alarm_id = host->health_max_alarm_id + 1;
  336. debug(D_HEALTH, "HEALTH [%s]: loaded file '%s' with %zd new alarm entries, updated %zd alarms, errors %zd entries, duplicate %zd", host->hostname, filename, loaded, updated, errored, duplicate);
  337. return loaded;
  338. }
  339. inline void health_alarm_log_load(RRDHOST *host) {
  340. health_alarm_log_close(host);
  341. char filename[FILENAME_MAX + 1];
  342. snprintfz(filename, FILENAME_MAX, "%s.old", host->health_log_filename);
  343. FILE *fp = fopen(filename, "r");
  344. if(!fp)
  345. error("HEALTH [%s]: cannot open health file: %s", host->hostname, filename);
  346. else {
  347. health_alarm_log_read(host, fp, filename);
  348. fclose(fp);
  349. }
  350. host->health_log_entries_written = 0;
  351. fp = fopen(host->health_log_filename, "r");
  352. if(!fp)
  353. error("HEALTH [%s]: cannot open health file: %s", host->hostname, host->health_log_filename);
  354. else {
  355. health_alarm_log_read(host, fp, host->health_log_filename);
  356. fclose(fp);
  357. }
  358. }
  359. // ----------------------------------------------------------------------------
  360. // health alarm log management
  361. inline ALARM_ENTRY* health_create_alarm_entry(
  362. RRDHOST *host,
  363. uint32_t alarm_id,
  364. uint32_t alarm_event_id,
  365. uuid_t config_hash_id,
  366. time_t when,
  367. const char *name,
  368. const char *chart,
  369. const char *chart_context,
  370. const char *family,
  371. const char *class,
  372. const char *component,
  373. const char *type,
  374. const char *exec,
  375. const char *recipient,
  376. time_t duration,
  377. NETDATA_DOUBLE old_value,
  378. NETDATA_DOUBLE new_value,
  379. RRDCALC_STATUS old_status,
  380. RRDCALC_STATUS new_status,
  381. const char *source,
  382. const char *units,
  383. const char *info,
  384. int delay,
  385. uint32_t flags
  386. ) {
  387. debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
  388. ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
  389. ae->name = strdupz(name);
  390. ae->hash_name = simple_hash(ae->name);
  391. if(chart) {
  392. ae->chart = strdupz(chart);
  393. ae->hash_chart = simple_hash(ae->chart);
  394. }
  395. if(chart_context)
  396. ae->chart_context = strdupz(chart_context);
  397. uuid_copy(ae->config_hash_id, *((uuid_t *) config_hash_id));
  398. if(family)
  399. ae->family = strdupz(family);
  400. if (class)
  401. ae->classification = strdupz(class);
  402. if (component)
  403. ae->component = strdupz(component);
  404. if (type)
  405. ae->type = strdupz(type);
  406. if(exec) ae->exec = strdupz(exec);
  407. if(recipient) ae->recipient = strdupz(recipient);
  408. if(source) ae->source = strdupz(source);
  409. if(units) ae->units = strdupz(units);
  410. ae->unique_id = host->health_log.next_log_id++;
  411. ae->alarm_id = alarm_id;
  412. ae->alarm_event_id = alarm_event_id;
  413. ae->when = when;
  414. ae->old_value = old_value;
  415. ae->new_value = new_value;
  416. char value_string[100 + 1];
  417. ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
  418. ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
  419. char *replaced_info = NULL;
  420. if (likely(info)) {
  421. char *m;
  422. replaced_info = strdupz(info);
  423. size_t pos = 0;
  424. while ((m = strstr(replaced_info + pos, "$family"))) {
  425. char *buf = NULL;
  426. pos = m - replaced_info;
  427. buf = find_and_replace(replaced_info, "$family", (ae->family) ? ae->family : "", m);
  428. freez(replaced_info);
  429. replaced_info = strdupz(buf);
  430. freez(buf);
  431. }
  432. }
  433. if(replaced_info) ae->info = strdupz(replaced_info);
  434. freez(replaced_info);
  435. ae->old_status = old_status;
  436. ae->new_status = new_status;
  437. ae->duration = duration;
  438. ae->delay = delay;
  439. ae->delay_up_to_timestamp = when + delay;
  440. ae->flags |= flags;
  441. ae->last_repeat = 0;
  442. if(ae->old_status == RRDCALC_STATUS_WARNING || ae->old_status == RRDCALC_STATUS_CRITICAL)
  443. ae->non_clear_duration += ae->duration;
  444. return ae;
  445. }
  446. inline void health_alarm_log(
  447. RRDHOST *host,
  448. ALARM_ENTRY *ae
  449. ) {
  450. debug(D_HEALTH, "Health adding alarm log entry with id: %u", ae->unique_id);
  451. // link it
  452. netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
  453. ae->next = host->health_log.alarms;
  454. host->health_log.alarms = ae;
  455. host->health_log.count++;
  456. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  457. // match previous alarms
  458. netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
  459. ALARM_ENTRY *t;
  460. for(t = host->health_log.alarms ; t ; t = t->next) {
  461. if(t != ae && t->alarm_id == ae->alarm_id) {
  462. if(!(t->flags & HEALTH_ENTRY_FLAG_UPDATED) && !t->updated_by_id) {
  463. t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
  464. t->updated_by_id = ae->unique_id;
  465. ae->updates_id = t->unique_id;
  466. if((t->new_status == RRDCALC_STATUS_WARNING || t->new_status == RRDCALC_STATUS_CRITICAL) &&
  467. (t->old_status == RRDCALC_STATUS_WARNING || t->old_status == RRDCALC_STATUS_CRITICAL))
  468. ae->non_clear_duration += t->non_clear_duration;
  469. health_alarm_log_save(host, t);
  470. }
  471. // no need to continue
  472. break;
  473. }
  474. }
  475. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  476. health_alarm_log_save(host, ae);
  477. }
  478. inline void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae) {
  479. freez(ae->name);
  480. freez(ae->chart);
  481. freez(ae->chart_context);
  482. freez(ae->family);
  483. freez(ae->classification);
  484. freez(ae->component);
  485. freez(ae->type);
  486. freez(ae->exec);
  487. freez(ae->recipient);
  488. freez(ae->source);
  489. freez(ae->units);
  490. freez(ae->info);
  491. freez(ae->old_value_string);
  492. freez(ae->new_value_string);
  493. freez(ae);
  494. }
  495. inline void health_alarm_log_free(RRDHOST *host) {
  496. rrdhost_check_wrlock(host);
  497. netdata_rwlock_wrlock(&host->health_log.alarm_log_rwlock);
  498. ALARM_ENTRY *ae;
  499. while((ae = host->health_log.alarms)) {
  500. host->health_log.alarms = ae->next;
  501. health_alarm_log_free_one_nochecks_nounlink(ae);
  502. }
  503. netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
  504. }