Index | Thread | Search

From:
Rafael Sadowski <rafael@sizeofvoid.org>
Subject:
relayd: allow setting log level in relayd.conf (fix relayctl log command)
To:
tech@openbsd.org
Date:
Sat, 1 Aug 2026 14:49:37 +0200

Download raw body.

Thread
  • Rafael Sadowski:

    relayd: allow setting log level in relayd.conf (fix relayctl log command)

"relayctl log verbose|brief" is simply useless and doesn't change a thing.
We've hidden everything useful for debugging behind DPRINF. You can use
the diffs to debug relayd again or to get useful information if something
isn't working in your setup.

I don't know the history here. Performance? But performance doesn't help me
when I need to debug my config ;).

With these diffs, we can also configure it with "log level
brief|verbose". I'd prefer have info|warn|fatal|debug (like the log
functions we have). But log.c doesn't support that, and I don't want to
modify log.c

Details, see commit msg.

OK?

commit 2f79b1b972e5c80d55562a0381120774463bd669
Author: Rafael Sadowski <rafael@sizeofvoid.org>
Date:   Sat Aug 1 14:06:54 2026 +0200

    relayd: allow setting log level from relayd.conf
    
    Add "log level (brief|verbose)" which sets or clears RELAYD_OPT_VERBOSE
    just like relayctl log verbose|brief does at runtime.
    
    This makes sense now because the recent DPRINTF removal turned all
    DPRINTF calls into real log_debug calls. Some debug logs moved to
    warning. Some remained DPRINF().
    
    Before that change the toggle had almost nothing to gate. All debug
    output was compiled out unless relayd was built with DEBUG > 1. So
    "log verbose" in relayctl was practically a no-op for users.
    It was impossible for the user to work out why the setup/config wasn't
    working.
    
    Now the toggle actually does what its name suggests. Pinning the
    level in the config file lets it survive restarts and SIGHUP reloads.

