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
#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)
}
#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);
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);
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 */
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)
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! */
break;
}
a = NULL;
-
+ ++nopts; /* processed one option from `flag` input, loop for more */
} while(!longopt && !singleopt && *++parse && !*usedarg && !err);
error: