Index: trunk/loreley/src/include/log.h |
— | — | @@ -39,16 +39,18 @@ |
40 | 40 | bool open(void); |
41 | 41 | void close(void); |
42 | 42 | |
43 | | - void debug (format const &f) { _log(ll_debug, str(f)); } |
44 | | - void notice(format const &f) { _log(ll_notice, str(f));} |
45 | | - void warn (format const &f) { _log(ll_warn, str(f)); } |
46 | | - void error (format const &f) { _log(ll_error, str(f)); } |
| 43 | + void debug (format const &f) { log(ll_debug, str(f)); } |
| 44 | + void notice(format const &f) { log(ll_notice, str(f));} |
| 45 | + void warn (format const &f) { log(ll_warn, str(f)); } |
| 46 | + void error (format const &f) { log(ll_error, str(f)); } |
47 | 47 | |
48 | | - void debug (string const &s) { _log(ll_debug, s); } |
49 | | - void notice(string const &s) { _log(ll_notice, s); } |
50 | | - void warn (string const &s) { _log(ll_warn, s); } |
51 | | - void error (string const &s) { _log(ll_error, s); } |
| 48 | + void debug (string const &s) { log(ll_debug, s); } |
| 49 | + void notice(string const &s) { log(ll_notice, s); } |
| 50 | + void warn (string const &s) { log(ll_warn, s); } |
| 51 | + void error (string const &s) { log(ll_error, s); } |
52 | 52 | |
| 53 | + void log(log_level l, string const &); |
| 54 | + |
53 | 55 | private: |
54 | 56 | bool _syslog; |
55 | 57 | int _facility; |
— | — | @@ -58,12 +60,109 @@ |
59 | 61 | ofstream *_fp; |
60 | 62 | lockable _lock; |
61 | 63 | |
62 | | - void _log(log_level l, string const &); |
63 | 64 | void _log_unlocked(log_level l, string const &); |
64 | 65 | }; |
65 | 66 | |
66 | 67 | extern logger wlog; |
67 | 68 | |
| 69 | +/** |
| 70 | + * A class to easily rate-limit certain log messages, so the error log isn't |
| 71 | + * spammed too much. |
| 72 | + * |
| 73 | + * Example usage: |
| 74 | + * |
| 75 | + *\code |
| 76 | + * rate_limited_logger rlog(5, ll_error, "%d + %d = %d") |
| 77 | + * rlog.log(1, 1, 2); |
| 78 | + * rlog.log(2, 2, 4); |
| 79 | + * sleep(5); |
| 80 | + * rlog.log(3, 3, 6); |
| 81 | + * \code |
| 82 | + * |
| 83 | + * Only the first and third messages will be logged. |
| 84 | + */ |
| 85 | +struct rate_limited_logger { |
| 86 | + /** |
| 87 | + * Construct a new limited logger. |
| 88 | + * \param nsecs Minimum number of seconds between logging instances of |
| 89 | + * this message. |
| 90 | + * \param lev Log level of this message. |
| 91 | + * \param msg Format-string should this message. |
| 92 | + */ |
| 93 | + rate_limited_logger(int nsecs, log_level lev, string const &msg) |
| 94 | + : _nsecs(nsecs) |
| 95 | + , _lev(lev) |
| 96 | + , _format(msg) |
| 97 | + , _last(0) |
| 98 | + {} |
| 99 | + |
| 100 | + /** |
| 101 | + * Log a message with no arguments. Note that the msg is still a |
| 102 | + * format string and needs appropriate escaping. |
| 103 | + */ |
| 104 | + void log(void) { |
| 105 | + if (!again()) |
| 106 | + return; |
| 107 | + |
| 108 | + wlog.log(_lev, str(_format)); |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * Log a message with one argument. |
| 113 | + */ |
| 114 | + template<typename arg1_type> |
| 115 | + void log(arg1_type const &arg1) { |
| 116 | + if (!again()) |
| 117 | + return; |
| 118 | + |
| 119 | + format f(_format); |
| 120 | + f % arg1; |
| 121 | + wlog.log(_lev, str(f)); |
| 122 | + } |
| 123 | + |
| 124 | + /** |
| 125 | + * Log a message with two arguments. |
| 126 | + */ |
| 127 | + template<typename arg1_type, typename arg2_type> |
| 128 | + void log(arg1_type const &arg1, arg2_type const &arg2) { |
| 129 | + if (!again()) |
| 130 | + return; |
| 131 | + |
| 132 | + format f(_format); |
| 133 | + f % arg1 % arg2; |
| 134 | + wlog.log(_lev, str(f)); |
| 135 | + } |
| 136 | + |
| 137 | + /** |
| 138 | + * Log a message with three arguments. |
| 139 | + */ |
| 140 | + template<typename arg1_type, typename arg2_type, typename arg3_type> |
| 141 | + void log(arg1_type const &arg1, arg2_type const &arg2, |
| 142 | + arg3_type const &arg3) { |
| 143 | + if (!again()) |
| 144 | + return; |
| 145 | + |
| 146 | + format f(_format); |
| 147 | + f % arg1 % arg2 % arg3; |
| 148 | + wlog.log(_lev, str(f)); |
| 149 | + } |
| 150 | + |
| 151 | +private: |
| 152 | + bool again(void) { |
| 153 | + time_t now = time(0); |
| 154 | + if (_last + _nsecs < now) { |
| 155 | + _last = now; |
| 156 | + return true; |
| 157 | + } |
| 158 | + return false; |
| 159 | + } |
| 160 | + |
| 161 | + int _nsecs; |
| 162 | + log_level _lev; |
| 163 | + format _format; |
| 164 | + int _last; |
| 165 | +}; |
| 166 | + |
68 | 167 | #ifndef LORELEY_DEBUG |
69 | 168 | # define WDEBUG(x) ((void)0) |
70 | 169 | #else |
Index: trunk/loreley/src/loreley/log.cc |
— | — | @@ -68,7 +68,7 @@ |
69 | 69 | } |
70 | 70 | |
71 | 71 | void |
72 | | -logger::_log(log_level sev, string const &e) |
| 72 | +logger::log(log_level sev, string const &e) |
73 | 73 | { |
74 | 74 | HOLDING(_lock); |
75 | 75 | _log_unlocked(sev, e); |
Index: trunk/loreley/src/loreley/net.cc |
— | — | @@ -262,15 +262,13 @@ |
263 | 263 | ioloop_t::_accept(wsocket *s, int) |
264 | 264 | { |
265 | 265 | wsocket *newe; |
266 | | -static atomic<time_t> last_nfile = 0; |
267 | | - time_t now = time(NULL); |
| 266 | +static rate_limited_logger enfile_log(60, ll_warn, "accept error: %s"); |
268 | 267 | |
269 | 268 | if ((newe = s->accept("HTTP client", prio_norm)) == NULL) { |
270 | | - if ((errno != ENFILE && errno != EMFILE) || (now - last_nfile) > 60) { |
271 | | - if (errno == ENFILE || errno == EMFILE) |
272 | | - last_nfile = now; |
| 269 | + if (errno == ENFILE || errno == EMFILE) |
| 270 | + enfile_log.log(strerror(errno)); |
| 271 | + else |
273 | 272 | wlog.warn(format("accept error: %s") % strerror(errno)); |
274 | | - } |
275 | 273 | s->readback(bind(&ioloop_t::_accept, this, _1, _2), -1); |
276 | 274 | return; |
277 | 275 | } |
Index: trunk/loreley/src/loreley/backend.cc |
— | — | @@ -96,8 +96,6 @@ |
97 | 97 | { |
98 | 98 | struct backend_cb_data *cbd; |
99 | 99 | wsocket *s = NULL; |
100 | | -static time_t last_nfile; |
101 | | - time_t now = time(NULL); |
102 | 100 | |
103 | 101 | /* |
104 | 102 | * If we're delegating (for failover), pass this request off. |
— | — | @@ -129,11 +127,13 @@ |
130 | 128 | "backend connection", prio_backend); |
131 | 129 | s->nonblocking(true); |
132 | 130 | } catch (socket_error &e) { |
133 | | - if (e.err() != ENFILE || now - last_nfile > 60) |
| 131 | + static rate_limited_logger enfile_msg(5, ll_warn, |
| 132 | + "opening backend socket: %s"); |
| 133 | + if (e.err() == ENFILE || e.err() == EMFILE) |
| 134 | + enfile_msg.log(e.what()); |
| 135 | + else |
134 | 136 | wlog.warn(format("opening backend socket: %s") |
135 | 137 | % e.what()); |
136 | | - if (e.err() == ENFILE) |
137 | | - last_nfile = now; |
138 | 138 | delete cbd; |
139 | 139 | delete s; |
140 | 140 | return -1; |