]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
curl: support repeated use of the verbose option; -vv etc
authorStefan Eissing <stefan@eissing.org>
Tue, 6 Aug 2024 09:44:23 +0000 (11:44 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 7 Aug 2024 06:14:36 +0000 (08:14 +0200)
- make mentioning `-v` on the curl command line increase the
  verbosity of the trace output
- related discussion https://github.com/curl/curl/discussions/13810
- make a single -v revert all previous -v+ changes
- make --no-verbose also reset all trace configs

Closes #13977

docs/cmdline-opts/verbose.md
docs/libcurl/curl_global_trace.md
lib/curl_trc.c
src/tool_cfgable.h
src/tool_getparam.c

index 89226f18c45414175e501f7f2cfc63a58c72554a..7703bc34ad44980e818272e68b97133b2b668617 100644 (file)
@@ -28,8 +28,28 @@ and a line starting with * means additional info provided by curl.
 If you only want HTTP headers in the output, --include or --dump-header might
 be more suitable options.
 
-If you think this option still does not give you enough details, consider using
---trace or --trace-ascii instead.
+Since curl 8.10, mentioning this option several times in the same argument
+increases the level of the trace output. However, as before,
+a single `-v`, `--verbose` or `--no-verbose` reverts any additions by
+previous `-vv` again. This means that `-vv -v` is equivalent to `-v`. This
+avoids unwanted verbosity when the option is mentioned in the command line
+*and* curl config files.
+
+Using it twice, e.g. `-vv`, outputs time (`--trace-time`) and transfer
+ids (`--trace-ids`), as well as enable tracing for all protocols
+(`--trace-config protocol`).
+
+Adding a third verbose outputs transfer content (`--trace-ascii %`) and
+enable tracing of more components (`--trace-config read,write,ssl`).
+
+A forth time adds tracing of all network components.
+(`--trace-config network`).
+
+Any addition of the verbose option after that has no effect.
+
+If you think this option does not give you the right details, consider using
+--trace or --trace-ascii instead. Or use it only once and use `--trace-config`
+to trace the specific components you wish to see.
 
 Note that verbose output of curl activities and network traffic might contain
 sensitive data, including usernames, credentials or secret data content. Be
index a76462570ae573705641c89c165251e7b8dd2be3..0b0799db3964010313e8a8327981d5a7c6888820 100644 (file)
@@ -109,6 +109,27 @@ Traces reading of upload data from the application in order to send it to the se
 
 Traces writing of download data, received from the server, to the application.
 
+# TRACE GROUPS
+
+Besides the specific component names there are the following group names
+defined:
+
+## `all`
+
+## `network`
+
+All components involved in bare network I/O, including the SSL layer.
+
+All components that your libcurl is built with.
+
+## `protocol`
+
+All components involved in transfer protocols, such as 'ftp' and 'http/2'.
+
+## `proxy`
+
+All components involved in use of proxies.
+
 # %PROTOCOLS%
 
 # EXAMPLE
index 3ae9517155f4ab757d07f48a8f1f6ac1adc934b2..a45e07509fce71f6cf781ed815a883053f5b35e9 100644 (file)
@@ -53,6 +53,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
 
 void Curl_debug(struct Curl_easy *data, curl_infotype type,
                 char *ptr, size_t size)
@@ -218,58 +221,102 @@ void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
 }
 #endif /* !CURL_DISABLE_FTP */
 
