curl: support repeated use of the verbose option; -vv etc

- 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
This commit is contained in:
Stefan Eissing 2024-08-06 11:44:23 +02:00 committed by Daniel Stenberg
parent 53146dd262
commit 06c5829dab
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
5 changed files with 175 additions and 56 deletions

View 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

View 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

View 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,
#ifndef CURL_DISABLE_FTP
&Curl_trc_feat_ftp,
#endif
#ifndef CURL_DISABLE_DOH
&Curl_doh_trc,
#endif
NULL,
#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 Curl_cftype *cf_types[] = {
&Curl_cft_tcp,
&Curl_cft_udp,
&Curl_cft_unix,
&Curl_cft_tcp_accept,
&Curl_cft_happy_eyeballs,
&Curl_cft_setup,
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, TRC_CT_PROTOCOL },
#endif
#ifndef CURL_DISABLE_DOH
{ &Curl_doh_trc, TRC_CT_NETWORK },
#endif
};
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);

View 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;

View 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: