Freeciv-3.3
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 = nullptr;
42static log_prefix_fn log_prefix = nullptr;
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 = nullptr;
64#endif /* FREECIV_DEBUG */
65
66static char *log_level_names[] = {
67 "Fatal", "Error", "Warning", "Normal", "Verbose", "Debug", nullptr
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] != nullptr && 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 (ret_level != nullptr) {
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] != nullptr && 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 char *d = strchr(tok, ',');
187
188 pfile->min = 0;
189 pfile->max = 0;
190 pfile->level = level;
191 if (d) {
192 char *pc = d + 1;
193
194 d[0] = '\0';
195 d = strchr(d + 1, ',');
196 if (d && *pc != '\0' && d[1] != '\0') {
197 d[0] = '\0';
198 if (!str_to_uint(pc, &pfile->min)) {
199 fc_fprintf(stderr, _("Not an unsigned integer: '%s'\n"), pc);
200 ret = FALSE;
201 goto out;
202 }
203 if (!str_to_uint(d + 1, &pfile->max)) {
204 fc_fprintf(stderr, _("Not an unsigned integer: '%s'\n"), d + 1);
205 ret = FALSE;
206 goto out;
207 }
208 }
209 }
210 if (strlen(tok) == 0) {
211 fc_fprintf(stderr, _("Empty filename in log level argument \"%s\".\n"),
212 level_str);
213 ret = FALSE;
214 goto out;
215 }
216 pfile->name = fc_strdup(tok);
217 i++;
218 tok = strtok(nullptr, ":");
219 } while (tok);
220
221 if (i != log_num_files) {
222 fc_fprintf(stderr, _("Badly formed log level argument \"%s\".\n"),
223 level_str);
224 ret = FALSE;
225 goto out;
226 }
227
228out:
229 free(dupled);
230 return ret;
231#else /* FREECIV_DEBUG */
233 _("Freeciv must be compiled with the FREECIV_DEBUG flag "
234 "to use advanced log levels based on files.\n"));
235 return FALSE;
236#endif /* FREECIV_DEBUG */
237}
238
239/**********************************************************************/
245void log_init(const char *filename, enum log_level initial_level,
248{
250 if (log_filename) {
252 log_filename = nullptr;
253 }
254 if (filename && strlen(filename) > 0) {
255 log_filename = fc_strdup(filename);
256 } else {
257 log_filename = nullptr;
258 }
260 log_prefix = prefix;
263 log_verbose("log started");
264 log_debug("LOG_DEBUG test");
265}
266
267/**********************************************************************/
270void log_close(void)
271{
273}
274
275/**********************************************************************/
286
287/**********************************************************************/
298
299/**********************************************************************/
303{
305
306 log_prefix = prefix;
307
308 return old;
309}
310
311/**********************************************************************/
315{
317}
318
319/**********************************************************************/
323{
324 return fc_log_level;
325}
326
327/**********************************************************************/
330const char *log_level_name(enum log_level lvl)
331{
333 return nullptr;
334 }
335
336 return log_level_names[lvl];
337}
338
339#ifdef FREECIV_DEBUG
340/**********************************************************************/
345 const char *file, int line)
346{
347 struct log_fileinfo *pfile;
348 int i;
349
350 for (i = 0, pfile = log_files; i < log_num_files; i++, pfile++) {
351 if (pfile->level >= level
352 && 0 == strcmp(pfile->name, file)
353 && ((0 == pfile->min && 0 == pfile->max)
354 || (pfile->min <= line && pfile->max >= line))) {
355 return TRUE;
356 }
357 }
358 return (fc_log_level >= level);
359}
360#endif /* FREECIV_DEBUG */
361
362/**********************************************************************/
367 const char *where, const char *message)
368{
369 if (log_filename || (!log_callback)) {
370 char prefix[128];
371
372 if (log_prefix) {
373 /* Get the log prefix. */
374 fc_snprintf(prefix, sizeof(prefix), "[%s] ", log_prefix());
375 } else {
376 prefix[0] = '\0';
377 }
378
379 if (log_filename || (print_from_where && where)) {
380 fc_fprintf(fs, "%d: %s%s%s\n", level, prefix, where, message);
381 } else {
382 fc_fprintf(fs, "%d: %s%s\n", level, prefix, message);
383 }
384 fflush(fs);
385 }
386
387 if (log_callback) {
388 if (print_from_where) {
389 char buf[MAX_LEN_LOG_LINE];
390
391 fc_snprintf(buf, sizeof(buf), "%s%s", where, message);
392 log_callback(level, buf, log_filename != nullptr);
393 } else {
395 }
396 }
397}
398
399/**********************************************************************/
403void vdo_log(const char *file, const char *function, int line,
405 char *buf, int buflen, const char *message, va_list args)
406{
408
409 /* There used to be check against recursive logging here, but
410 * the way it worked prevented any kind of simultaneous logging,
411 * not just recursive. Multiple threads should be able to log
412 * simultaneously. */
413
415 fc_snprintf(buf_where, sizeof(buf_where), "in %s() [%s::%d]: ",
416 function, file, line);
417
418 /* In the default configuration log_pre_callback is equal to log_real(). */
419 if (log_pre_callback) {
421 }
422}
423
424/**********************************************************************/
431 const char *where, const char *msg)
432{
433 static char last_msg[MAX_LEN_LOG_LINE] = "";
434 static unsigned int repeated = 0; /* Total times current message repeated */
435 static unsigned int next = 2; /* Next total to print update */
436 static unsigned int prev = 0; /* Total on last update */
437 /* Only count as repeat if same level */
438 static enum log_level prev_level = -1;
439 char buf[MAX_LEN_LOG_LINE];
440 FILE *fs;
441
442 if (log_filename) {
444 if (!(fs = fc_fopen(log_filename, "a"))) {
446 _("Couldn't open logfile: %s for appending \"%s\".\n"),
447 log_filename, msg);
449 }
450 } else {
451 fs = stderr;
452 }
453
454 if (level == prev_level && !fc_strncmp(msg, last_msg,
455 MAX_LEN_LOG_LINE - 1)) {
456 repeated++;
457 if (repeated == next) {
458 fc_snprintf(buf, sizeof(buf),
459 PL_("last message repeated %d time",
460 "last message repeated %d times",
461 repeated-prev), repeated-prev);
462 if (repeated > 2) {
463 cat_snprintf(buf, sizeof(buf),
464 /* TRANS: preserve leading space */
465 PL_(" (total %d repeat)",
466 " (total %d repeats)",
468 }
470 prev = repeated;
471 next *= 2;
472 }
473 } else {
474 if (repeated > 0 && repeated != prev) {
475 if (repeated == 1) {
476 /* just repeat the previous message: */
478 } else {
479 fc_snprintf(buf, sizeof(buf),
480 PL_("last message repeated %d time",
481 "last message repeated %d times",
482 repeated - prev), repeated - prev);
483 if (repeated > 2) {
484 cat_snprintf(buf, sizeof(buf),
485 PL_(" (total %d repeat)", " (total %d repeats)",
487 }
489 }
490 }
492 repeated = 0;
493 next = 2;
494 prev = 0;
496 }
497 /* Save last message. */
498 sz_strlcpy(last_msg, msg);
499
500 fflush(fs);
501 if (log_filename) {
502 fclose(fs);
504 }
505}
506
507/**********************************************************************/
514void do_log(const char *file, const char *function, int line,
516 const char *message, ...)
517{
518 char buf[MAX_LEN_LOG_LINE];
519 va_list args;
520
521 va_start(args, message);
524 va_end(args);
525}
526
527/**********************************************************************/
535
536#ifndef FREECIV_NDEBUG
537/**********************************************************************/
541void fc_assert_fail(const char *file, const char *function, int line,
542 const char *assertion, const char *message, ...)
543{
545
546 if (assertion != nullptr) {
547 do_log(file, function, line, TRUE, level,
548 "assertion '%s' failed.", assertion);
549 }
550
551 if (message != nullptr && NOLOGMSG != message) {
552 /* Additional message. */
553 char buf[MAX_LEN_LOG_LINE];
554 va_list args;
555
556 va_start(args, message);
558 message, args);
559 va_end(args);
560 }
561
562 do_log(file, function, line, FALSE, level,
563 /* TRANS: No full stop after the URL, could cause confusion. */
564 _("Please report this message at %s"), BUG_URL);
565
566 if (0 <= fc_fatal_assertions) {
567 /* Emit a signal. */
569 }
570}
571#endif /* FREECIV_NDEBUG */
#define n
Definition astring.c:77
char * incite_cost
Definition comments.c:76
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:541
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:430
void log_close(void)
Definition log.c:270
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:245
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:531
static log_callback_fn log_callback
Definition log.c:41
void log_set_level(enum log_level level)
Definition log.c:314
log_callback_fn log_set_callback(log_callback_fn callback)
Definition log.c:290
const char * log_level_name(enum log_level lvl)
Definition log.c:330
static void log_write(FILE *fs, enum log_level level, bool print_from_where, const char *where, const char *message)
Definition log.c:366
log_pre_callback_fn log_set_pre_callback(log_pre_callback_fn precallback)
Definition log.c:278
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:403
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:302
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:514
static log_prefix_fn log_prefix
Definition log.c:42
enum log_level log_get_level(void)
Definition log.c:322
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:59
void(* log_callback_fn)(enum log_level, const char *, bool file_too)
Definition log.h:55
#define log_verbose(message,...)
Definition log.h:110
void(* log_pre_callback_fn)(enum log_level, bool print_from_where, const char *where, const char *msg)
Definition log.h:48
#define MAX_LEN_LOG_LINE
Definition log.h:27
#define log_debug(message,...)
Definition log.h:116
#define NOLOGMSG
Definition log.h:45
log_level
Definition log.h:29
@ LOG_ERROR
Definition log.h:31
@ LOG_DEBUG
Definition log.h:35
@ LOG_NORMAL
Definition log.h:33
@ LOG_FATAL
Definition log.h:30
@ LOG_VERBOSE
Definition log.h:34
#define fc_strdup(str)
Definition mem.h:43
#define fc_realloc(ptr, sz)
Definition mem.h:36
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:547
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:986
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
FILE * fc_fopen(const char *filename, const char *opentype)
Definition support.c:505
int fc_strncasecmp(const char *str0, const char *str1, size_t n)
Definition support.c:235
#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