-static struct curl_trc_feat *trc_feats[] = {
-  &Curl_trc_feat_read,
-  &Curl_trc_feat_write,
+#define TRC_CT_NONE        (0)
+#define TRC_CT_PROTOCOL    (1<<(0))
+#define TRC_CT_NETWORK     (1<<(1))
+#define TRC_CT_PROXY       (1<<(2))
+
+struct trc_feat_def {
+  struct curl_trc_feat *feat;
+  unsigned int category;
+};
+
+static struct trc_feat_def trc_feats[] = {
+  { &Curl_trc_feat_read,      TRC_CT_NONE },
+  { &Curl_trc_feat_write,     TRC_CT_NONE },
 #ifndef CURL_DISABLE_FTP
-  &Curl_trc_feat_ftp,
+  { &Curl_trc_feat_ftp,       TRC_CT_PROTOCOL },
 #endif
 #ifndef CURL_DISABLE_DOH
-  &Curl_doh_trc,
+  { &Curl_doh_trc,            TRC_CT_NETWORK },
 #endif
-  NULL,
 };
 
-static struct Curl_cftype *cf_types[] = {
-  &Curl_cft_tcp,
-  &Curl_cft_udp,
-  &Curl_cft_unix,
-  &Curl_cft_tcp_accept,
-  &Curl_cft_happy_eyeballs,
-  &Curl_cft_setup,
+struct trc_cft_def {
+  struct Curl_cftype *cft;
+  unsigned int category;
+};
+
+static struct trc_cft_def trc_cfts[] = {
+  { &Curl_cft_tcp,            TRC_CT_NETWORK },
+  { &Curl_cft_udp,            TRC_CT_NETWORK },
+  { &Curl_cft_unix,           TRC_CT_NETWORK },
+  { &Curl_cft_tcp_accept,     TRC_CT_NETWORK },
+  { &Curl_cft_happy_eyeballs, TRC_CT_NETWORK },
+  { &Curl_cft_setup,          TRC_CT_PROTOCOL },
 #ifdef USE_NGHTTP2
-  &Curl_cft_nghttp2,
+  { &Curl_cft_nghttp2,        TRC_CT_PROTOCOL },
 #endif
 #ifdef USE_SSL
-  &Curl_cft_ssl,
+  { &Curl_cft_ssl,            TRC_CT_NETWORK },
 #ifndef CURL_DISABLE_PROXY
-  &Curl_cft_ssl_proxy,
+  { &Curl_cft_ssl_proxy,      TRC_CT_PROXY },
 #endif
 #endif
 #if !defined(CURL_DISABLE_PROXY)
 #if !defined(CURL_DISABLE_HTTP)
-  &Curl_cft_h1_proxy,
+  { &Curl_cft_h1_proxy,       TRC_CT_PROXY },
 #ifdef USE_NGHTTP2
-  &Curl_cft_h2_proxy,
+  { &Curl_cft_h2_proxy,       TRC_CT_PROXY },
 #endif
-  &Curl_cft_http_proxy,
+  { &Curl_cft_http_proxy,     TRC_CT_PROXY },
 #endif /* !CURL_DISABLE_HTTP */
-  &Curl_cft_haproxy,
-  &Curl_cft_socks_proxy,
+  { &Curl_cft_haproxy,        TRC_CT_PROXY },
+  { &Curl_cft_socks_proxy,    TRC_CT_PROXY },
 #endif /* !CURL_DISABLE_PROXY */
 #ifdef USE_HTTP3
-  &Curl_cft_http3,
+  { &Curl_cft_http3,          TRC_CT_PROTOCOL },
 #endif
 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
-  &Curl_cft_http_connect,
+  { &Curl_cft_http_connect,   TRC_CT_PROTOCOL },
 #endif
-  NULL,
 };
 
+static void trc_apply_level_by_name(const char * const token, int lvl)
+{
+  size_t i;
+
+  for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+    if(strcasecompare(token, trc_cfts[i].cft->name)) {
+      trc_cfts[i].cft->log_level = lvl;
+      break;
+    }
+  }
+  for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+    if(strcasecompare(token, trc_feats[i].feat->name)) {
+      trc_feats[i].feat->log_level = lvl;
+      break;
+    }
+  }
+}
+
+static void trc_apply_level_by_category(int category, int lvl)
+{
+  size_t i;
+
+  for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+    if(!category || (trc_cfts[i].category & category))
+      trc_cfts[i].cft->log_level = lvl;
+  }
+  for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+    if(!category || (trc_feats[i].category & category))
+      trc_feats[i].feat->log_level = lvl;
+  }
+}
+
 CURLcode Curl_trc_opt(const char *config)
 {
   char *token, *tok_buf, *tmp;
-  size_t i;
   int lvl;
 
   tmp = strdup(config);
@@ -291,24 +338,17 @@ CURLcode Curl_trc_opt(const char *config)
         lvl = CURL_LOG_LVL_INFO;
         break;
     }
