See initial patch attached. (30 seconds between reports is just to make testing faster.)
Index: server/mpm/worker/worker.c =================================================================== --- server/mpm/worker/worker.c (revision 906552) +++ server/mpm/worker/worker.c (working copy) @@ -1550,15 +1550,11 @@ /* no threads are "inactive" - starting, stopping, etc. */ /* have we reached MaxClients, or just getting close? */ if (0 == idle_thread_count) { - static int reported = 0; - if (!reported) { - /* only report this condition once */ - ap_log_error(APLOG_MARK, APLOG_ERR, 0, - ap_server_conf, - "server reached MaxClients setting, consider" - " raising the MaxClients setting"); - reported = 1; - } + static ap_log_interval_t interval = {0}; + ap_log_error_interval(APLOG_MARK, APLOG_ERR, apr_time_from_sec(30), 0, + &interval, 0, ap_server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); } else { static int reported = 0; if (!reported) { Index: include/http_log.h =================================================================== --- include/http_log.h (revision 906552) +++ include/http_log.h (working copy) @@ -169,6 +169,17 @@ const char *fmt, ...) __attribute__((format(printf,6,7))); +typedef struct ap_log_interval_t { + apr_time_t last_report; + apr_int32_t count; +} ap_log_interval_t; + +AP_DECLARE(void) ap_log_error_interval(const char *file, int line, int level, + apr_time_t max_interval, apr_int32_t max_count, + ap_log_interval_t *state, + apr_status_t status, const server_rec *s, + const char *fmt, ...); + /** * ap_log_perror() - log messages which are not related to a particular * request, connection, or virtual server. This uses a printf-like Index: server/log.c =================================================================== --- server/log.c (revision 906552) +++ server/log.c (working copy) @@ -732,6 +732,41 @@ va_end(args); } +AP_DECLARE(void) ap_log_error_interval(const char *file, int line, int level, + apr_time_t max_interval, apr_int32_t max_count, + ap_log_interval_t *state, + apr_status_t status, const server_rec *s, + const char *fmt, ...) +{ + va_list args; + char fmt_buf[512]; + apr_time_t now; + int report = 0; + + ++state->count; + + if (max_interval != 0) { + now = apr_time_now(); + if ((now - state->last_report) >= max_interval) { + report = 1; + } + } + else if (state->count >= max_count) { + report = 1; + } + + if (report) { + apr_snprintf(fmt_buf, sizeof fmt_buf, "%s (occurred %u times)", + fmt, state->count); + va_start(args, fmt); + log_error_core(file, line, level, status, s, NULL, NULL, NULL, fmt_buf, args); + va_end(args); + + state->count = 0; + state->last_report = apr_time_now(); + } +} + AP_DECLARE(void) ap_log_perror(const char *file, int line, int level, apr_status_t status, apr_pool_t *p, const char *fmt, ...)