diff --git a/agentx_control.c b/agentx_control.c
index 7af1c0e..41bb2d7 100644
--- a/agentx_control.c
+++ b/agentx_control.c
@@ -1003,7 +1003,7 @@ snmp_element(const char *oidstr, enum snmp_type type, void *buf, int64_t val,
 	u_int64_t		 l;
 	struct snmp_oid		 oid;
 
-	DPRINTF("%s: oid %s type %d buf %p val %lld", __func__,
+	log_debug("%s: oid %s type %d buf %p val %lld", __func__,
 	    oidstr, type, buf, val);
 
 	if (snmp_string2oid(oidstr, &oid) == -1)
diff --git a/ca.c b/ca.c
index f3d3dc0..93dfab6 100644
--- a/ca.c
+++ b/ca.c
@@ -268,7 +268,7 @@ ca_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
 		if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
 			fatalx("%s: invalid relay key", __func__);
 
-		DPRINTF("%s:%d: key hash %s proc %d",
+		log_debug("%s:%d: key hash %s proc %d",
 		    __func__, __LINE__, cko.cko_hash, cko.cko_proc);
 
 		if ((to = calloc(1, cko.cko_tlen)) == NULL)
@@ -508,14 +508,18 @@ rsae_send_imsg(int flen, const u_char *from, u_char *to, RSA *rsa,
 int
 rsae_priv_enc(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
 {
-	DPRINTF("%s:%d", __func__, __LINE__);
+#if DEBUG_CERT
+	log_debug("%s:%d", __func__, __LINE__);
+#endif
 	return rsae_send_imsg(flen, from, to, rsa, padding, IMSG_CA_PRIVENC);
 }
 
 int
 rsae_priv_dec(int flen, const u_char *from, u_char *to, RSA *rsa, int padding)
 {
-	DPRINTF("%s:%d", __func__, __LINE__);
+#if DEBUG_CERT
+	log_debug("%s:%d", __func__, __LINE__);
+#endif
 	return rsae_send_imsg(flen, from, to, rsa, padding, IMSG_CA_PRIVDEC);
 }
 
@@ -701,7 +705,9 @@ ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
 	ECDSA_SIG *(*psign_sig)(const unsigned char *, int, const BIGNUM *,
 	    const BIGNUM *, EC_KEY *);
 
-	DPRINTF("%s:%d", __func__, __LINE__);
+#if DEBUG_CERT
+	log_debug("%s:%d", __func__, __LINE__);
+#endif
 	if (EC_KEY_get_ex_data(eckey, 0) != NULL)
 		return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
 	EC_KEY_METHOD_get_sign(ecdsa_default, NULL, NULL, &psign_sig);
diff --git a/check_script.c b/check_script.c
index 07b02b8..4caca71 100644
--- a/check_script.c
+++ b/check_script.c
@@ -106,7 +106,7 @@ script_exec(struct relayd *env, struct ctl_script *scr)
 		return (-1);
 	}
 
-	DPRINTF("%s: running script %s, host %s",
+	log_debug("%s: running script %s, host %s",
 	    __func__, scr->path, scr->name);
 
 	arg = scr->name;
diff --git a/check_tls.c b/check_tls.c
index f659ba1..3ff55be 100644
--- a/check_tls.c
+++ b/check_tls.c
@@ -206,8 +206,6 @@ check_tls_cleanup(struct ctl_tcp_event *cte)
 void
 check_tls_error(struct ctl_tcp_event *cte, const char *where, const char *what)
 {
-	if (log_getverbose() < 2)
-		return;
 	log_debug("TLS error: %s: %s: %s", where, what, tls_error(cte->tls));
 }
 
diff --git a/config.c b/config.c
index b273fd7..a6fe546 100644
--- a/config.c
+++ b/config.c
@@ -273,6 +273,8 @@ config_getcfg(struct relayd *env, struct imsg *imsg)
 		return (-1);
 	}
 
+	log_setverbose((env->sc_conf.opts & RELAYD_OPT_VERBOSE) ? 2 : 0);
+
 	what = ps->ps_what[privsep_process];
 
 	if (what & CONFIG_TABLES) {
@@ -316,7 +318,7 @@ config_settable(struct relayd *env, struct table *tb)
 		if (id == PROC_HCE && tb->conf.check == CHECK_NOCHECK)
 			continue;
 
-		DPRINTF("%s: sending table %s %d to %s", __func__,
+		log_debug("%s: sending table %s %d to %s", __func__,
 		    tb->conf.name, tb->conf.id, env->sc_ps->ps_title[id]);
 
 		c = 0;
@@ -379,7 +381,7 @@ config_gettable(struct relayd *env, struct imsg *imsg)
 
 	env->sc_tablecount++;
 
-	DPRINTF("%s: %s %d received table %d (%s)", __func__,
+	log_debug("%s: %s %d received table %d (%s)", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    tb->conf.id, tb->conf.name);
 
@@ -423,7 +425,7 @@ config_gethost(struct relayd *env, struct imsg *imsg)
 	TAILQ_INSERT_TAIL(&tb->hosts, host, entry);
 	TAILQ_INSERT_TAIL(&env->sc_hosts, host, globalentry);
 
-	DPRINTF("%s: %s %d received host %s for table %s", __func__,
+	log_debug("%s: %s %d received host %s for table %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    host->conf.name, tb->conf.name);
 
@@ -442,7 +444,7 @@ config_setrdr(struct relayd *env, struct rdr *rdr)
 		    id == privsep_process)
 			continue;
 
-		DPRINTF("%s: sending rdr %s to %s", __func__,
+		log_debug("%s: sending rdr %s to %s", __func__,
 		    rdr->conf.name, ps->ps_title[id]);
 
 		proc_compose(ps, id, IMSG_CFG_RDR,
@@ -487,7 +489,7 @@ config_getrdr(struct relayd *env, struct imsg *imsg)
 
 	env->sc_rdrcount++;
 
-	DPRINTF("%s: %s %d received rdr %s", __func__,
+	log_debug("%s: %s %d received rdr %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    rdr->conf.name);
 
@@ -517,7 +519,7 @@ config_getvirt(struct relayd *env, struct imsg *imsg)
 
 	TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
 
-	DPRINTF("%s: %s %d received address for rdr %s", __func__,
+	log_debug("%s: %s %d received address for rdr %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    rdr->conf.name);
 
@@ -536,7 +538,7 @@ config_setrt(struct relayd *env, struct router *rt)
 		    id == privsep_process)
 			continue;
 
-		DPRINTF("%s: sending router %s to %s tbl %d", __func__,
+		log_debug("%s: sending router %s to %s tbl %d", __func__,
 		    rt->rt_conf.name, ps->ps_title[id], rt->rt_conf.gwtable);
 
 		proc_compose(ps, id, IMSG_CFG_ROUTER,
@@ -576,7 +578,7 @@ config_getrt(struct relayd *env, struct imsg *imsg)
 
 	env->sc_routercount++;
 
-	DPRINTF("%s: %s %d received router %s", __func__,
+	log_debug("%s: %s %d received router %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    rt->rt_conf.name);
 
@@ -618,7 +620,7 @@ config_getroute(struct relayd *env, struct imsg *imsg)
 
 	env->sc_routecount++;
 
-	DPRINTF("%s: %s %d received route %d for router %s", __func__,
+	log_debug("%s: %s %d received route %d for router %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    nr->nr_conf.id, rt->rt_conf.name);
 
@@ -638,7 +640,7 @@ config_setproto(struct relayd *env, struct protocol *proto)
 		    id == privsep_process)
 			continue;
 
-		DPRINTF("%s: sending protocol %s to %s", __func__,
+		log_debug("%s: sending protocol %s to %s", __func__,
 		    proto->name, ps->ps_title[id]);
 
 		c = 0;
@@ -670,7 +672,7 @@ config_setrule(struct relayd *env, struct protocol *proto)
 		    id == privsep_process)
 			continue;
 
-		DPRINTF("%s: sending rules %s to %s", __func__,
+		log_debug("%s: sending rules %s to %s", __func__,
 		    proto->name, ps->ps_title[id]);
 
 		/* Now send all the rules */
@@ -746,7 +748,7 @@ config_getproto(struct relayd *env, struct imsg *imsg)
 
 	env->sc_protocount++;
 
-	DPRINTF("%s: %s %d received protocol %s", __func__,
+	log_debug("%s: %s %d received protocol %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    proto->name);
 
@@ -791,7 +793,7 @@ config_getrule(struct relayd *env, struct imsg *imsg)
 			free(rule);					\
 			return (-1);					\
 		}							\
-		DPRINTF("%s: %s %s (len %ld, option %d): %s", __func__,	\
+		log_debug("%s: %s %s (len %ld, option %d): %s", __func__,\
 		    #_n, #_f, len,					\
 		    rule->rule_kv[_n].kv_option,			\
 		    rule->rule_kv[_n].kv_##_f);				\
@@ -818,7 +820,7 @@ config_getrule(struct relayd *env, struct imsg *imsg)
 
 	TAILQ_INSERT_TAIL(&proto->rules, rule, rule_entry);
 
-	DPRINTF("%s: %s %d received rule %u for protocol %s", __func__,
+	log_debug("%s: %s %d received rule %u for protocol %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    rule->rule_id, proto->name);
 
@@ -869,7 +871,7 @@ config_setrelay(struct relayd *env, struct relay *rlay)
 		if ((what & CONFIG_RELAYS) == 0 || id == privsep_process)
 			continue;
 
-		DPRINTF("%s: sending relay %s to %s fd %d", __func__,
+		log_debug("%s: sending relay %s to %s fd %d", __func__,
 		    rlay->rl_conf.name, ps->ps_title[id], rlay->rl_s);
 
 		memcpy(&rl, &rlay->rl_conf, sizeof(rl));
@@ -1106,7 +1108,7 @@ config_getrelay(struct relayd *env, struct imsg *imsg)
 
 	env->sc_relaycount++;
 
-	DPRINTF("%s: %s %d received relay %s", __func__,
+	log_debug("%s: %s %d received relay %s", __func__,
 	    ps->ps_title[privsep_process], ps->ps_instance,
 	    rlay->rl_conf.name);
 
@@ -1151,7 +1153,7 @@ config_getrelaytable(struct relayd *env, struct imsg *imsg)
 
 	TAILQ_INSERT_TAIL(&rlay->rl_tables, rlt, rlt_entry);
 
-	DPRINTF("%s: %s %d received relay table %s for relay %s", __func__,
+	log_debug("%s: %s %d received relay table %s for relay %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    table->conf.name, rlay->rl_conf.name);
 
@@ -1213,7 +1215,7 @@ config_getrelayfd(struct relayd *env, struct imsg *imsg)
 		break;
 	}
 
-	DPRINTF("%s: %s %d received relay type %d for relay %s", __func__,
+	log_debug("%s: %s %d received relay type %d for relay %s", __func__,
 	    env->sc_ps->ps_title[privsep_process], env->sc_ps->ps_instance,
 	    crfd.type, rlay->rl_conf.name);
 
diff --git a/control.c b/control.c
index fb64608..3ed43bf 100644
--- a/control.c
+++ b/control.c
@@ -265,7 +265,7 @@ control_dispatch_imsg(int fd, short event, void *arg)
 			break;
 
 		if (c->waiting) {
-			log_debug("%s: unexpected imsg %d",
+			log_warn("%s: unexpected imsg %d",
 			    __func__, imsg_get_type(&imsg));
 			imsg_free(&imsg);
 			control_close(fd, cs);
diff --git a/parse.y b/parse.y
index e89d40e..0c6839d 100644
--- a/parse.y
+++ b/parse.y
@@ -183,7 +183,7 @@ typedef struct {
 %token	TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT URL WITH TTL RTABLE
 %token	MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE
 %token	EDH TICKETS CONNECTION CONNECTIONS CONTEXT ERRORS STATE CHANGES CHECKS
-%token	WEBSOCKETS PFLOG CLIENT PROXYPROTO V1 V2
+%token	WEBSOCKETS PFLOG CLIENT PROXYPROTO V1 V2 VERBOSE BRIEF LEVEL
 %token	<v.string>	STRING
 %token  <v.number>	NUMBER
 %type	<v.string>	context hostname interface table value path
@@ -412,6 +412,12 @@ main		: INTERVAL NUMBER	{
 		| LOG loglevel		{
 			conf->sc_conf.opts |= $2;
 		}
+		| LOG LEVEL VERBOSE	{
+			conf->sc_conf.opts |= RELAYD_OPT_VERBOSE;
+		}
+		| LOG LEVEL BRIEF	{
+			conf->sc_conf.opts &= ~RELAYD_OPT_VERBOSE;
+		}
 		| TIMEOUT timeout	{
 			bcopy(&$2, &conf->sc_conf.timeout,
 			    sizeof(struct timeval));
@@ -2524,6 +2530,7 @@ lookup(char *s)
 		{ "backup",		BACKUP },
 		{ "binary",		BINARY },
 		{ "block",		BLOCK },
+		{ "brief",		BRIEF },
 		{ "buffer",		BUFFER },
 		{ "ca",			CA },
 		{ "cache",		CACHE },
@@ -2566,6 +2573,7 @@ lookup(char *s)
 		{ "keypair",		KEYPAIR },
 		{ "label",		LABEL },
 		{ "least-states",	LEASTSTATES },
+		{ "level",		LEVEL },
 		{ "listen",		LISTEN },
 		{ "loadbalance",	LOADBALANCE },
 		{ "log",		LOG },
@@ -2632,6 +2640,7 @@ lookup(char *s)
 		{ "v1",			V1 },
 		{ "v2",			V2 },
 		{ "value",		VALUE },
+		{ "verbose",		VERBOSE },
 		{ "websockets",		WEBSOCKETS },
 		{ "with",		WITH }
 	};
diff --git a/pfe.c b/pfe.c
index c66ad06..1438171 100644
--- a/pfe.c
+++ b/pfe.c
@@ -351,7 +351,7 @@ pfe_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
 			TAILQ_REMOVE(&env->sc_sessions, s, se_entry);
 			free(s);
 		} else {
-			DPRINTF("removal of unpublished session %i", sid);
+			log_debug("removal of unpublished session %i", sid);
 		}
 		break;
 	default:
diff --git a/pfe_filter.c b/pfe_filter.c
index a3587dd..f3dbdae 100644
--- a/pfe_filter.c
+++ b/pfe_filter.c
@@ -414,7 +414,7 @@ sync_ruleset(struct relayd *env, struct rdr *rdr, int enable)
 		rio.rule.dst.port[1] = address->port.val[1];
 		rio.rule.rtableid = -1; /* stay in the main routing table */
 		rio.rule.onrdomain = env->sc_rtable;
-		DPRINTF("%s rtable %d", __func__, env->sc_rtable);
+		log_debug("%s rtable %d", __func__, env->sc_rtable);
 
 		if (rio.rule.proto == IPPROTO_TCP)
 			rio.rule.timeout[PFTM_TCP_ESTABLISHED] =
diff --git a/relay.c b/relay.c
index 6992900..efc2045 100644
--- a/relay.c
+++ b/relay.c
@@ -724,7 +724,7 @@ relay_connected(int fd, short sig, void *arg)
 		return;
 	}
 
-	DPRINTF("%s: session %d: successful", __func__, con->se_id);
+	log_debug("%s: session %d: successful", __func__, con->se_id);
 
 	/* Log destination if it was changed in a keep-alive connection */
 	if ((con->se_table != con->se_table0) &&
@@ -951,7 +951,7 @@ relay_splice(struct ctl_relay_event *cre)
 		return (0);
 
 	if (!(cre->toread == TOREAD_UNLIMITED || cre->toread > 0)) {
-		DPRINTF("%s: session %d: splice dir %d, nothing to read %lld",
+		log_debug("%s: session %d: splice dir %d, nothing to read %lld",
 		    __func__, con->se_id, cre->dir, cre->toread);
 		return (0);
 	}
@@ -959,7 +959,7 @@ relay_splice(struct ctl_relay_event *cre)
 	/* do not splice before buffers have not been completely flushed */
 	if (EVBUFFER_LENGTH(cre->bev->input) ||
 	    EVBUFFER_LENGTH(cre->dst->bev->output)) {
-		DPRINTF("%s: session %d: splice dir %d, dirty buffer",
+		log_debug("%s: session %d: splice dir %d, dirty buffer",
 		    __func__, con->se_id, cre->dir);
 		bufferevent_disable(cre->bev, EV_READ);
 		return (0);
@@ -977,7 +977,7 @@ relay_splice(struct ctl_relay_event *cre)
 	cre->splicelen = 0;
 	bufferevent_enable(cre->bev, EV_READ);
 
-	DPRINTF("%s: session %d: splice dir %d, maximum %lld, successful",
+	log_debug("%s: session %d: splice dir %d, maximum %lld, successful",
 	    __func__, con->se_id, cre->dir, cre->toread);
 
 	return (1);
@@ -1000,7 +1000,7 @@ relay_splicelen(struct ctl_relay_event *cre)
 		return (-1);
 	}
 
-	DPRINTF("%s: session %d: splice dir %d, length %lld",
+	log_debug("%s: session %d: splice dir %d, length %lld",
 	    __func__, con->se_id, cre->dir, len);
 
 	if (len > cre->splicelen) {
@@ -1034,7 +1034,7 @@ relay_error(struct bufferevent *bev, short error, void *arg)
 	struct rsession		*con = cre->con;
 	struct evbuffer		*dst;
 
-	DPRINTF("%s: session %d: dir %d state %d to read %lld event error %x",
+	log_debug("%s: session %d: dir %d state %d to read %lld event error %x",
 	    __func__, con->se_id, cre->dir, cre->state, cre->toread, error);
 	if (error & EVBUFFER_TIMEOUT) {
 		if (cre->splicelen >= 0) {
@@ -1132,7 +1132,7 @@ relay_accept(int fd, short event, void *arg)
 
 			event_del(&rlay->rl_ev);
 			evtimer_add(&rlay->rl_evt, &evtpause);
-			log_debug("%s: deferring connections", __func__);
+			DPRINTF("%s: deferring connections", __func__);
 		}
 		return;
 	}
@@ -1367,7 +1367,7 @@ relay_from_table(struct rsession *con)
 
 			host = rlt->rlt_host[idx];
 
-			DPRINTF("%s: session %d: table %s host %s, "
+			log_debug("%s: session %d: table %s host %s, "
 			    "p 0x%016llx, idx %d, cnt %d, max %d",
 			    __func__, con->se_id, table->conf.name,
 			    host->conf.name, p, idx, cnt, maxtries);
@@ -1379,20 +1379,20 @@ relay_from_table(struct rsession *con)
 	} else {
 		/* handle all non-hashing algorithms */
 		host = rlt->rlt_host[idx];
-		DPRINTF("%s: session %d: table %s host %s, p 0x%016llx, idx %d",
+		log_debug("%s: session %d: table %s host %s, p 0x%016llx, idx %d",
 		    __func__, con->se_id, table->conf.name, host->conf.name,
 		    p, idx);
 	}
 
 	while (host != NULL) {
-		DPRINTF("%s: session %d: host %s", __func__,
+		log_debug("%s: session %d: host %s", __func__,
 		    con->se_id, host->conf.name);
 		if (!table->conf.check || host->up == HOST_UP)
 			goto found;
 		host = TAILQ_NEXT(host, entry);
 	}
 	TAILQ_FOREACH(host, &table->hosts, entry) {
-		DPRINTF("%s: session %d: next host %s",
+		log_debug("%s: session %d: next host %s",
 		    __func__, con->se_id, host->conf.name);
 		if (!table->conf.check || host->up == HOST_UP)
 			goto found;
@@ -1518,7 +1518,7 @@ void
 relay_connect_state(struct rsession *con, struct ctl_relay_event *cre,
     enum relay_state new)
 {
-	DPRINTF("%s: session %d: %s state %s -> %s",
+	log_debug("%s: session %d: %s state %s -> %s",
 	    __func__, con->se_id,
 	    cre->dir == RELAY_DIR_REQUEST ? "accept" : "connect",
 	    relay_state(cre->state), relay_state(new));
@@ -1538,7 +1538,7 @@ relay_connect_retry(int fd, short sig, void *arg)
 		relay_inflight = 1;
 	}
 
-	DPRINTF("%s: retry %d of %d, inflight: %d", __func__,
+	log_debug("%s: retry %d of %d, inflight: %d", __func__,
 	    con->se_retrycount, con->se_retry, relay_inflight);
 
 	if (sig != EV_TIMEOUT)
@@ -1551,7 +1551,7 @@ relay_connect_retry(int fd, short sig, void *arg)
 	 * available: client could have closed it while we were waiting?
 	 */
 
-	DPRINTF("%s: got EV_TIMEOUT", __func__);
+	log_debug("%s: got EV_TIMEOUT", __func__);
 
 	if (getdtablecount() + FD_RESERVE +
 	    relay_inflight > getdtablesize()) {
@@ -1598,7 +1598,7 @@ relay_connect_retry(int fd, short sig, void *arg)
 	else
 		relay_connect_state(con, &con->se_out, STATE_CONNECTED);
 	relay_inflight--;
-	DPRINTF("%s: inflight decremented, now %d", __func__, relay_inflight);
+	log_debug("%s: inflight decremented, now %d", __func__, relay_inflight);
 
 	event_add(&rlay->rl_ev, NULL);
 
@@ -1724,7 +1724,7 @@ relay_connect(struct rsession *con)
 
 	relay_connect_state(con, &con->se_out, STATE_CONNECTED);
 	relay_inflight--;
-	DPRINTF("%s: inflight decremented, now %d", __func__,
+	log_debug("%s: inflight decremented, now %d", __func__,
 	    relay_inflight);
 
 	if (errno == EINPROGRESS)
@@ -1927,7 +1927,7 @@ relay_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
 		    NULL)
 			fatalx("%s: invalid table id", __func__);
 
-		DPRINTF("%s: [%d] state %d for "
+		log_debug("%s: [%d] state %d for "
 		    "host %u %s", __func__, p->p_ps->ps_instance, st.up,
 		    host->conf.id, host->conf.name);
 
@@ -2445,11 +2445,7 @@ relay_tls_handshake(int fd, short event, void *arg)
 
 	ret = tls_handshake(cre->tls);
 	if (ret == 0) {
-#ifdef DEBUG
-		log_info(
-#else
 		log_debug(
-#endif
 		    "relay %s, tls session %d %s (%d active)",
 		    rlay->rl_conf.name, con->se_id,
 		    cre->dir == RELAY_DIR_REQUEST ? "established" : "connected",
@@ -2498,7 +2494,7 @@ relay_tls_handshake(int fd, short event, void *arg)
 		return;
 	}
 
-	DPRINTF("%s: session %d: scheduling on %s", __func__, con->se_id,
+	log_debug("%s: session %d: scheduling on %s", __func__, con->se_id,
 	    (retry_flag == EV_READ) ? "EV_READ" : "EV_WRITE");
 	event_again(&con->se_ev, fd, EV_TIMEOUT|retry_flag, relay_tls_handshake,
 	    &con->se_tv_start, &rlay->rl_conf.timeout, cre);
diff --git a/relay_http.c b/relay_http.c
index dbaafa7..e3a668f 100644
--- a/relay_http.c
+++ b/relay_http.c
@@ -123,8 +123,7 @@ relay_http_priv_init(struct rsession *con)
 	if ((hs = calloc(1, sizeof(*hs))) == NULL)
 		return (-1);
 	SIMPLEQ_INIT(&hs->hs_methods);
-	DPRINTF("%s: session %d http_session %p", __func__,
-	    con->se_id, hs);
+	log_debug("%s: session %d http_session %p", __func__, con->se_id, hs);
 	con->se_priv = hs;
 	return (relay_httpdesc_init(&con->se_in));
 }
@@ -203,7 +202,7 @@ relay_read_http(struct bufferevent *bev, void *arg)
 	cre->timedout = 0;
 
 	size = EVBUFFER_LENGTH(src);
-	DPRINTF("%s: session %d: size %lu, to read %lld",
+	log_debug("%s: session %d: size %lu, to read %lld",
 	    __func__, con->se_id, size, cre->toread);
 	if (size == 0) {
 		if (cre->dir == RELAY_DIR_RESPONSE)
@@ -280,7 +279,7 @@ relay_read_http(struct bufferevent *bev, void *arg)
 			key = desc->http_lastheader->kv_key;
 			value = desc->http_lastheader->kv_value;
 
-			DPRINTF("%s: session %d: header '%s: %s'", __func__,
+			log_debug("%s: session %d: header '%s: %s'", __func__,
 			    con->se_id, key, value);
 
 			if (desc->http_method != HTTP_METHOD_NONE &&
@@ -644,7 +643,7 @@ relay_read_httpcontent(struct bufferevent *bev, void *arg)
 	cre->timedout = 0;
 
 	size = EVBUFFER_LENGTH(src);
-	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
+	log_debug("%s: session %d: size %lu, to read %lld", __func__,
 	    con->se_id, size, cre->toread);
 	if (!size)
 		return;
@@ -664,7 +663,7 @@ relay_read_httpcontent(struct bufferevent *bev, void *arg)
 				goto fail;
 			cre->toread -= size;
 		}
-		DPRINTF("%s: done, size %lu, to read %lld", __func__,
+		log_debug("%s: done, size %lu, to read %lld", __func__,
 		    size, cre->toread);
 	}
 	if (cre->toread == 0) {
@@ -705,7 +704,7 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg)
 	cre->timedout = 0;
 
 	size = EVBUFFER_LENGTH(src);
-	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
+	log_debug("%s: session %d: size %lu, to read %lld", __func__,
 	    con->se_id, size, cre->toread);
 	if (!size)
 		return;
@@ -725,7 +724,7 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg)
 				goto fail;
 			cre->toread -= size;
 		}
-		DPRINTF("%s: done, size %lu, to read %lld", __func__,
+		log_debug("%s: done, size %lu, to read %lld", __func__,
 		    size, cre->toread);
 	}
 	switch (cre->toread) {
@@ -768,7 +767,7 @@ relay_read_httpchunks(struct bufferevent *bev, void *arg)
 		free(line);
 
 		if ((cre->toread = llval) == 0) {
-			DPRINTF("%s: last chunk", __func__);
+			log_debug("%s: last chunk", __func__);
 			cre->toread = TOREAD_HTTP_CHUNK_TRAILER;
 		}
 		break;
@@ -870,7 +869,7 @@ _relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path,
 		break;
 	}
 
-	DPRINTF("%s: session %d: %s, %s: %d", __func__, con->se_id,
+	log_debug("%s: session %d: %s, %s: %d", __func__, con->se_id,
 	    str, kv->kv_key, strcasecmp(kv->kv_key, str));
 
 	if (strcasecmp(kv->kv_key, str) == 0) {
@@ -903,7 +902,7 @@ relay_lookup_url(struct ctl_relay_event *cre, const char *host, struct kv *kv)
 	 *     developers_guide.html#PerformingLookups
 	 */
 
-	DPRINTF("%s: host '%s', path '%s', query '%s'",
+	log_debug("%s: host '%s', path '%s', query '%s'",
 	    __func__, host, desc->http_path,
 	    desc->http_query == NULL ? "" : desc->http_query);
 
@@ -995,7 +994,7 @@ relay_lookup_cookie(struct ctl_relay_event *cre, const char *str,
 		if (value[strlen(value) - 1] == '"')
 			value[strlen(value) - 1] = '\0';
 
-		DPRINTF("%s: key %s = %s, %s = %s : %d",
+		log_debug("%s: key %s = %s, %s = %s : %d",
 		    __func__, key, value, kv->kv_key, kv->kv_value,
 		    strcasecmp(kv->kv_key, key));
 
@@ -1178,13 +1177,13 @@ relay_close_http(struct rsession *con)
 	struct http_session	*hs = con->se_priv;
 	struct http_method_node	*hmn;
 
-	DPRINTF("%s: session %d http_session %p", __func__,
+	log_debug("%s: session %d http_session %p", __func__,
 	    con->se_id, hs);
 	if (hs != NULL)
 		while (!SIMPLEQ_EMPTY(&hs->hs_methods)) {
 			hmn = SIMPLEQ_FIRST(&hs->hs_methods);
 			SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry);
-			DPRINTF("%s: session %d freeing %s", __func__,
+			log_debug("%s: session %d freeing %s", __func__,
 			    con->se_id, relay_httpmethod_byid(hmn->hmn_method));
 			free(hmn);
 		}
@@ -1298,7 +1297,7 @@ relay_writeresponse_http(struct ctl_relay_event *dst,
 {
 	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
 
-	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
+	log_debug("version: %s rescode: %s resmsg: %s", desc->http_version,
 	    desc->http_rescode, desc->http_resmesg);
 
 	if (relay_bufferevent_print(dst, desc->http_version) == -1 ||
@@ -1852,12 +1851,12 @@ relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions,
 #define	RELAY_GET_SKIP_STEP(i)						\
 	do {								\
 		r = r->rule_skip[i];					\
-		DPRINTF("%s:%d: skip %d rules", __func__, __LINE__, i);	\
+		log_debug("%s:%d: skip %d rules", __func__, __LINE__, i);\
 	} while (0)
 
 #define	RELAY_GET_NEXT_STEP						\
 	do {								\
-		DPRINTF("%s:%d: next rule", __func__, __LINE__);	\
+		log_debug("%s:%d: next rule", __func__, __LINE__);	\
 		goto nextrule;						\
 	} while (0)
 
@@ -1909,7 +1908,7 @@ relay_test(struct protocol *proto, struct ctl_relay_event *cre)
 		else if ((res = relay_httpcookie_test(cre, r, &matches)) != 0)
 			RELAY_GET_NEXT_STEP;
 		else {
-			DPRINTF("%s: session %d: matched rule %d",
+			log_debug("%s: session %d: matched rule %d",
 			    __func__, con->se_id, r->rule_id);
 
 			if (r->rule_action == RULE_ACTION_MATCH) {
@@ -1939,7 +1938,7 @@ relay_test(struct protocol *proto, struct ctl_relay_event *cre)
 
  nextrule:
 			/* Continue to find last matching policy */
-			DPRINTF("%s: session %d, res %d", __func__,
+			log_debug("%s: session %d, res %d", __func__,
 			    con->se_id, res);
 			if (res == RES_BAD || res == RES_INTERNAL)
 				return (res);
@@ -1959,7 +1958,7 @@ relay_test(struct protocol *proto, struct ctl_relay_event *cre)
 		action = RES_DROP;
 	}
 
-	DPRINTF("%s: session %d: action %d", __func__,
+	log_debug("%s: session %d: action %d", __func__,
 	    con->se_id, action);
 
 	return (action);
@@ -2045,7 +2044,7 @@ relay_http_parse_startline(struct ctl_relay_event *cre, char *line,
 	char			*key, *value;
 	const char		*errstr;
 
-	DPRINTF("%s: session %d http_session %p", __func__, con->se_id, hs);
+	log_debug("%s: session %d http_session %p", __func__, con->se_id, hs);
 
 	key = line;
 	if ((value = strchr(key, ' ')) == NULL) {
@@ -2064,12 +2063,12 @@ relay_http_parse_startline(struct ctl_relay_event *cre, char *line,
 		 */
 		if (hmn == NULL) {
 			*request_method = HTTP_METHOD_NONE;
-			DPRINTF("%s: session %d unbalanced response",
+			log_debug("%s: session %d unbalanced response",
 			    __func__, con->se_id);
 		} else {
 			SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry);
 			*request_method = hmn->hmn_method;
-			DPRINTF("%s: session %d dequeuing %s",
+			log_debug("%s: session %d dequeuing %s",
 			    __func__, con->se_id,
 			    relay_httpmethod_byid(*request_method));
 			free(hmn);
@@ -2098,13 +2097,13 @@ relay_http_parse_startline(struct ctl_relay_event *cre, char *line,
 		desc->http_status = strtonum(desc->http_rescode, 100, 599,
 		    &errstr);
 		if (errstr) {
-			DPRINTF(
+			log_debug(
 			    "%s: http_status %s: errno %d, %s",
 			    __func__, desc->http_rescode, errno,
 			    errstr);
 			goto fail;
 		}
-		DPRINTF("http_version %s http_rescode %s http_resmesg %s",
+		log_debug("http_version %s http_rescode %s http_resmesg %s",
 		    desc->http_version, desc->http_rescode,
 		    desc->http_resmesg);
 	} else if (cre->dir == RELAY_DIR_REQUEST) {
@@ -2116,7 +2115,7 @@ relay_http_parse_startline(struct ctl_relay_event *cre, char *line,
 		if ((hmn = calloc(1, sizeof *hmn)) == NULL)
 			goto fail;
 		hmn->hmn_method = desc->http_method;
-		DPRINTF("%s: session %d enqueuing %s", __func__, con->se_id,
+		log_debug("%s: session %d enqueuing %s", __func__, con->se_id,
 		    relay_httpmethod_byid(hmn->hmn_method));
 		SIMPLEQ_INSERT_TAIL(&hs->hs_methods, hmn, hmn_entry);
 		/*
diff --git a/relay_udp.c b/relay_udp.c
index 7226861..27ec18b 100644
--- a/relay_udp.c
+++ b/relay_udp.c
@@ -491,13 +491,13 @@ relay_dns_request(struct rsession *con)
 	    (struct sockaddr *)&con->se_out.ss, slen) == -1) {
 		if (con->se_retry) {
 			con->se_retry--;
-			log_debug("%s: session %d: "
+			log_warn("%s: session %d: "
 			    "forward failed: %s, %s", __func__,
 			    con->se_id, strerror(errno),
 			    con->se_retry ? "next retry" : "last retry");
 			goto retry;
 		}
-		log_debug("%s: session %d: forward failed: %s", __func__,
+		log_warn("%s: session %d: forward failed: %s", __func__,
 		    con->se_id, strerror(errno));
 		return (-1);
 	}
diff --git a/relayd.c b/relayd.c
index 7043735..34ae0d0 100644
--- a/relayd.c
+++ b/relayd.c
@@ -70,6 +70,8 @@ void		 parent_tls_ticket_rekey(int, short, void *);
 
 struct relayd			*relayd_env;
 
+static int			 cli_verbose;
+
 static struct privsep_proc procs[] = {
 	{ "pfe",	PROC_PFE, parent_dispatch_pfe, pfe },
 	{ "hce",	PROC_HCE, parent_dispatch_hce, hce },
@@ -121,7 +123,7 @@ int
 main(int argc, char *argv[])
 {
 	int			 c;
-	int			 debug = 0, verbose = 0;
+	int			 debug = 0;
 	u_int32_t		 opts = 0;
 	struct relayd		*env;
 	struct privsep		*ps;
@@ -149,7 +151,7 @@ main(int argc, char *argv[])
 			conffile = optarg;
 			break;
 		case 'v':
-			verbose++;
+			cli_verbose = 1;
 			opts |= RELAYD_OPT_VERBOSE;
 			break;
 		case 'P':
@@ -197,6 +199,10 @@ main(int argc, char *argv[])
 	if (debug)
 		env->sc_conf.opts |= RELAYD_OPT_LOGUPDATE;
 
+	/* CLI always wins over log level form config. */
+	if (cli_verbose)
+		env->sc_conf.opts |= RELAYD_OPT_VERBOSE;
+
 	if (geteuid())
 		errx(1, "need root privileges");
 
@@ -204,7 +210,7 @@ main(int argc, char *argv[])
 		errx(1, "unknown user %s", RELAYD_USER);
 
 	log_init(debug, LOG_DAEMON);
-	log_setverbose(verbose);
+	log_setverbose(env->sc_conf.opts & RELAYD_OPT_VERBOSE ? 2 : 0);
 
 	if (env->sc_conf.opts & RELAYD_OPT_NOACTION)
 		ps->ps_noaction = 1;
@@ -311,6 +317,10 @@ parent_configure(struct relayd *env)
 	/* HCE, PFE, CA and the relays need to reload their config. */
 	env->sc_reload = 2 + (2 * env->sc_conf.prefork_relay);
 
+	/* CLI always wins over log level form config. */
+	if (cli_verbose)
+		env->sc_conf.opts |= RELAYD_OPT_VERBOSE;
+
 	for (id = 0; id < PROC_MAX; id++) {
 		if (id == privsep_process)
 			continue;
@@ -343,14 +353,14 @@ parent_reload(struct relayd *env, u_int reset, const char *filename)
 
 	if (reset == CONFIG_RELOAD) {
 		if (load_config(filename, env) == -1) {
-			log_debug("%s: failed to load config file %s",
+			log_warn("%s: failed to load config file %s",
 			    __func__, filename);
 		}
 
 		config_setreset(env, CONFIG_ALL);
 
 		if (parent_configure(env) == -1) {
-			log_debug("%s: failed to commit config from %s",
+			log_warn("%s: failed to commit config from %s",
 			    __func__, filename);
 		}
 	} else
@@ -878,10 +888,8 @@ kv_find_value(struct kvtree *keys, char *key, const char *value,
 	/* not matched */
 	match = NULL;
  done:
-#ifdef DEBUG
 	if (match != NULL)
-		DPRINTF("%s: matched %s: %s", __func__, key, value);
-#endif
+		log_debug("%s: matched %s: %s", __func__, key, value);
 	free(val);
 	return (match);
 }
@@ -1516,7 +1524,6 @@ expand_string(char *label, size_t len, const char *srch, const char *repl)
 	char *p, *q;
 
 	if ((tmp = calloc(1, len)) == NULL) {
-		log_debug("%s: calloc", __func__);
 		return (-1);
 	}
 	p = label;
@@ -1524,7 +1531,7 @@ expand_string(char *label, size_t len, const char *srch, const char *repl)
 		*q = '\0';
 		if ((strlcat(tmp, p, len) >= len) ||
 		    (strlcat(tmp, repl, len) >= len)) {
-			log_debug("%s: string too long", __func__);
+			log_warn("%s: string too long", __func__);
 			free(tmp);
 			return (-1);
 		}
@@ -1532,7 +1539,7 @@ expand_string(char *label, size_t len, const char *srch, const char *repl)
 		p = q;
 	}
 	if (strlcat(tmp, p, len) >= len) {
-		log_debug("%s: string too long", __func__);
+		log_warn("%s: string too long", __func__);
 		free(tmp);
 		return (-1);
 	}
@@ -1669,7 +1676,7 @@ parse_url(const char *url, char **protoptr, char **hostptr, char **pathptr)
 	/* strip path after host */
 	host[strcspn(host, "/")] = '\0';
 
-	DPRINTF("%s: %s proto %s, host %s, path %s", __func__,
+	log_debug("%s: %s proto %s, host %s, path %s", __func__,
 	    url, proto, host, path);
 
 	*protoptr = proto;
@@ -1913,7 +1920,8 @@ accept_reserve(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
 
 	if ((ret = accept4(sockfd, addr, addrlen, SOCK_NONBLOCK)) > -1) {
 		(*counter)++;
-		DPRINTF("%s: inflight incremented, now %d", __func__, *counter);
+		log_debug("%s: inflight incremented, now %d", __func__,
+		    *counter);
 	}
 	return (ret);
 }
@@ -1926,7 +1934,7 @@ parent_tls_ticket_rekey(int fd, short events, void *arg)
 	struct timeval		 tv;
 	struct relay_ticket_key	 key;
 
-	log_debug("%s: rekeying tickets", __func__);
+	DPRINTF("%s: rekeying tickets", __func__);
 
 	key.tt_keyrev = arc4random();
 	arc4random_buf(key.tt_key, sizeof(key.tt_key));
diff --git a/relayd.conf.5 b/relayd.conf.5
index 4c48620..5cb3c5f 100644
--- a/relayd.conf.5
+++ b/relayd.conf.5
@@ -165,6 +165,18 @@ or
 When using relays, log all TCP connections.
 Optionally log only
 .Ic connection errors .
+.It Xo
+.Ic log level
+.Pq Ic brief Ns | Ns Ic verbose
+.Xc
+Set the log verbosity.
+.Ic brief
+is the default;
+.Ic verbose
+enables debug logging, equivalent to starting
+.Xr relayd 8
+with
+.Fl v .
 .It Ic prefork Ar number
 When using relays, run the specified number of processes to handle
 relayed connections.
diff --git a/relayd.h b/relayd.h
index 2aa6eab..4cfd547 100644
--- a/relayd.h
+++ b/relayd.h
@@ -1159,9 +1159,7 @@ int	 cmdline_symset(char *);
 const char *host_error(enum host_error);
 const char *host_status(enum host_status);
 const char *table_check(enum table_check);
-#ifdef DEBUG
 const char *relay_state(enum relay_state);
-#endif
 const char	*print_availability(u_long, u_long);
 const char	*print_host(struct sockaddr_storage *, char *, size_t);
 const char	*print_time(struct timeval *, struct timeval *, char *, size_t);
diff --git a/util.c b/util.c
index 527891f..472b99f 100644
--- a/util.c
+++ b/util.c
@@ -179,7 +179,6 @@ table_check(enum table_check check)
 	return ("invalid");
 }
 
-#ifdef DEBUG
 const char *
 relay_state(enum relay_state state)
 {
@@ -200,7 +199,6 @@ relay_state(enum relay_state state)
 	/* NOTREACHED */
 	return ("invalid");
 }
-#endif
 
 const char *
 print_availability(u_long cnt, u_long up)