-    for(i = 0; cf_types[i]; ++i) {
-      if(strcasecompare(token, "all")) {
-        cf_types[i]->log_level = lvl;
-      }
-      else if(strcasecompare(token, cf_types[i]->name)) {
-        cf_types[i]->log_level = lvl;
-        break;
-      }
-    }
-    for(i = 0; trc_feats[i]; ++i) {
-      if(strcasecompare(token, "all")) {
-        trc_feats[i]->log_level = lvl;
-      }
-      else if(strcasecompare(token, trc_feats[i]->name)) {
-        trc_feats[i]->log_level = lvl;
-        break;
-      }
-    }
+    if(strcasecompare(token, "all"))
+      trc_apply_level_by_category(TRC_CT_NONE, lvl);
+    else if(strcasecompare(token, "protocol"))
+      trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
+    else if(strcasecompare(token, "network"))
+      trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
+    else if(strcasecompare(token, "proxy"))
+      trc_apply_level_by_category(TRC_CT_PROXY, lvl);
+    else
+      trc_apply_level_by_name(token, lvl);
+
     token = strtok_r(NULL, ", ", &tok_buf);
   }
   free(tmp);
index e76ab46b0e42e9bdc0abeeb0631d8a9b120aa3b8..1a2e5374e3edbf2fc43c5ec377554bc80bf1fd5c 100644 (file)
@@ -316,6 +316,7 @@ struct GlobalConfig {
   bool silent;                    /* do not show messages, --silent given */
   bool noprogress;                /* do not show progress bar */
   bool isatty;                    /* Updated internally if output is a tty */
+  unsigned char verbosity;        /* How verbose we should be */
   char *trace_dump;               /* file to dump the network trace to */
   FILE *trace_stream;
   bool trace_fopened;
index a55cac44ca20fb0cb413aefade51ec2ed65123b8..411a349304a3b0cb9beabaae8896c3f268a9f40d 100644 (file)
@@ -1029,6 +1029,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
   time_t now;
   bool longopt = FALSE;
   bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */
+  size_t nopts = 0; /* options processed in `flag`*/
   ParameterError err = PARAM_OK;
   bool toggle = TRUE; /* how to switch boolean options, on or off. Controlled
                          by using --OPTION or --no-OPTION */
@@ -2490,8 +2491,27 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
       cleanarg(clearthis);
       break;
     case C_VERBOSE: /* --verbose */
-      if(toggle) {
-        /* the '%' thing here will cause the trace get sent to stderr */
+      /* This option is a super-boolean with side effect when applied
+       * more than once in the same argument flag, like `-vvv`. */
+      if(!toggle) {
+        global->verbosity = 0;
+        if(set_trace_config(global, "-all"))
+          err = PARAM_NO_MEM;
+        global->tracetype = TRACE_NONE;
+        break;
+      }
+      else if(!nopts) {
+        /* fist `-v` in an argument resets to base verbosity */
+        global->verbosity = 0;
+        if(set_trace_config(global, "-all")) {
+          err = PARAM_NO_MEM;
+          break;
+        }
+      }
+      /* the '%' thing here will cause the trace get sent to stderr */
+      switch(global->verbosity) {
+      case 0:
+        global->verbosity = 1;
         Curl_safefree(global->trace_dump);
         global->trace_dump = strdup("%");
         if(!global->trace_dump)
@@ -2499,13 +2519,30 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
         else {
           if(global->tracetype && (global->tracetype != TRACE_PLAIN))
             warnf(global,
-                  "-v, --verbose overrides an earlier trace/verbose option");
+                  "-v, --verbose overrides an earlier trace option");
           global->tracetype = TRACE_PLAIN;
         }
+        break;
+      case 1:
+        global->verbosity = 2;
+        if(set_trace_config(global, "ids,time,protocol"))
+          err = PARAM_NO_MEM;
+        break;
+      case 2:
+        global->verbosity = 3;
+        global->tracetype = TRACE_ASCII;
+        if(set_trace_config(global, "ssl,read,write"))
+          err = PARAM_NO_MEM;
+        break;
+      case 3:
+        global->verbosity = 4;
+        if(set_trace_config(global, "network"))
+          err = PARAM_NO_MEM;
+        break;
+      default:
+        /* no effect for now */
+        break;
       }
-      else
-        /* verbose is disabled here */
-        global->tracetype = TRACE_NONE;
       break;
     case C_VERSION: /* --version */
       if(toggle)    /* --no-version yields no output! */
@@ -2634,7 +2671,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
       break;
     }
     a = NULL;
-
+    ++nopts; /* processed one option from `flag` input, loop for more */
   } while(!longopt && !singleopt && *++parse && !*usedarg && !err);
 
 error: