Переглянути джерело

Allow multiple logfiles at different severity ranges

svn:r1899
Nick Mathewson 20 роки тому
батько
коміт
9d2cd7fc6e
6 змінених файлів з 152 додано та 52 видалено
  1. 2 0
      .cvsignore
  2. 33 5
      src/common/log.c
  3. 4 2
      src/common/log.h
  4. 106 25
      src/or/config.c
  5. 3 18
      src/or/main.c
  6. 4 2
      src/or/or.h

+ 2 - 0
.cvsignore

@@ -8,6 +8,8 @@ orconfig.h.in
 config.cache
 config.log
 config.status
+config.guess
+config.sub
 conftest*
 stamp-h
 stamp-h1

+ 33 - 5
src/common/log.c

@@ -11,6 +11,7 @@
 #include <stdarg.h>
 #include <assert.h>
 #include <stdlib.h>
+#include <string.h>
 #include "orconfig.h"
 #include "./util.h"
 #include "./log.h"
@@ -162,14 +163,14 @@ void reset_logs()
 
 /** Add a log handler to send all messages of severity <b>loglevel</b>
  * or higher to <b>stream</b>. */
-void add_stream_log(int loglevel, const char *name, FILE *stream)
+void add_stream_log(int loglevelMin, int loglevelMax, const char *name, FILE *stream)
 {
   logfile_t *lf;
   lf = tor_malloc(sizeof(logfile_t));
   lf->filename = name;
   lf->needs_close = 0;
-  lf->loglevel = loglevel;
-  lf->max_loglevel = LOG_ERR;
+  lf->loglevel = loglevelMin;
+  lf->max_loglevel = loglevelMax;
   lf->file = stream;
   lf->next = logfiles;
   logfiles = lf;
@@ -180,16 +181,43 @@ void add_stream_log(int loglevel, const char *name, FILE *stream)
  * the logfile fails, -1 is returned and errno is set appropriately
  * (by fopen).
  */
-int add_file_log(int loglevel, const char *filename)
+int add_file_log(int loglevelMin, int loglevelMax, const char *filename)
 {
   FILE *f;
   f = fopen(filename, "a");
   if (!f) return -1;
-  add_stream_log(loglevel, filename, f);
+  add_stream_log(loglevelMin, loglevelMax, filename, f);
   logfiles->needs_close = 1;
   return 0;
 }
 
+/** If <b>level</b> is a valid log severity, return the corresponding
+ * numeric value.  Otherwise, return -1. */
+int parse_log_level(const char *level) {
+  if (!strcasecmp(level, "err"))
+    return LOG_ERR;
+  else if (!strcasecmp(level, "notice"))
+    return LOG_NOTICE;
+  else if (!strcasecmp(level, "info"))
+    return LOG_INFO;
+  else if (!strcasecmp(level, "debug"))
+    return LOG_DEBUG;
+  else
+    return -1;
+}
+
+int get_min_log_level(void)
+{
+  logfile_t *lf;
+  int min = LOG_ERR;
+  for (lf = logfiles; lf; lf = lf->next) {
+    if (lf->loglevel < min)
+      min = lf->loglevel;
+  }
+  return min;
+}
+
+
 /*
   Local Variables:
   mode:c

+ 4 - 2
src/common/log.h

@@ -46,8 +46,10 @@
 #define CHECK_PRINTF(formatIdx, firstArg)
 #endif
 
-void add_stream_log(int loglevel, const char *name, FILE *stream);
-int add_file_log(int severity, const char *filename);
+int parse_log_level(const char *level);
+void add_stream_log(int severityMin, int severityMax, const char *name, FILE *stream);
+int add_file_log(int severityMin, int severityMax, const char *filename);
+int get_min_log_level(void);
 void close_logs();
 void reset_logs();
 

+ 106 - 25
src/or/config.c

@@ -28,7 +28,7 @@ static int config_close(FILE *f);
 static struct config_line_t *config_get_commandlines(int argc, char **argv);
 static struct config_line_t *config_get_lines(FILE *f);
 static void config_free_lines(struct config_line_t *front);
-static int config_compare(struct config_line_t *c, char *key, config_type_t type, void *arg);
+static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg);
 static int config_assign(or_options_t *options, struct config_line_t *list);
 
 /** Open a configuration file for reading */
@@ -131,12 +131,17 @@ static void config_free_lines(struct config_line_t *front) {
  * the result in <b>arg</b>.  If the option is misformatted, log a warning and
  * skip it.
  */
-static int config_compare(struct config_line_t *c, char *key, config_type_t type, void *arg) {
+static int config_compare(struct config_line_t *c, const char *key, config_type_t type, void *arg) {
   int i;
 
   if(strncasecmp(c->key,key,strlen(c->key)))
     return 0;
 
+  if(strcasecmp(c->key,key)) {
+    tor_free(c->key);
+    c->key = tor_strdup(key);
+  }
+
   /* it's a match. cast and assign. */
   log_fn(LOG_DEBUG,"Recognized keyword '%s' as %s, using value '%s'.",c->key,key,c->value);
 
@@ -204,8 +209,8 @@ static int config_assign(or_options_t *options, struct config_line_t *list) {
 
     config_compare(list, "KeepalivePeriod",CONFIG_TYPE_INT, &options->KeepalivePeriod) ||
 
-    config_compare(list, "LogLevel",       CONFIG_TYPE_STRING, &options->LogLevel) ||
-    config_compare(list, "LogFile",        CONFIG_TYPE_STRING, &options->LogFile) ||
+    config_compare(list, "LogLevel",       CONFIG_TYPE_LINELIST, &options->LogOptions) ||
+    config_compare(list, "LogFile",        CONFIG_TYPE_LINELIST, &options->LogOptions) ||
     config_compare(list, "LinkPadding",    CONFIG_TYPE_BOOL, &options->LinkPadding) ||
 
     config_compare(list, "MaxConn",        CONFIG_TYPE_INT, &options->MaxConn) ||
@@ -460,8 +465,7 @@ static int resolve_my_address(or_options_t *options) {
 
 /** Release storage held by <b>options</b> */
 static void free_options(or_options_t *options) {
-  tor_free(options->LogLevel);
-  tor_free(options->LogFile);
+  config_free_lines(options->LogOptions);
   tor_free(options->DebugLogFile);
   tor_free(options->DataDirectory);
   tor_free(options->RouterFile);
@@ -487,7 +491,7 @@ static void free_options(or_options_t *options) {
 static void init_options(or_options_t *options) {
 /* give reasonable values for each option. Defaults to zero. */
   memset(options,0,sizeof(or_options_t));
-  options->LogLevel = tor_strdup("notice");
+  options->LogOptions = NULL;
   options->ExitNodes = tor_strdup("");
   options->EntryNodes = tor_strdup("");
   options->ExcludeNodes = tor_strdup("");
@@ -498,7 +502,6 @@ static void init_options(or_options_t *options) {
   options->ORBindAddress = tor_strdup("0.0.0.0");
   options->DirBindAddress = tor_strdup("0.0.0.0");
   options->RecommendedVersions = NULL;
-  options->loglevel = LOG_INFO;
   options->PidFile = NULL; // tor_strdup("tor.pid");
   options->DataDirectory = NULL;
   options->PathlenCoinWeight = 0.3;
@@ -616,23 +619,6 @@ int getconfig(int argc, char **argv, or_options_t *options) {
     return -1;
   }
 
-  if(options->LogLevel) {
-    if(!strcmp(options->LogLevel,"err"))
-      options->loglevel = LOG_ERR;
-    else if(!strcmp(options->LogLevel,"warn"))
-      options->loglevel = LOG_WARN;
-    else if(!strcmp(options->LogLevel,"notice"))
-      options->loglevel = LOG_NOTICE;
-    else if(!strcmp(options->LogLevel,"info"))
-      options->loglevel = LOG_INFO;
-    else if(!strcmp(options->LogLevel,"debug"))
-      options->loglevel = LOG_DEBUG;
-    else {
-      log(LOG_WARN,"LogLevel must be one of err|warn|notice|info|debug.");
-      result = -1;
-    }
-  }
-
   if(options->ORPort < 0) {
     log(LOG_WARN,"ORPort option can't be negative.");
     result = -1;
@@ -727,6 +713,101 @@ int getconfig(int argc, char **argv, or_options_t *options) {
   return result;
 }
 
+static void add_single_log(struct config_line_t *level_opt,
+                           struct config_line_t *file_opt,
+                           int isDaemon)
+{
+  int levelMin=-1, levelMax=-1;
+  char *cp, *tmp_sev;
+
+  if (level_opt) {
+    cp = strchr(level_opt->value, '-');
+    if (cp) {
+      tmp_sev = tor_strndup(level_opt->value, cp - level_opt->value);
+      levelMin = parse_log_level(tmp_sev);
+      if (levelMin<0) {
+        log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", tmp_sev);
+      }
+      tor_free(tmp_sev);
+      levelMax = parse_log_level(cp+1);
+      if (levelMax<0) {
+        log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", cp+1);
+      }
+    } else {
+      levelMin = parse_log_level(level_opt->value);
+      if (levelMin<0) {
+        log_fn(LOG_WARN, "Unrecognized log severity %s: must be one of err|warn|notice|info|debug", level_opt->value);
+      }
+    }
+  }
+  if (levelMin < 0 && levelMax < 0) {
+    levelMin = LOG_NOTICE;
+    levelMax = LOG_ERR;
+  } else if (levelMin < 0) {
+    levelMin = levelMax;
+  } else {
+    levelMax = LOG_ERR;
+  }
+  if (file_opt) {
+    if (add_file_log(levelMin, levelMax, file_opt->value) < 0) {
+      /* opening the log file failed!  Use stderr and log a warning */
+      add_stream_log(levelMin, levelMax, "<stderr>", stderr);
+      log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", file_opt->value,
+             strerror(errno));
+    }
+    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
+           file_opt->value);
+  } else if (!isDaemon) {
+    add_stream_log(levelMin, levelMax, "<stderr>", stderr);
+  }
+}
+
+/**
+ * Initialize the logs based on the configuration file.
+ */
+void config_init_logs(or_options_t *options)
+{
+  /* The order of options is:  Level? (File Level?)+
+   */
+  struct config_line_t *opt = options->LogOptions;
+
+  /* Special case for if first option is LogLevel. */
+  if (opt && !strcasecmp(opt->key, "LogLevel")) {
+    if (opt->next && !strcasecmp(opt->next->key, "LogFile")) {
+      add_single_log(opt, opt->next, options->RunAsDaemon);
+      opt = opt->next->next;
+    } else if (!opt->next) {
+      add_single_log(opt, NULL, options->RunAsDaemon);
+      return;
+    } else {
+      opt = opt->next;
+    }
+  }
+
+  while (opt) {
+    if (!strcasecmp(opt->key, "LogLevel")) {
+      log_fn(LOG_WARN, "Two LogLevel options in a row without intervening LogFile");
+      opt = opt->next;
+    } else {
+      assert(!strcasecmp(opt->key, "LogFile"));
+      if (opt->next && !strcasecmp(opt->next->key, "LogLevel")) {
+        /* LogFile followed by LogLevel */
+        add_single_log(opt->next, opt, options->RunAsDaemon);
+        opt = opt->next->next;
+      } else {
+        /* LogFile followed by LogFile or end of list. */
+        add_single_log(NULL, opt, options->RunAsDaemon);
+        opt = opt->next;
+      }
+    }
+  }
+
+  if (options->DebugLogFile) {
+    log_fn(LOG_WARN, "DebugLogFile is deprecated; use LogFile and LogLevel instead");
+    add_file_log(LOG_DEBUG, LOG_ERR, options->DebugLogFile);
+  }
+}
+
 /*
   Local Variables:
   mode:c

+ 3 - 18
src/or/main.c

@@ -566,22 +566,7 @@ static int init_from_config(int argc, char **argv) {
   }
 
   /* Configure the log(s) */
-  if(!options.LogFile && !options.RunAsDaemon)
-    add_stream_log(options.loglevel, "<stdout>", stdout);
-  if(options.LogFile) {
-    if (add_file_log(options.loglevel, options.LogFile) != 0) {
-      /* opening the log file failed!  Use stderr and log a warning */
-      add_stream_log(options.loglevel, "<stderr>", stderr);
-      log_fn(LOG_WARN, "Cannot write to LogFile '%s': %s.", options.LogFile, strerror(errno));
-    }
-    log_fn(LOG_NOTICE, "Successfully opened LogFile '%s', redirecting output.",
-           options.LogFile);
-  }
-  if(options.DebugLogFile) {
-    if (add_file_log(LOG_DEBUG, options.DebugLogFile) != 0)
-      log_fn(LOG_WARN, "Cannot write to DebugLogFile '%s': %s.", options.DebugLogFile, strerror(errno));
-    log_fn(LOG_DEBUG, "Successfully opened DebugLogFile '%s'.", options.DebugLogFile);
-  }
+  config_init_logs(&options);
 
   /* Set up our buckets */
   connection_bucket_init();
@@ -696,7 +681,7 @@ static int do_main_loop(void) {
 #ifndef MS_WINDOWS /* do signal stuff only on unix */
     if(please_dumpstats) {
       /* prefer to log it at INFO, but make sure we always see it */
-      dumpstats(options.loglevel>LOG_INFO ? options.loglevel : LOG_INFO);
+      dumpstats(get_min_log_level()>LOG_INFO ? get_min_log_level() : LOG_INFO);
       please_dumpstats = 0;
     }
     if(please_reset) {
@@ -867,7 +852,7 @@ void exit_function(void)
 int tor_main(int argc, char *argv[]) {
 
   /* give it somewhere to log to initially */
-  add_stream_log(LOG_INFO, "<stdout>", stdout);
+  add_stream_log(LOG_INFO, LOG_ERR, "<stdout>", stdout);
   log_fn(LOG_NOTICE,"Tor v%s. This is experimental software. Do not use it if you need anonymity.",VERSION);
 
   if (network_init()<0) {

+ 4 - 2
src/or/or.h

@@ -753,6 +753,9 @@ typedef struct circuit_t circuit_t;
 
 /** Configuration options for a Tor process */
 typedef struct {
+  struct config_line_t *LogOptions; /**< List of configuration lines
+                                     * for logfiles */
+
   char *LogLevel; /**< Verbosity of log: minimal level of messages to report. */
   char *LogFile; /**< Where to send normal log messages. */
   char *DebugLogFile; /**< Where to send verbose log messages. */
@@ -811,8 +814,6 @@ typedef struct {
   int BandwidthBurst; /**< How much bandwidth, at maximum, are we willing to
                        * use in a second? */
   int NumCpus; /**< How many CPUs should we try to use? */
-  int loglevel; /**< How verbose should we be?  Log messages less severe than
-                 * this will be ignored. */
   int RunTesting; /**< If true, create testing circuits to measure how well the
                    * other ORs are running. */
   struct config_line_t *RendConfigLines; /**< List of configuration lines
@@ -951,6 +952,7 @@ struct config_line_t {
 
 int config_assign_default_dirservers(void);
 int getconfig(int argc, char **argv, or_options_t *options);
+void config_init_logs(or_options_t *options);
 
 /********************************* connection.c ***************************/