comparison src/core/ngx_log.c @ 9299:2706b60dc225 default tip

Core: error logging rate limiting. With this change, error logging to files can be rate-limited with the "rate=" parameter. The parameter specifies allowed log messages rate to a particular file (per worker), in messages per second (m/s). By default, "rate=1000m/s" is used. Rate limiting is implemented using the "leaky bucket" method, similarly to the limit_req module. Maximum burst size is set to the number of log messages per second for each severity level, so "error" messages are logged even if the rate limit is hit by "info" messages (but not vice versa). When the limit is reached for a particular level, the "too many log messages, limiting" message is logged at this level. If debug logging is enabled, either for the particular log file or for the particular connection, rate limiting is not used.
author Maxim Dounin <mdounin@mdounin.ru>
date Tue, 25 Jun 2024 22:58:56 +0300
parents 14770557be17
children
comparison
equal deleted inserted replaced
9298:14770557be17 9299:2706b60dc225
7 7
8 #include <ngx_config.h> 8 #include <ngx_config.h>
9 #include <ngx_core.h> 9 #include <ngx_core.h>
10 10
11 11
12 static ngx_int_t ngx_log_check_rate(ngx_log_t *log, ngx_uint_t level);
12 static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 13 static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
13 static char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log); 14 static char *ngx_log_set_params(ngx_conf_t *cf, ngx_log_t *log);
14 static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log); 15 static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log);
15 16
16 17
17 #if (NGX_DEBUG) 18 #if (NGX_DEBUG)
18 19
162 163
163 if (log->log_level < level && !debug_connection) { 164 if (log->log_level < level && !debug_connection) {
164 break; 165 break;
165 } 166 }
166 167
168 if (log->limit && !debug_connection) {
169 if (ngx_log_check_rate(log, level) == NGX_BUSY) {
170 goto next;
171 }
172 }
173
167 if (log->writer) { 174 if (log->writer) {
168 log->writer(log, level, errstr, p - errstr); 175 log->writer(log, level, errstr, p - errstr);
169 goto next; 176 goto next;
170 } 177 }
171 178
309 if (buf < last) { 316 if (buf < last) {
310 *buf++ = ')'; 317 *buf++ = ')';
311 } 318 }
312 319
313 return buf; 320 return buf;
321 }
322
323
324 static ngx_int_t
325 ngx_log_check_rate(ngx_log_t *log, ngx_uint_t level)
326 {
327 ngx_log_t temp_log;
328 ngx_int_t excess, changed, burst;
329 ngx_atomic_int_t ms;
330 ngx_atomic_uint_t now, last;
331
332 now = ngx_current_msec;
333
334 last = log->limit->last;
335 excess = log->limit->excess;
336
337 ms = (ngx_atomic_int_t) (now - last);
338
339 if (ms < -60000) {
340 ms = 1;
341
342 } else if (ms < 0) {
343 ms = 0;
344 }
345
346 changed = excess - log->limit->rate * ms / 1000 + 1000;
347
348 if (changed < 0) {
349 changed = 0;
350 }
351
352 burst = (log->log_level - level + 1) * log->limit->rate;
353
354 if (changed > burst) {
355 if (excess <= burst) {
356
357 ngx_atomic_fetch_add(&log->limit->excess, 1000);
358
359 /* log message to this log only */
360
361 temp_log = *log;
362 temp_log.connection = 0;
363 temp_log.handler = NULL;
364 temp_log.limit = NULL;
365 temp_log.next = NULL;
366
367 ngx_log_error(level, &temp_log, 0,
368 "too many log messages, limiting");
369 }
370
371 return NGX_BUSY;
372 }
373
374 if (ms > 0
375 && ngx_atomic_cmp_set(&log->limit->last, last, now))
376 {
377 ngx_atomic_fetch_add(&log->limit->excess, changed - excess);
378
379 } else {
380 ngx_atomic_fetch_add(&log->limit->excess, 1000);
381 }
382
383 return NGX_OK;
314 } 384 }
315 385
316 386
317 ngx_log_t * 387 ngx_log_t *
318 ngx_log_init(u_char *prefix, u_char *error_log) 388 ngx_log_init(u_char *prefix, u_char *error_log)
596 if (new_log->file == NULL) { 666 if (new_log->file == NULL) {
597 return NGX_CONF_ERROR; 667 return NGX_CONF_ERROR;
598 } 668 }
599 } 669 }
600 670
601 if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) { 671 if (ngx_log_set_params(cf, new_log) != NGX_CONF_OK) {
602 return NGX_CONF_ERROR; 672 return NGX_CONF_ERROR;
603 } 673 }
604 674
605 if (*head != new_log) { 675 if (*head != new_log) {
606 ngx_log_insert(*head, new_log); 676 ngx_log_insert(*head, new_log);
609 return NGX_CONF_OK; 679 return NGX_CONF_OK;
610 } 680 }
611 681
612 682
613 static char * 683 static char *
614 ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log) 684 ngx_log_set_params(ngx_conf_t *cf, ngx_log_t *log)
615 { 685 {
686 size_t len;
687 ngx_int_t rate;
616 ngx_uint_t i, n, d; 688 ngx_uint_t i, n, d;
617 ngx_str_t *value; 689 ngx_str_t *value;
618 690
619 value = cf->args->elts; 691 value = cf->args->elts;
692
693 rate = 1000;
620 694
621 for (i = 2; i < cf->args->nelts; i++) { 695 for (i = 2; i < cf->args->nelts; i++) {
622 696
623 for (n = 1; n <= NGX_LOG_DEBUG; n++) { 697 for (n = 1; n <= NGX_LOG_DEBUG; n++) {
624 if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) { 698 if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {
647 log->log_level |= d; 721 log->log_level |= d;
648 goto next; 722 goto next;
649 } 723 }
650 } 724 }
651 725
652 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 726 if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
653 "invalid log level \"%V\"", &value[i]); 727
728 len = value[i].len;
729
730 if (ngx_strncmp(value[i].data + len - 3, "m/s", 3) == 0) {
731 len -= 3;
732 }
733
734 rate = ngx_atoi(value[i].data + 5, len - 5);
735 if (rate < 0) {
736 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
737 "invalid rate \"%V\"", &value[i]);
738 return NGX_CONF_ERROR;
739 }
740
741 continue;
742 }
743
744 if (log->log_level) {
745 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
746 "invalid parameter \"%V\"", &value[i]);
747
748 } else {
749 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
750 "invalid log level \"%V\"", &value[i]);
751 }
752
654 return NGX_CONF_ERROR; 753 return NGX_CONF_ERROR;
655 754
656 next: 755 next:
657 756
658 continue; 757 continue;
662 log->log_level = NGX_LOG_ERR; 761 log->log_level = NGX_LOG_ERR;
663 } 762 }
664 763
665 if (log->log_level == NGX_LOG_DEBUG) { 764 if (log->log_level == NGX_LOG_DEBUG) {
666 log->log_level = NGX_LOG_DEBUG_ALL; 765 log->log_level = NGX_LOG_DEBUG_ALL;
766 }
767
768 if (rate > 0
769 && log->log_level < NGX_LOG_DEBUG)
770 {
771 log->limit = ngx_pcalloc(cf->pool, sizeof(ngx_log_limit_t));
772 if (log->limit == NULL) {
773 return NGX_CONF_ERROR;
774 }
775
776 log->limit->rate = rate * 1000;
667 } 777 }
668 778
669 return NGX_CONF_OK; 779 return NGX_CONF_OK;
670 } 780 }
671 781