Freeciv-3.2
Loading...
Searching...
No Matches
log.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <signal.h>
19#include <stdarg.h>
20#include <stdio.h>
21#include <string.h>
22
23/* utility */
24#include "deprecations.h"
25#include "fciconv.h"
26#include "fcintl.h"
27#include "fcthread.h"
28#include "mem.h"
29#include "shared.h"
30#include "support.h"
31
32#include "log.h"
33
34static void log_write(FILE *fs, enum log_level level, bool print_from_where,
35 const char *where, const char *message);
36static void log_real(enum log_level level, bool print_from_where,
37 const char *where, const char *msg);
38
39static char *log_filename = NULL;
43
45
46#ifdef FREECIV_DEBUG
47static const enum log_level max_level = LOG_DEBUG;
48#else
49static const enum log_level max_level = LOG_VERBOSE;
50#endif /* FREECIV_DEBUG */
51
53static int fc_fatal_assertions = -1;
54
55#ifdef FREECIV_DEBUG
56struct log_fileinfo {
57 char *name;
58 enum log_level level;
59 unsigned int min;
60 unsigned int max;
61};
62static int log_num_files = 0;
63static struct log_fileinfo *log_files = NULL;
64#endif /* FREECIV_DEBUG */
65
66static char *log_level_names[] = {
67 "Fatal", "Error", "Warning", "Normal", "Verbose", "Debug", NULL
68};
69
70/* A helper variable to indicate that there is no log message. The '%s' is
71 * added to use it as format string as well as the argument. */
72const char *nologmsg = "nologmsg:%s";
73
74/**********************************************************************/
87{
88 const char *c;
89 int n = 0; /* number of filenames */
90 unsigned int level;
91 int ln;
92 int first_len = -1;
93#ifdef FREECIV_DEBUG
94 const char *tok;
95 int i;
96 char *dupled;
97 bool ret = TRUE;
98#endif /* FREECIV_DEBUG */
99
100 c = level_str;
101 n = 0;
102 while ((c = strchr(c, ':'))) {
103 if (first_len < 0) {
104 first_len = c - level_str;
105 }
106 c++;
107 n++;
108 }
109 if (n == 0) {
110 /* Global log level. */
111 if (!str_to_uint(level_str, &level)) {
112 level = LOG_DEBUG + 1;
113 for (ln = 0; log_level_names[ln] != NULL && level > LOG_DEBUG; ln++) {
115 level = ln;
116 }
117 }
118 if (level > LOG_DEBUG) {
119 fc_fprintf(stderr, _("Bad log level \"%s\".\n"), level_str);
120 return FALSE;
121 }
122 } else {
123 /* TRANS: Do not translate log level names that user has to provide in English */
124 deprecation_pending( _("Do not provide log level with a numerical value."
125 " Use one of the levels Fatal, Error, Warning, Normal, Verbose, Debug") );
126 }
127 if (level <= max_level) {
128 if (NULL != ret_level) {
129 *ret_level = level;
130 }
131 return TRUE;
132 } else {
133 fc_fprintf(stderr, _("Bad log level %d in \"%s\".\n"),
135#ifndef FREECIV_DEBUG
136 if (level == max_level + 1) {
138 _("Freeciv must be compiled with the FREECIV_DEBUG flag "
139 "to use debug level %d.\n"), max_level + 1);
140 }
141#endif /* FREECIV_DEBUG */
142 return FALSE;
143 }
144 }
145
146#ifdef FREECIV_DEBUG
147 c = level_str;
148 level = LOG_DEBUG + 1;
149 if (first_len > 0) {
150 for (ln = 0; log_level_names[ln] != NULL && level > LOG_DEBUG; ln++) {
152 level = ln;
153 }
154 }
155 }
156 if (level > LOG_DEBUG) {
157 level = c[0] - '0';
158 if (c[1] == ':') {
159 if (level > max_level) {
160 fc_fprintf(stderr, _("Bad log level %c in \"%s\".\n"),
161 c[0], level_str);
162 return FALSE;
163 }
164 } else {
165 fc_fprintf(stderr, _("Badly formed log level argument \"%s\".\n"),
166 level_str);
167 return FALSE;
168 }
169 }
171 log_num_files += n;
173 log_num_files * sizeof(struct log_fileinfo));
174
175 dupled = fc_strdup(c + 2);
176 tok = strtok(dupled, ":");
177
178 if (!tok) {
179 fc_fprintf(stderr, _("Badly formed log level argument \"%s\".\n"),
180 level_str);
181 ret = FALSE;
182 goto out;
183 }
184 do {
185 struct log_fileinfo *pfile = log_files + i;
186 const char *dtmp = strchr(tok, ',');
187
188 pfile->min = 0;
189 pfile->max = 0;
190 pfile->level = level;
191 if (dtmp) {
192 char *d, *dm;
193 char *pc;
194
195 dm = fc_malloc(strlen(dtmp) + 1);
196 strcpy(dm, dtmp);
197
198 pc = dm + 1;
199
200 dm[0] = '\0';
201 d = strchr(dm + 1, ',');
202 if (d && *pc != '\0' && d[1] != '\0') {
203 d[0] = '\0';
204 if (!str_to_uint(pc, &pfile->min)) {
205 fc_fprintf(stderr, _("Not an unsigned integer: '%s'\n"), pc);
206 ret = FALSE;
207 goto out;
208 }
209 if (!str_to_uint(d + 1, &pfile->max)) {
210 fc_fprintf(stderr, _("Not an unsigned integer: '%s'\n"), d + 1);
211 ret = FALSE;
212 goto out;
213 }
214 }
215
216 free(dm);
217 }
218 if (strlen(tok) == 0) {
219 fc_fprintf(stderr, _("Empty filename in log level argument \"%s\".\n"),
220 level_str);
221 ret = FALSE;
222 goto out;
223 }
224 pfile->name = fc_strdup(tok);
225 i++;
226 tok = strtok(NULL, ":");
227 } while (tok);
228
229 if (i != log_num_files) {
230 fc_fprintf(stderr, _("Badly formed log level argument \"%s\".\n"),
231 level_str);
232 ret = FALSE;
233 goto out;
234 }
235
236out:
237 free(dupled);
238 return ret;
239#else /* FREECIV_DEBUG */
241 _("Freeciv must be compiled with the FREECIV_DEBUG flag "
242 "to use advanced log levels based on files.\n"));
243 return FALSE;
244#endif /* FREECIV_DEBUG */
245}
246
247/**********************************************************************/
253void log_init(const char *filename, enum log_level initial_level,
256{
258 if (log_filename) {
261 }
262 if (filename && strlen(filename) > 0) {
263 log_filename = fc_strdup(filename);
264 } else {
266 }
268 log_prefix = prefix;
271 log_verbose("log started");
272 log_debug("LOG_DEBUG test");
273}
274
275/**********************************************************************/
278void log_close(void)
279{
281}
282
283/**********************************************************************/
294
295/**********************************************************************/
306
307/**********************************************************************/
311{
313
314 log_prefix = prefix;
315
316 return old;
317}
318
319/**********************************************************************/
323{
325}
326
327/**********************************************************************/
331{
332 return fc_log_level;
333}
334
335/**********************************************************************/
338const char *log_level_name(enum log_level lvl)
339{
341 return NULL;
342 }
343
344 return log_level_names[lvl];
345}
346
347#ifdef FREECIV_DEBUG
348/**********************************************************************/
353 const char *file, int line)
354{
355 struct log_fileinfo *pfile;
356 int i;
357
358 for (i = 0, pfile = log_files; i < log_num_files; i++, pfile++) {
359 if (pfile->level >= level
360 && 0 == strcmp(pfile->name, file)
361 && ((0 == pfile->min && 0 == pfile->max)
362 || (pfile->min <= line && pfile->max >= line))) {
363 return TRUE;
364 }
365 }
366 return (fc_log_level >= level);
367}
368#endif /* FREECIV_DEBUG */
369
370/**********************************************************************/
375 const char *where, const char *message)
376{
377 if (log_filename || (!log_callback)) {
378 char prefix[128];
379
380 if (log_prefix) {
381 /* Get the log prefix. */
382 fc_snprintf(prefix, sizeof(prefix), "[%s] ", log_prefix());
383 } else {
384 prefix[0] = '\0';
385 }
386
387 if (log_filename || (print_from_where && where)) {
388 fc_fprintf(fs, "%d: %s%s%s\n", level, prefix, where, message);
389 } else {
390 fc_fprintf(fs, "%d: %s%s\n", level, prefix, message);
391 }
392 fflush(fs);
393 }
394
395 if (log_callback) {
396 if (print_from_where) {
397 char buf[MAX_LEN_LOG_LINE];
398
399 fc_snprintf(buf, sizeof(buf), "%s%s", where, message);
401 } else {
403 }
404 }
405}
406
407/**********************************************************************/
411void vdo_log(const char *file, const char *function, int line,
413 char *buf, int buflen, const char *message, va_list args)
414{
416
417 /* There used to be check against recursive logging here, but
418 * the way it worked prevented any kind of simultaneous logging,
419 * not just recursive. Multiple threads should be able to log
420 * simultaneously. */
421
423 fc_snprintf(buf_where, sizeof(buf_where), "in %s() [%s::%d]: ",
424 function, file, line);
425
426 /* In the default configuration log_pre_callback is equal to log_real(). */
427 if (log_pre_callback) {
429 }
430}
431
432/**********************************************************************/
439 const char *where, const char *msg)
440{
441 static char last_msg[MAX_LEN_LOG_LINE] = "";
442 static unsigned int repeated = 0; /* Total times current message repeated */
443 static unsigned int next = 2; /* Next total to print update */
444 static unsigned int prev = 0; /* Total on last update */
445 /* Only count as repeat if same level */
446 static enum log_level prev_level = -1;
447 char buf[MAX_LEN_LOG_LINE];
448 FILE *fs;
449
450 if (log_filename) {
452 if (!(fs = fc_fopen(log_filename, "a"))) {
454 _("Couldn't open logfile: %s for appending \"%s\".\n"),
455 log_filename, msg);
457 }
458 } else {
459 fs = stderr;
460 }
461
462 if (level == prev_level && !fc_strncmp(msg, last_msg,
463 MAX_LEN_LOG_LINE - 1)) {
464 repeated++;
465 if (repeated == next) {
466 fc_snprintf(buf, sizeof(buf),
467 PL_("last message repeated %d time",
468 "last message repeated %d times",
469 repeated-prev), repeated-prev);
470 if (repeated > 2) {
471 cat_snprintf(buf, sizeof(buf),
472 /* TRANS: preserve leading space */
473 PL_(" (total %d repeat)",
474 " (total %d repeats)",
476 }
478 prev = repeated;
479 next *= 2;
480 }
481 } else {
482 if (repeated > 0 && repeated != prev) {
483 if (repeated == 1) {
484 /* just repeat the previous message: */
486 } else {
487 fc_snprintf(buf, sizeof(buf),
488 PL_("last message repeated %d time",
489 "last message repeated %d times",
490 repeated - prev), repeated - prev);
491 if (repeated > 2) {
492 cat_snprintf(buf, sizeof(buf),
493 PL_(" (total %d repeat)", " (total %d repeats)",
495 }
497 }
498 }
500 repeated = 0;
501 next = 2;
502 prev = 0;
504 }
505 /* Save last message. */
506 sz_strlcpy(last_msg, msg);
507
508 fflush(fs);
509 if (log_filename) {
510 fclose(fs);
512 }
513}
514
515/**********************************************************************/
522void do_log(const char *file, const char *function, int line,
524 const char *message, ...)
525{
526 char buf[MAX_LEN_LOG_LINE];
527 va_list args;
528
529 va_start(args, message);
532 va_end(args);
533}
534
535/**********************************************************************/
543
544#ifndef FREECIV_NDEBUG
545/**********************************************************************/
549void fc_assert_fail(const char *file, const char *function, int line,
550 const char *assertion, const char *message, ...)
551{
553
554 if (NULL != assertion) {
555 do_log(file, function, line, TRUE, level,
556 "assertion '%s' failed.", assertion);
557 }
558
559 if (NULL != message && NOLOGMSG != message) {
560 /* Additional message. */
561 char buf[MAX_LEN_LOG_LINE];
562 va_list args;
563
564 va_start(args, message);
566 message, args);
567 va_end(args);
568 }
569
570 do_log(file, function, line, FALSE, level,
571 /* TRANS: No full stop after the URL, could cause confusion. */
572 _("Please report this message at %s"), BUG_URL);
573
574 if (0 <= fc_fatal_assertions) {
575 /* Emit a signal. */
577 }
578}
579#endif /* FREECIV_NDEBUG */
#define n
Definition astring.c:77
char * incite_cost
Definition comments.c:75
void deprecation_pending(const char *format,...)
void fc_fprintf(FILE *stream, const char *format,...) fc__attribute((__format__(__printf__
#define PL_(String1, String2, n)
Definition fcintl.h:71
#define _(String)
Definition fcintl.h:67
void fc_mutex_allocate(fc_mutex *mutex)
void fc_mutex_init(fc_mutex *mutex)
void fc_mutex_release(fc_mutex *mutex)
void fc_mutex_destroy(fc_mutex *mutex)
const char * name
Definition inputfile.c:127
void fc_assert_fail(const char *file, const char *function, int line, const char *assertion, const char *message,...)
Definition log.c:549
static log_pre_callback_fn log_pre_callback
Definition log.c:40
static enum log_level max_level
Definition log.c:49
static void log_real(enum log_level level, bool print_from_where, const char *where, const char *msg)
Definition log.c:438
void log_close(void)
Definition log.c:278
const char * nologmsg
Definition log.c:72
void log_init(const char *filename, enum log_level initial_level, log_callback_fn callback, log_prefix_fn prefix, int fatal_assertions)
Definition log.c:253
static char * log_level_names[]
Definition log.c:66
static char * log_filename
Definition log.c:39
void fc_assert_set_fatal(int fatal_assertions)
Definition log.c:539
static log_callback_fn log_callback
Definition log.c:41
void log_set_level(enum log_level level)
Definition log.c:322
log_callback_fn log_set_callback(log_callback_fn callback)
Definition log.c:298
const char * log_level_name(enum log_level lvl)
Definition log.c:338
static void log_write(FILE *fs, enum log_level level, bool print_from_where, const char *where, const char *message)
Definition log.c:374
log_pre_callback_fn log_set_pre_callback(log_pre_callback_fn precallback)
Definition log.c:286
void vdo_log(const char *file, const char *function, int line, bool print_from_where, enum log_level level, char *buf, int buflen, const char *message, va_list args)
Definition log.c:411
static int fc_fatal_assertions
Definition log.c:53
bool log_parse_level_str(const char *level_str, enum log_level *ret_level)
Definition log.c:86
log_prefix_fn log_set_prefix(log_prefix_fn prefix)
Definition log.c:310
void do_log(const char *file, const char *function, int line, bool print_from_where, enum log_level level, const char *message,...)
Definition log.c:522
static log_prefix_fn log_prefix
Definition log.c:42
enum log_level log_get_level(void)
Definition log.c:330
static enum log_level fc_log_level
Definition log.c:52
static fc_mutex logfile_mutex
Definition log.c:44
const char *(* log_prefix_fn)(void)
Definition log.h:58
void(* log_callback_fn)(enum log_level, const char *, bool file_too)
Definition log.h:54
#define log_verbose(message,...)
Definition log.h:109
void(* log_pre_callback_fn)(enum log_level, bool print_from_where, const char *where, const char *msg)
Definition log.h:47
#define MAX_LEN_LOG_LINE
Definition log.h:26
#define log_debug(message,...)
Definition log.h:115
#define NOLOGMSG
Definition log.h:44
log_level
Definition log.h:28
@ LOG_ERROR
Definition log.h:30
@ LOG_DEBUG
Definition log.h:34
@ LOG_NORMAL
Definition log.h:32
@ LOG_FATAL
Definition log.h:29
@ LOG_VERBOSE
Definition log.h:33
#define fc_strdup(str)
Definition mem.h:43
#define fc_realloc(ptr, sz)
Definition mem.h:36
#define fc_malloc(sz)
Definition mem.h:34
static int fatal_assertions
Definition ruledit.cpp:58
struct setting_list * level[OLEVELS_NUM]
Definition settings.c:190
bool str_to_uint(const char *str, unsigned int *pint)
Definition shared.c:551
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:974
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:1000
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:900
FILE * fc_fopen(const char *filename, const char *opentype)
Definition support.c:507
int fc_strncasecmp(const char *str0, const char *str1, size_t n)
Definition support.c:238
#define sz_strlcpy(dest, src)
Definition support.h:195
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define fc_strncmp(_s1_, _s2_, _len_)
Definition support.h:160