diff options
Diffstat (limited to 'src/util/common_logging.c')
-rw-r--r-- | src/util/common_logging.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/src/util/common_logging.c b/src/util/common_logging.c new file mode 100644 index 000000000..4068ed94b --- /dev/null +++ b/src/util/common_logging.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2006, 2008, 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/common_logging.c | ||
23 | * @brief error handling API | ||
24 | * | ||
25 | * @author Christian Grothoff | ||
26 | */ | ||
27 | #include "platform.h" | ||
28 | #include "gnunet_common.h" | ||
29 | #include "gnunet_crypto_lib.h" | ||
30 | #include "gnunet_strings_lib.h" | ||
31 | #include "gnunet_time_lib.h" | ||
32 | |||
33 | /** | ||
34 | * After how many seconds do we always print | ||
35 | * that "message X was repeated N times"? Use 12h. | ||
36 | */ | ||
37 | #define BULK_DELAY_THRESHOLD (12 * 60 * 60 * 1000) | ||
38 | |||
39 | /** | ||
40 | * After how many repetitions do we always print | ||
41 | * that "message X was repeated N times"? (even if | ||
42 | * we have not yet reached the delay threshold) | ||
43 | */ | ||
44 | #define BULK_REPEAT_THRESHOLD 1000 | ||
45 | |||
46 | /** | ||
47 | * How many characters do we use for matching of | ||
48 | * bulk messages? | ||
49 | */ | ||
50 | #define BULK_TRACK_SIZE 256 | ||
51 | |||
52 | /** | ||
53 | * How many characters can a date/time string | ||
54 | * be at most? | ||
55 | */ | ||
56 | #define DATE_STR_SIZE 64 | ||
57 | |||
58 | /** | ||
59 | * Linked list of active loggers. | ||
60 | */ | ||
61 | struct CustomLogger | ||
62 | { | ||
63 | /** | ||
64 | * This is a linked list. | ||
65 | */ | ||
66 | struct CustomLogger *next; | ||
67 | |||
68 | /** | ||
69 | * Log function. | ||
70 | */ | ||
71 | GNUNET_Logger logger; | ||
72 | |||
73 | /** | ||
74 | * Closure for logger. | ||
75 | */ | ||
76 | void *logger_cls; | ||
77 | }; | ||
78 | |||
79 | /** | ||
80 | * The last "bulk" error message that we have been logging. | ||
81 | * Note that this message maybe truncated to the first BULK_TRACK_SIZE | ||
82 | * characters, in which case it is NOT 0-terminated! | ||
83 | */ | ||
84 | static char last_bulk[BULK_TRACK_SIZE]; | ||
85 | |||
86 | /** | ||
87 | * Type of the last bulk message. | ||
88 | */ | ||
89 | static enum GNUNET_ErrorType last_bulk_kind; | ||
90 | |||
91 | /** | ||
92 | * Time of the last bulk error message (0 for none) | ||
93 | */ | ||
94 | static struct GNUNET_TIME_Absolute last_bulk_time; | ||
95 | |||
96 | /** | ||
97 | * Number of times that bulk message has been repeated since. | ||
98 | */ | ||
99 | static unsigned int last_bulk_repeat; | ||
100 | |||
101 | /** | ||
102 | * Component when the last bulk was logged. | ||
103 | */ | ||
104 | static const char *last_bulk_comp; | ||
105 | |||
106 | /** | ||
107 | * Running component. | ||
108 | */ | ||
109 | static const char *component; | ||
110 | |||
111 | /** | ||
112 | * Minimum log level. | ||
113 | */ | ||
114 | static enum GNUNET_ErrorType min_level; | ||
115 | |||
116 | /** | ||
117 | * Linked list of our custom loggres. | ||
118 | */ | ||
119 | static struct CustomLogger *loggers; | ||
120 | |||
121 | /** | ||
122 | * Number of log calls to ignore. | ||
123 | */ | ||
124 | static unsigned int skip_log; | ||
125 | |||
126 | /** | ||
127 | * Convert a textual description of a loglevel | ||
128 | * to the respective GNUNET_GE_KIND. | ||
129 | * @returns GNUNET_GE_INVALID if log does not parse | ||
130 | */ | ||
131 | static enum GNUNET_ErrorType | ||
132 | get_type (const char *log) | ||
133 | { | ||
134 | if (0 == strcasecmp (log, _("DEBUG"))) | ||
135 | return GNUNET_ERROR_TYPE_DEBUG; | ||
136 | if (0 == strcasecmp (log, _("INFO"))) | ||
137 | return GNUNET_ERROR_TYPE_INFO; | ||
138 | if (0 == strcasecmp (log, _("WARNING"))) | ||
139 | return GNUNET_ERROR_TYPE_WARNING; | ||
140 | if (0 == strcasecmp (log, _("ERROR"))) | ||
141 | return GNUNET_ERROR_TYPE_ERROR; | ||
142 | return GNUNET_ERROR_TYPE_INVALID; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Setup logging. | ||
147 | * | ||
148 | * @param comp default component to use | ||
149 | * @param loglevel what types of messages should be logged | ||
150 | */ | ||
151 | int | ||
152 | GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile) | ||
153 | { | ||
154 | FILE *altlog; | ||
155 | |||
156 | component = comp; | ||
157 | min_level = get_type (loglevel); | ||
158 | if (logfile == NULL) | ||
159 | return GNUNET_OK; | ||
160 | altlog = fopen (logfile, "a"); | ||
161 | if (altlog == NULL) | ||
162 | { | ||
163 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "fopen", logfile); | ||
164 | return GNUNET_SYSERR; | ||
165 | } | ||
166 | if (stderr != NULL) | ||
167 | fclose (stderr); | ||
168 | stderr = altlog; | ||
169 | return GNUNET_OK; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * Add a custom logger. | ||
174 | * | ||
175 | * @param logger log function | ||
176 | * @param logger_cls closure for logger | ||
177 | */ | ||
178 | void | ||
179 | GNUNET_logger_add (GNUNET_Logger logger, void *logger_cls) | ||
180 | { | ||
181 | struct CustomLogger *entry; | ||
182 | |||
183 | entry = GNUNET_malloc (sizeof (struct CustomLogger)); | ||
184 | entry->logger = logger; | ||
185 | entry->logger_cls = logger_cls; | ||
186 | entry->next = loggers; | ||
187 | loggers = entry; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Remove a custom logger. | ||
192 | * | ||
193 | * @param logger log function | ||
194 | * @param logger_cls closure for logger | ||
195 | */ | ||
196 | void | ||
197 | GNUNET_logger_remove (GNUNET_Logger logger, void *logger_cls) | ||
198 | { | ||
199 | struct CustomLogger *pos; | ||
200 | struct CustomLogger *prev; | ||
201 | |||
202 | prev = NULL; | ||
203 | pos = loggers; | ||
204 | while ((pos != NULL) && | ||
205 | ((pos->logger != logger) || (pos->logger_cls != logger_cls))) | ||
206 | { | ||
207 | prev = pos; | ||
208 | pos = pos->next; | ||
209 | } | ||
210 | GNUNET_assert (pos != NULL); | ||
211 | if (prev == NULL) | ||
212 | loggers = pos->next; | ||
213 | else | ||
214 | prev->next = pos->next; | ||
215 | GNUNET_free (pos); | ||
216 | } | ||
217 | |||
218 | static void | ||
219 | output_message (enum GNUNET_ErrorType kind, | ||
220 | const char *comp, const char *datestr, const char *msg) | ||
221 | { | ||
222 | struct CustomLogger *pos; | ||
223 | if (stderr != NULL) | ||
224 | fprintf (stderr, "%s %s %s %s", datestr, comp, | ||
225 | GNUNET_error_type_to_string (kind), msg); | ||
226 | pos = loggers; | ||
227 | while (pos != NULL) | ||
228 | { | ||
229 | pos->logger (pos->logger_cls, kind, comp, datestr, msg); | ||
230 | pos = pos->next; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | static void | ||
235 | flush_bulk (const char *datestr) | ||
236 | { | ||
237 | char msg[DATE_STR_SIZE + BULK_TRACK_SIZE + 256]; | ||
238 | int rev; | ||
239 | char *last; | ||
240 | char *ft; | ||
241 | |||
242 | if ((last_bulk_time.value == 0) || (last_bulk_repeat == 0)) | ||
243 | return; | ||
244 | rev = 0; | ||
245 | last = memchr (last_bulk, '\0', BULK_TRACK_SIZE); | ||
246 | if (last == NULL) | ||
247 | last = &last_bulk[BULK_TRACK_SIZE - 1]; | ||
248 | else if (last != last_bulk) | ||
249 | last--; | ||
250 | if (last[0] == '\n') | ||
251 | { | ||
252 | rev = 1; | ||
253 | last[0] = '\0'; | ||
254 | } | ||
255 | ft = | ||
256 | GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration | ||
257 | (last_bulk_time)); | ||
258 | snprintf (msg, sizeof (msg), | ||
259 | _("Message `%.*s' repeated %u times in the last %s\n"), | ||
260 | BULK_TRACK_SIZE, last_bulk, last_bulk_repeat, ft); | ||
261 | GNUNET_free (ft); | ||
262 | if (rev == 1) | ||
263 | last[0] = '\n'; | ||
264 | output_message (last_bulk_kind, last_bulk_comp, datestr, msg); | ||
265 | last_bulk_time = GNUNET_TIME_absolute_get (); | ||
266 | last_bulk_repeat = 0; | ||
267 | } | ||
268 | |||
269 | |||
270 | /** | ||
271 | * Ignore the next n calls to the log function. | ||
272 | * | ||
273 | * @param n number of log calls to ignore | ||
274 | */ | ||
275 | void | ||
276 | GNUNET_log_skip (unsigned int n) | ||
277 | { | ||
278 | int ok; | ||
279 | |||
280 | if (n == 0) | ||
281 | { | ||
282 | ok = (0 == skip_log); | ||
283 | skip_log = 0; | ||
284 | GNUNET_assert (ok); | ||
285 | } | ||
286 | skip_log += n; | ||
287 | } | ||
288 | |||
289 | |||
290 | static void | ||
291 | mylog (enum GNUNET_ErrorType kind, | ||
292 | const char *comp, const char *message, va_list va) | ||
293 | { | ||
294 | char date[DATE_STR_SIZE]; | ||
295 | time_t timetmp; | ||
296 | struct tm *tmptr; | ||
297 | size_t size; | ||
298 | char *buf; | ||
299 | va_list vacp; | ||
300 | |||
301 | if (skip_log > 0) | ||
302 | { | ||
303 | skip_log--; | ||
304 | return; | ||
305 | } | ||
306 | if ((kind & (~GNUNET_ERROR_TYPE_BULK)) > min_level) | ||
307 | return; | ||
308 | va_copy (vacp, va); | ||
309 | size = VSNPRINTF (NULL, 0, message, vacp) + 1; | ||
310 | va_end (vacp); | ||
311 | buf = malloc (size); | ||
312 | if (buf == NULL) | ||
313 | return; /* oops */ | ||
314 | VSNPRINTF (buf, size, message, va); | ||
315 | time (&timetmp); | ||
316 | memset (date, 0, DATE_STR_SIZE); | ||
317 | tmptr = localtime (&timetmp); | ||
318 | strftime (date, DATE_STR_SIZE, "%b %d %H:%M:%S", tmptr); | ||
319 | if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) && | ||
320 | (last_bulk_time.value != 0) && | ||
321 | (0 == strncmp (buf, last_bulk, sizeof (last_bulk)))) | ||
322 | { | ||
323 | last_bulk_repeat++; | ||
324 | if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).value > | ||
325 | BULK_DELAY_THRESHOLD) | ||
326 | || (last_bulk_repeat > BULK_REPEAT_THRESHOLD)) | ||
327 | flush_bulk (date); | ||
328 | free (buf); | ||
329 | return; | ||
330 | } | ||
331 | flush_bulk (date); | ||
332 | strncpy (last_bulk, buf, sizeof (last_bulk)); | ||
333 | last_bulk_repeat = 0; | ||
334 | last_bulk_kind = kind; | ||
335 | last_bulk_time = GNUNET_TIME_absolute_get (); | ||
336 | last_bulk_comp = comp; | ||
337 | output_message (kind, comp, date, buf); | ||
338 | free (buf); | ||
339 | } | ||
340 | |||
341 | |||
342 | void | ||
343 | GNUNET_log (enum GNUNET_ErrorType kind, const char *message, ...) | ||
344 | { | ||
345 | va_list va; | ||
346 | va_start (va, message); | ||
347 | mylog (kind, component, message, va); | ||
348 | va_end (va); | ||
349 | } | ||
350 | |||
351 | |||
352 | void | ||
353 | GNUNET_log_from (enum GNUNET_ErrorType kind, | ||
354 | const char *comp, const char *message, ...) | ||
355 | { | ||
356 | va_list va; | ||
357 | va_start (va, message); | ||
358 | mylog (kind, comp, message, va); | ||
359 | va_end (va); | ||
360 | } | ||
361 | |||
362 | |||
363 | /** | ||
364 | * Convert KIND to String | ||
365 | */ | ||
366 | const char * | ||
367 | GNUNET_error_type_to_string (enum GNUNET_ErrorType kind) | ||
368 | { | ||
369 | if ((kind & GNUNET_ERROR_TYPE_ERROR) > 0) | ||
370 | return _("ERROR"); | ||
371 | if ((kind & GNUNET_ERROR_TYPE_WARNING) > 0) | ||
372 | return _("WARNING"); | ||
373 | if ((kind & GNUNET_ERROR_TYPE_INFO) > 0) | ||
374 | return _("INFO"); | ||
375 | if ((kind & GNUNET_ERROR_TYPE_DEBUG) > 0) | ||
376 | return _("DEBUG"); | ||
377 | return _("INVALID"); | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * Convert a peer identity to a string (for printing debug messages). | ||
383 | * This is one of the very few calls in the entire API that is | ||
384 | * NOT reentrant! | ||
385 | * | ||
386 | * @param pid the peer identity | ||
387 | * @return string form of the pid; will be overwritten by next | ||
388 | * call to GNUNET_i2s. | ||
389 | */ | ||
390 | const char * | ||
391 | GNUNET_i2s (const struct GNUNET_PeerIdentity *pid) | ||
392 | { | ||
393 | static struct GNUNET_CRYPTO_HashAsciiEncoded ret; | ||
394 | GNUNET_CRYPTO_hash_to_enc (&pid->hashPubKey, &ret); | ||
395 | ret.encoding[4] = '\0'; | ||
396 | return (const char *) ret.encoding; | ||
397 | } | ||
398 | |||
399 | |||
400 | |||
401 | /* end of common_logging.c */ | ||