On 07/03/2017 01:46 PM, Andjelko Iharos wrote:
> On 07/02/2017 09:52 PM, Willy Tarreau wrote:
>> On Sat, Jul 01, 2017 at 01:37:52AM +0200, Dennis Jacobfeuerborn wrote:

>>> This could either be introduced only in the current development version
>>> because of the compatibility breakage or a parameter could be introduced
>>> to configure the socket with the new "protocol". If that parameter is
>>> not set then the code could simply not be sent in the response and
>>> messages would be returned just as they are now.
>>
>> This is a good idea. We could for example have a CLI command to enable
>> error code reporting in front of messages. It could become the default
>> in a future version, keeping an option on the stats socket config line
>> to go back to the current output.
> 
> Perhaps also add an optional config-file setting to set feedback type on
> startup (that can be overriden through a CLI command)?
> 
> Within each use case (different types of automation or no automation)
> one type of output is always likely to be the most desirable. Being able
> to set that with a startup option should be easy for adapting (or not
> adapting) external tools even if the default feedback type changes over
> time.

After some musing here are two patches implementing this, attached for
comments.

The first patch mostly just adds the code to support producing severity
output as number or string in cli interactions, as well as configuring
and modifying the behavior.

The second patch inserts severity information in all places I found
where cli messages are set. In total there are 120, 107 are LOG_ERR, 6
LOG_INFO, 5 LOG_NOTICE and 2 LOG_WARNING.

Contributors of cli functionality that produces messages are
particularly welcome to comment on the severity levels I assigned.

Cheers,
Andjelko

From d0b1ff14e2d518ff07e262c6dc6fdbc481ec6466 Mon Sep 17 00:00:00 2001
From: Andjelko Iharos <aiha...@haproxy.com>
Date: Thu, 20 Jul 2017 16:49:14 +0200
Subject: [PATCH 2/2] MINOR: add severity information to cli feedback messages

---
 src/cli.c         | 26 ++++++++++++++++++----
 src/dns.c         |  1 +
 src/map.c         | 64 ++++++++++++++++++++++++++++++++++++++++++++-----------
 src/proto_http.c  |  1 +
 src/proxy.c       | 16 ++++++++++++++
 src/server.c      | 26 ++++++++++++++++++++++
 src/ssl_sock.c    |  9 ++++++++
 src/stats.c       |  1 +
 src/stick_table.c | 24 +++++++++++++++++++++
 src/stream.c      |  2 ++
 10 files changed, 154 insertions(+), 16 deletions(-)

diff --git a/src/cli.c b/src/cli.c
index 8e085c2..066c423 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -385,6 +385,7 @@ int cli_has_level(struct appctx *appctx, int level)
 	struct stream *s = si_strm(si);
 
 	if ((strm_li(s)->bind_conf->level & ACCESS_LVL_MASK) < level) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = stats_permission_denied_msg;
 		appctx->st0 = CLI_ST_PRINT;
 		return 0;
@@ -610,10 +611,14 @@ static void cli_io_handler(struct appctx *appctx)
 				else if (strcmp(trash.str, "help") == 0 ||
 					 !cli_parse_request(appctx, trash.str)) {
 					cli_gen_usage_msg();
-					if (dynamic_usage_msg)
+					if (dynamic_usage_msg) {
+						appctx->ctx.cli.severity = LOG_INFO;
 						appctx->ctx.cli.msg = dynamic_usage_msg;
-					else
+					}
+					else {
+						appctx->ctx.cli.severity = LOG_INFO;
 						appctx->ctx.cli.msg = stats_sock_usage_msg;
+					}
 					appctx->st0 = CLI_ST_PRINT;
 				}
 				/* NB: stats_sock_parse_request() may have put
@@ -626,10 +631,14 @@ static void cli_io_handler(struct appctx *appctx)
 				 * prompt and find help.
 				 */
 				cli_gen_usage_msg();
-				if (dynamic_usage_msg)
+				if (dynamic_usage_msg) {
+					appctx->ctx.cli.severity = LOG_INFO;
 					appctx->ctx.cli.msg = dynamic_usage_msg;
-				else
+				}
+				else {
+					appctx->ctx.cli.severity = LOG_INFO;
 					appctx->ctx.cli.msg = stats_sock_usage_msg;
+				}
 				appctx->st0 = CLI_ST_PRINT;
 			}
 
@@ -896,6 +905,7 @@ static int cli_parse_show_env(char **args, struct appctx *appctx, void *private)
 				break;
 		}
 		if (!*var) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Variable not found\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -917,6 +927,7 @@ static int cli_parse_set_timeout(char **args, struct appctx *appctx, void *priva
 		const char *res;
 
 		if (!*args[3]) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Expects an integer value.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -924,6 +935,7 @@ static int cli_parse_set_timeout(char **args, struct appctx *appctx, void *priva
 
 		res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
 		if (res || timeout < 1) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Invalid timeout value.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -934,6 +946,7 @@ static int cli_parse_set_timeout(char **args, struct appctx *appctx, void *priva
 		return 1;
 	}
 	else {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -949,6 +962,7 @@ static int cli_parse_set_maxconn_global(char **args, struct appctx *appctx, void
 		return 1;
 
 	if (!*args[3]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Expects an integer value.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -956,6 +970,7 @@ static int cli_parse_set_maxconn_global(char **args, struct appctx *appctx, void
 
 	v = atoi(args[3]);
 	if (v > global.hardmaxconn) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Value out of range.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1031,6 +1046,7 @@ static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *pri
 		mul = 1024;
 	}
 	else {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg =
 			"'set rate-limit' only supports :\n"
 			"   - 'connections global' to set the per-process maximum connection rate\n"
@@ -1044,6 +1060,7 @@ static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *pri
 	}
 
 	if (!*args[4]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Expects an integer value.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1051,6 +1068,7 @@ static int cli_parse_set_ratelimit(char **args, struct appctx *appctx, void *pri
 
 	v = atoi(args[4]);
 	if (v < 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Value out of range.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
diff --git a/src/dns.c b/src/dns.c
index 78ee62b..2ce528e 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -1963,6 +1963,7 @@ static int cli_parse_stat_resolvers(char **args, struct appctx *appctx, void *pr
 			}
 		}
 		if (appctx->ctx.cli.p0 == NULL) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Can't find that resolvers section\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
diff --git a/src/map.c b/src/map.c
index 0c48683..816fd66 100644
--- a/src/map.c
+++ b/src/map.c
@@ -25,6 +25,7 @@
 #include <proto/applet.h>
 #include <proto/arg.h>
 #include <proto/cli.h>
+#include <proto/log.h>
 #include <proto/map.h>
 #include <proto/pattern.h>
 #include <proto/stream_interface.h>
@@ -569,10 +570,14 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
 
 		/* No parameter. */
 		if (!*args[2] || !*args[3]) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
@@ -580,10 +585,14 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
 		/* lookup into the maps */
 		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.map.ref) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
@@ -596,6 +605,7 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
 		appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
 		appctx->ctx.map.chunk.str = strdup(args[3]);
 		if (!appctx->ctx.map.chunk.str) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Out of memory error.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -635,10 +645,14 @@ static int cli_parse_show_map(char **args, struct appctx *appctx, void *private)
 		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.map.ref ||
 		    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
@@ -660,6 +674,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
 
 		/* Expect three parameters: map name, key and new value. */
 		if (!*args[2] || !*args[3] || !*args[4]) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -668,6 +683,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
 		/* Lookup the reference in the maps. */
 		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.map.ref) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -684,6 +700,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
 			/* Convert argument to integer value. */
 			conv = strtoll(&args[3][1], &error, 16);
 			if (*error != '\0') {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -692,6 +709,7 @@ static int cli_parse_set_map(char **args, struct appctx *appctx, void *private)
 			/* Convert and check integer to pointer. */
 			ref = (struct pat_ref_elt *)(long)conv;
 			if ((long long int)(long)ref != conv) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -746,6 +764,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
 		 */
 		if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 			if (!*args[2] || !*args[3] || !*args[4]) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -753,6 +772,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
 		}
 		else {
 			if (!*args[2] || !*args[3]) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -762,10 +782,14 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
 		/* Lookup for the reference. */
 		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.map.ref) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
@@ -775,6 +799,7 @@ static int cli_parse_add_map(char **args, struct appctx *appctx, void *private)
 		 */
 		if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
 		    (appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
 				"You must use the command 'add map' to add values.\n";
 			appctx->st0 = CLI_ST_PRINT;
@@ -813,6 +838,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 	/* Expect two parameters: map name and key. */
 	if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 		if (!*args[2] || !*args[3]) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -821,6 +847,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 
 	else {
 		if (!*args[2] || !*args[3]) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -831,6 +858,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 	appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 	if (!appctx->ctx.map.ref ||
 	    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -847,6 +875,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 		/* Convert argument to integer value. */
 		conv = strtoll(&args[3][1], &error, 16);
 		if (*error != '\0') {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -855,6 +884,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 		/* Convert and check integer to pointer. */
 		ref = (struct pat_ref_elt *)(long)conv;
 		if ((long long int)(long)ref != conv) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -863,6 +893,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 		/* Try to delete the entry. */
 		if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
 			/* The entry is not found, send message. */
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Key not found.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -874,6 +905,7 @@ static int cli_parse_del_map(char **args, struct appctx *appctx, void *private)
 		 */
 		if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
 			/* The entry is not found, send message. */
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Key not found.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -897,10 +929,14 @@ static int cli_parse_clear_map(char **args, struct appctx *appctx, void *private
 
 		/* no parameter */
 		if (!*args[2]) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Missing map identifier.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Missing ACL identifier.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
@@ -909,10 +945,14 @@ static int cli_parse_clear_map(char **args, struct appctx *appctx, void *private
 		appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.map.ref ||
 		    !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
-			if (appctx->ctx.map.display_flags == PAT_REF_MAP)
+			if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
-			else
+			}
+			else {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
+			}
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
diff --git a/src/proto_http.c b/src/proto_http.c
index e72e7e9..836abf9 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -13136,6 +13136,7 @@ static int cli_parse_show_errors(char **args, struct appctx *appctx, void *priva
 			appctx->ctx.errors.iid = atoi(args[2]);
 
 		if (!appctx->ctx.errors.iid) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "No such proxy.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
diff --git a/src/proxy.c b/src/proxy.c
index 1481089..69aa7d6 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1350,6 +1350,7 @@ struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg)
 	struct proxy *px;
 
 	if (!*arg) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "A frontend name is expected.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
@@ -1357,6 +1358,7 @@ struct proxy *cli_find_frontend(struct appctx *appctx, const char *arg)
 
 	px = proxy_fe_by_name(arg);
 	if (!px) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "No such frontend.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
@@ -1373,6 +1375,7 @@ struct proxy *cli_find_backend(struct appctx *appctx, const char *arg)
 	struct proxy *px;
 
 	if (!*arg) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "A backend name is expected.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
@@ -1380,6 +1383,7 @@ struct proxy *cli_find_backend(struct appctx *appctx, const char *arg)
 
 	px = proxy_be_by_name(arg);
 	if (!px) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "No such backend.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
@@ -1402,6 +1406,7 @@ static int cli_parse_show_servers(char **args, struct appctx *appctx, void *priv
 		px = proxy_be_by_name(args[3]);
 
 		if (!px) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Can't find backend.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -1618,6 +1623,7 @@ static int cli_parse_set_dyncookie_key_backend(char **args, struct appctx *appct
 		return 1;
 
 	if (!*args[4]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "String value expected.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1625,6 +1631,7 @@ static int cli_parse_set_dyncookie_key_backend(char **args, struct appctx *appct
 
 	newkey = strdup(args[4]);
 	if (!newkey) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Failed to allocate memory.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1653,6 +1660,7 @@ static int cli_parse_set_maxconn_frontend(char **args, struct appctx *appctx, vo
 		return 1;
 
 	if (!*args[4]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Integer value expected.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1660,6 +1668,7 @@ static int cli_parse_set_maxconn_frontend(char **args, struct appctx *appctx, vo
 
 	v = atoi(args[4]);
 	if (v < 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Value out of range.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1694,6 +1703,7 @@ static int cli_parse_shutdown_frontend(char **args, struct appctx *appctx, void
 		return 1;
 
 	if (px->state == PR_STSTOPPED) {
+		appctx->ctx.cli.severity = LOG_NOTICE;
 		appctx->ctx.cli.msg = "Frontend was already shut down.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1720,18 +1730,21 @@ static int cli_parse_disable_frontend(char **args, struct appctx *appctx, void *
 		return 1;
 
 	if (px->state == PR_STSTOPPED) {
+		appctx->ctx.cli.severity = LOG_NOTICE;
 		appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (px->state == PR_STPAUSED) {
+		appctx->ctx.cli.severity = LOG_NOTICE;
 		appctx->ctx.cli.msg = "Frontend is already disabled.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (!pause_proxy(px)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -1752,18 +1765,21 @@ static int cli_parse_enable_frontend(char **args, struct appctx *appctx, void *p
 		return 1;
 
 	if (px->state == PR_STSTOPPED) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (px->state != PR_STPAUSED) {
+		appctx->ctx.cli.severity = LOG_NOTICE;
 		appctx->ctx.cli.msg = "Frontend is already enabled.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (!resume_proxy(px)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
diff --git a/src/server.c b/src/server.c
index 008bafa..51aa368 100644
--- a/src/server.c
+++ b/src/server.c
@@ -4250,18 +4250,21 @@ struct server *cli_find_server(struct appctx *appctx, char *arg)
 		}
 
 	if (!*line || !*arg) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Require 'backend/server'.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
 	}
 
 	if (!get_backend_server(arg, line, &px, &sv)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
 	}
 
 	if (px->state == PR_STSTOPPED) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Proxy is disabled.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return NULL;
@@ -4286,6 +4289,7 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 	if (strcmp(args[3], "weight") == 0) {
 		warning = server_parse_weight_change_request(sv, args[4]);
 		if (warning) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = warning;
 			appctx->st0 = CLI_ST_PRINT;
 		}
@@ -4298,12 +4302,14 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 		else if (strcmp(args[4], "maint") == 0)
 			srv_adm_set_maint(sv);
 		else {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain' and 'maint'.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
 	}
 	else if (strcmp(args[3], "health") == 0) {
 		if (sv->track) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "cannot change health on a tracking server.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
@@ -4320,12 +4326,14 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 			srv_set_stopped(sv, "changed from CLI");
 		}
 		else {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'set server <srv> health' expects 'up', 'stopping', or 'down'.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
 	}
 	else if (strcmp(args[3], "agent") == 0) {
 		if (!(sv->agent.state & CHK_ST_ENABLED)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
@@ -4338,16 +4346,19 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 			srv_set_stopped(sv, "changed from CLI");
 		}
 		else {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'set server <srv> agent' expects 'up' or 'down'.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
 	}
 	else if (strcmp(args[3], "agent-addr") == 0) {
 		if (!(sv->agent.state & CHK_ST_ENABLED)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		} else {
 			if (str2ip(args[4], &sv->agent.addr) == NULL) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "incorrect addr address given for agent.\n";
 				appctx->st0 = CLI_ST_PRINT;
 			}
@@ -4355,11 +4366,13 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 	}
 	else if (strcmp(args[3], "agent-send") == 0) {
 		if (!(sv->agent.state & CHK_ST_ENABLED)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		} else {
 			char *nss = strdup(args[4]);
 			if (!nss) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "cannot allocate memory for new string.\n";
 				appctx->st0 = CLI_ST_PRINT;
 			} else {
@@ -4372,20 +4385,24 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 	else if (strcmp(args[3], "check-port") == 0) {
 		int i = 0;
 		if (strl2irc(args[4], strlen(args[4]), &i) != 0) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'set server <srv> check-port' expects an integer as argument.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
 		if ((i < 0) || (i > 65535)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "provided port is not valid.\n";
 			appctx->st0 = CLI_ST_PRINT;
 		}
 		/* prevent the update of port to 0 if MAPPORTS are in use */
 		if ((sv->flags & SRV_F_MAPPORTS) && (i == 0)) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "can't unset 'port' since MAPPORTS is in use.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
 		}
 		sv->check.port = i;
+		appctx->ctx.cli.severity = LOG_NOTICE;
 		appctx->ctx.cli.msg = "health check port updated.\n";
 		appctx->st0 = CLI_ST_PRINT;
 	}
@@ -4393,6 +4410,7 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 		char *addr = NULL;
 		char *port = NULL;
 		if (strlen(args[4]) == 0) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "set server <b>/<s> addr requires an address and optionally a port.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -4405,6 +4423,7 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 		}
 		warning = update_server_addr_port(sv, addr, port, "stats socket command");
 		if (warning) {
+			appctx->ctx.cli.severity = LOG_WARNING;
 			appctx->ctx.cli.msg = warning;
 			appctx->st0 = CLI_ST_PRINT;
 		}
@@ -4418,11 +4437,13 @@ static int cli_parse_set_server(char **args, struct appctx *appctx, void *privat
 		}
 		warning = update_server_fqdn(sv, args[4], "stats socket command");
 		if (warning) {
+			appctx->ctx.cli.severity = LOG_WARNING;
 			appctx->ctx.cli.msg = warning;
 			appctx->st0 = CLI_ST_PRINT;
 		}
 	}
 	else {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state', 'weight', 'addr', 'fqdn' and 'check-port'.\n";
 		appctx->st0 = CLI_ST_PRINT;
 	}
@@ -4445,12 +4466,14 @@ static int cli_parse_get_weight(char **args, struct appctx *appctx, void *privat
 		}
 
 	if (!*line) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Require 'backend/server'.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (!get_backend_server(args[2], line, &px, &sv)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -4479,6 +4502,7 @@ static int cli_parse_set_weight(char **args, struct appctx *appctx, void *privat
 
 	warning = server_parse_weight_change_request(sv, args[3]);
 	if (warning) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = warning;
 		appctx->st0 = CLI_ST_PRINT;
 	}
@@ -4500,6 +4524,7 @@ static int cli_parse_set_maxconn_server(char **args, struct appctx *appctx, void
 
 	warning = server_parse_maxconn_change_request(sv, args[4]);
 	if (warning) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = warning;
 		appctx->st0 = CLI_ST_PRINT;
 	}
@@ -4567,6 +4592,7 @@ static int cli_parse_enable_agent(char **args, struct appctx *appctx, void *priv
 		return 1;
 
 	if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index fa81571..fc92dfc 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -7778,6 +7778,7 @@ static int cli_parse_show_tlskeys(char **args, struct appctx *appctx, void *priv
 	} else {
 		appctx->ctx.cli.p0 = tlskeys_ref_lookup_ref(args[2]);
 		if (!appctx->ctx.cli.p0) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "'show tls-keys' unable to locate referenced filename\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -7793,6 +7794,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva
 
 	/* Expect two parameters: the filename and the new new TLS key in encoding */
 	if (!*args[3] || !*args[4]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set ssl tls-key' expects a filename and the new TLS key in base64 encoding.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -7800,6 +7802,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva
 
 	ref = tlskeys_ref_lookup_ref(args[3]);
 	if (!ref) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set ssl tls-key' unable to locate referenced filename\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -7807,6 +7810,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva
 
 	trash.len = base64dec(args[4], strlen(args[4]), trash.str, trash.size);
 	if (trash.len != sizeof(struct tls_sess_key)) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set ssl tls-key' received invalid base64 encoded TLS key.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -7815,6 +7819,7 @@ static int cli_parse_set_tlskeys(char **args, struct appctx *appctx, void *priva
 	memcpy(ref->tlskeys + ((ref->tls_ticket_enc_index + 2) % TLS_TICKETS_NO), trash.str, trash.len);
 	ref->tls_ticket_enc_index = (ref->tls_ticket_enc_index + 1) % TLS_TICKETS_NO;
 
+	appctx->ctx.cli.severity = LOG_INFO;
 	appctx->ctx.cli.msg = "TLS ticket key updated!";
 	appctx->st0 = CLI_ST_PRINT;
 	return 1;
@@ -7829,6 +7834,7 @@ static int cli_parse_set_ocspresponse(char **args, struct appctx *appctx, void *
 
 	/* Expect one parameter: the new response in base64 encoding */
 	if (!*args[3]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set ssl ocsp-response' expects response in base64 encoding.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -7836,6 +7842,7 @@ static int cli_parse_set_ocspresponse(char **args, struct appctx *appctx, void *
 
 	trash.len = base64dec(args[3], strlen(args[3]), trash.str, trash.size);
 	if (trash.len < 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "'set ssl ocsp-response' received invalid base64 encoded response.\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -7849,10 +7856,12 @@ static int cli_parse_set_ocspresponse(char **args, struct appctx *appctx, void *
 		}
 		return 1;
 	}
+	appctx->ctx.cli.severity = LOG_INFO;
 	appctx->ctx.cli.msg = "OCSP Response updated!";
 	appctx->st0 = CLI_ST_PRINT;
 	return 1;
 #else
+	appctx->ctx.cli.severity = LOG_ERR;
 	appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n";
 	appctx->st0 = CLI_ST_PRINT;
 	return 1;
diff --git a/src/stats.c b/src/stats.c
index 71230d0..a4b0274 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -3614,6 +3614,7 @@ static int cli_parse_show_stat(char **args, struct appctx *appctx, void *private
 			appctx->ctx.stats.iid = atoi(args[2]);
 
 		if (!appctx->ctx.stats.iid) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "No such proxy.\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
diff --git a/src/stick_table.c b/src/stick_table.c
index 8cc7dd2..072a32f 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -29,6 +29,7 @@
 
 #include <proto/arg.h>
 #include <proto/cli.h>
+#include <proto/log.h>
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/proxy.h>
@@ -2355,6 +2356,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 	struct freq_ctr_period *frqp;
 
 	if (!*args[4]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Key value expected\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -2378,6 +2380,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 			if ((errno == ERANGE && val == ULONG_MAX) ||
 			    (errno != 0 && val == 0) || endptr == args[4] ||
 			    val > 0xffffffff) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Invalid key\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -2394,15 +2397,19 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 	default:
 		switch (appctx->ctx.table.action) {
 		case STK_CLI_ACT_SHOW:
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
 			break;
 		case STK_CLI_ACT_CLR:
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Removing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
 			break;
 		case STK_CLI_ACT_SET:
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Inserting keys into tables of type other than ip, ipv6, string and integer is not supported\n";
 			break;
 		default:
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Unknown action\n";
 			break;
 		}
@@ -2432,6 +2439,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 			return 1;
 		if (ts->ref_cnt) {
 			/* don't delete an entry which is currently referenced */
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -2446,6 +2454,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 			ts = stksess_new(&px->table, static_table_key);
 			if (!ts) {
 				/* don't delete an entry which is currently referenced */
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -2455,6 +2464,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 
 		for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
 			if (strncmp(args[cur_arg], "data.", 5) != 0) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -2462,18 +2472,21 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 
 			data_type = stktable_get_data_type(args[cur_arg] + 5);
 			if (data_type < 0) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Unknown data type\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
 			}
 
 			if (!px->table.data_ofs[data_type]) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Data type not stored in this table\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
 			}
 
 			if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
+				appctx->ctx.cli.severity = LOG_ERR;
 				appctx->ctx.cli.msg = "Require a valid integer value to store\n";
 				appctx->st0 = CLI_ST_PRINT;
 				return 1;
@@ -2507,6 +2520,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 		break;
 
 	default:
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Unknown action\n";
 		appctx->st0 = CLI_ST_PRINT;
 		break;
@@ -2520,6 +2534,7 @@ static int table_process_entry_per_key(struct appctx *appctx, char **args)
 static int table_prepare_data_request(struct appctx *appctx, char **args)
 {
 	if (appctx->ctx.table.action != STK_CLI_ACT_SHOW && appctx->ctx.table.action != STK_CLI_ACT_CLR) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -2528,12 +2543,14 @@ static int table_prepare_data_request(struct appctx *appctx, char **args)
 	/* condition on stored data value */
 	appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
 	if (appctx->ctx.table.data_type < 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Unknown data type\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Data type not stored in this table\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -2541,12 +2558,14 @@ static int table_prepare_data_request(struct appctx *appctx, char **args)
 
 	appctx->ctx.table.data_op = get_std_op(args[4]);
 	if (appctx->ctx.table.data_op < 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
 	}
 
 	if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -2568,6 +2587,7 @@ static int cli_parse_table_req(char **args, struct appctx *appctx, void *private
 	if (*args[2]) {
 		appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
 		if (!appctx->ctx.table.target) {
+			appctx->ctx.cli.severity = LOG_ERR;
 			appctx->ctx.cli.msg = "No such table\n";
 			appctx->st0 = CLI_ST_PRINT;
 			return 1;
@@ -2591,15 +2611,19 @@ static int cli_parse_table_req(char **args, struct appctx *appctx, void *private
 err_args:
 	switch (appctx->ctx.table.action) {
 	case STK_CLI_ACT_SHOW:
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
 		break;
 	case STK_CLI_ACT_CLR:
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
 		break;
 	case STK_CLI_ACT_SET:
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Required arguments: <table> key <key> [data.<store_data_type> <value>]*\n";
 		break;
 	default:
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Unknown action\n";
 		break;
 	}
diff --git a/src/stream.c b/src/stream.c
index 1aa5475..32cab3b 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -3169,6 +3169,7 @@ static int cli_parse_shutdown_session(char **args, struct appctx *appctx, void *
 		return 1;
 
 	if (!*args[2]) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
@@ -3184,6 +3185,7 @@ static int cli_parse_shutdown_session(char **args, struct appctx *appctx, void *
 
 	/* do we have the stream ? */
 	if (strm != ptr) {
+		appctx->ctx.cli.severity = LOG_ERR;
 		appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
 		appctx->st0 = CLI_ST_PRINT;
 		return 1;
-- 
2.7.4

From 215841e543cc3f4aa6eb7f6a8697776ebc394337 Mon Sep 17 00:00:00 2001
From: Andjelko Iharos <aiha...@haproxy.com>
Date: Thu, 20 Jul 2017 11:59:48 +0200
Subject: [PATCH 1/2] MINOR: cli: add socket commands and config to prepend
 informational messages with severity

Adds cli commands to change at runtime whether informational messages
are prepended with severity level or not, with support for numeric and
worded severity in line with syslog severity level.

Adds stats socket config keyword severity-output to set default behavior
per socket on startup.
---
 doc/configuration.txt    | 12 +++++++
 doc/management.txt       |  4 +++
 include/proto/listener.h |  2 ++
 include/types/applet.h   |  2 ++
 include/types/cli.h      |  8 +++++
 include/types/listener.h |  1 +
 src/cli.c                | 93 ++++++++++++++++++++++++++++++++++++++++++++++--
 7 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 0f425f4..8660c33 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10550,6 +10550,18 @@ level <level>
   - "admin" should be used with care, as everything is permitted (eg: clear
     all counters).
 
+severity-output <format>
+  This setting is used with the stats sockets only to configure severity
+  level output prepended to informational feedback messages. Severity
+  level of messages can range between 0 and 7, conforming to syslog
+  rfc5424. Valid and successful socket commands requesting data
+  (i.e. "show map", "get acl foo" etc.) will never have a severity level
+  prepended. It is ignored by other sockets. <format> can be one of :
+  - "none" (default) no severity level is prepended to feedback messages.
+  - "number" severity level is prepended as a number.
+  - "string" severity level is prepended as a string following the
+    rfc5424 convention.
+
 maxconn <maxconn>
   Limits the sockets to this number of concurrent connections. Extraneous
   connections will remain in the system's backlog until a connection is
diff --git a/doc/management.txt b/doc/management.txt
index df091bb..6187078 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1665,6 +1665,10 @@ set server <backend>/<server> weight <weight>[%]
 set server <backend>/<server> fqdn <FQDN>
   Change a server's FQDN to the value passed in argument.
 
+set severity-output [ none | number | string ]
+  Change the severity output format of the stats socket connected to for the
+  duration of the current session.
+
 set ssl ocsp-response <response>
   This command is used to update an OCSP Response for a certificate (see "crt"
   on "bind" lines). Same controls are performed as during the initial loading of
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 079d976..491211e 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include <types/listener.h>
+#include <types/cli.h>
 
 /* This function adds the specified listener's file descriptor to the polling
  * lists if it is in the LI_LISTEN state. The listener enters LI_READY or
@@ -144,6 +145,7 @@ static inline struct bind_conf *bind_conf_alloc(struct proxy *fe, const char *fi
 	bind_conf->ux.mode = 0;
 	bind_conf->xprt = xprt;
 	bind_conf->frontend = fe;
+	bind_conf->severity_output = CLI_SEVERITY_NONE;
 
 	LIST_INIT(&bind_conf->listeners);
 	return bind_conf;
diff --git a/include/types/applet.h b/include/types/applet.h
index e81cff7..4c62ac3 100644
--- a/include/types/applet.h
+++ b/include/types/applet.h
@@ -64,6 +64,7 @@ struct appctx {
 	int (*io_handler)(struct appctx *appctx);  /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK */
 	void (*io_release)(struct appctx *appctx);  /* used within the cli_io_handler when st0 = CLI_ST_CALLBACK,
 	                                               if the command is terminated or the session released */
+	int cli_severity_output;        /* used within the cli_io_handler to format severity output of informational feedback */
 	struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */
 	unsigned long process_mask;     /* mask of thread IDs authorized to process the applet */
 
@@ -96,6 +97,7 @@ struct appctx {
 		} spoe;                         /* used by SPOE filter */
 		struct {
 			const char *msg;        /* pointer to a persistent message to be returned in CLI_ST_PRINT state */
+			int severity;           /* severity of the message to be returned according to (syslog) rfc5424 */
 			char *err;              /* pointer to a 'must free' message to be returned in CLI_ST_PRINT_FREE state */
 			void *p0, *p1;          /* general purpose pointers and integers for registered commands, initialized */
 			int i0, i1;             /* to 0 by the CLI before first invocation of the keyword parser. */
diff --git a/include/types/cli.h b/include/types/cli.h
index 80da45d..63e0e9d 100644
--- a/include/types/cli.h
+++ b/include/types/cli.h
@@ -50,5 +50,13 @@ enum {
 	CLI_ST_CALLBACK,   /* custom callback pointer */
 };
 
+/* CLI severity output formats */
+enum {
+	CLI_SEVERITY_UNDEFINED = 0, /* undefined severity format */
+	CLI_SEVERITY_NONE,          /* no severity information prepended */
+	CLI_SEVERITY_NUMBER,        /* prepend informational cli messages with a severity as number */
+	CLI_SEVERITY_STRING,        /* prepend informational cli messages with a severity as string */
+};
+
 
 #endif /* _TYPES_CLI_H */
diff --git a/include/types/listener.h b/include/types/listener.h
index 9a77e96..889c5fd 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -161,6 +161,7 @@ struct bind_conf {
 		mode_t mode;       /* 0 to leave unchanged */
 	} ux;
 	int level;                 /* stats access level (ACCESS_LVL_*) */
+	int severity_output;       /* default severity output format in cli feedback messages */
 	struct list by_fe;         /* next binding for the same frontend, or NULL */
 	struct list listeners;     /* list of listeners using this bind config */
 	uint32_t ns_cip_magic;     /* Excepted NetScaler Client IP magic number */
diff --git a/src/cli.c b/src/cli.c
index fef34e9..8e085c2 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -392,6 +392,13 @@ int cli_has_level(struct appctx *appctx, int level)
 	return 1;
 }
 
+/* Returns severity_output for the current session if set, or default for the socket */
+static int cli_get_severity_output(struct appctx *appctx)
+{
+	if (appctx->cli_severity_output)
+		return appctx->cli_severity_output;
+	return strm_li(si_strm(appctx->owner))->bind_conf->severity_output;
+}
 
 /* Processes the CLI interpreter on the stats socket. This function is called
  * from the CLI's IO handler running in an appctx context. The function returns 1
@@ -472,6 +479,38 @@ static int cli_parse_request(struct appctx *appctx, char *line)
 	return 1;
 }
 
+/* prepends then outputs the argument msg with a syslog-type severity depending on severity_output value */
+static int cli_output_msg(struct channel *chn, const char *msg, int severity, int severity_output)
+{
+	struct chunk *tmp;
+
+	if (likely(severity_output == CLI_SEVERITY_NONE))
+		return bi_putblk(chn, msg, strlen(msg));
+
+	tmp = get_trash_chunk();
+	chunk_reset(tmp);
+
+	if (severity < 0 || severity > 7) {
+		Warning("socket command feedback with invalid severity %d", severity);
+		chunk_printf(tmp, "[%d]: ", severity);
+	}
+	else {
+		switch (severity_output) {
+			case CLI_SEVERITY_NUMBER:
+				chunk_printf(tmp, "[%d]: ", severity);
+				break;
+			case CLI_SEVERITY_STRING:
+				chunk_printf(tmp, "[%s]: ", log_levels[severity]);
+				break;
+			default:
+				Warning("Unrecognized severity output %d", severity_output);
+		}
+	}
+	chunk_appendf(tmp, "%s", msg);
+
+	return bi_putblk(chn, tmp->str, strlen(tmp->str));
+}
+
 /* This I/O handler runs as an applet embedded in a stream interface. It is
  * used to processes I/O from/to the stats unix socket. The system relies on a
  * state machine handling requests and various responses. We read a request,
@@ -485,6 +524,7 @@ static void cli_io_handler(struct appctx *appctx)
 	struct stream_interface *si = appctx->owner;
 	struct channel *req = si_oc(si);
 	struct channel *res = si_ic(si);
+	struct bind_conf *bind_conf = strm_li(si_strm(si))->bind_conf;
 	int reql;
 	int len;
 
@@ -501,6 +541,8 @@ static void cli_io_handler(struct appctx *appctx)
 		if (appctx->st0 == CLI_ST_INIT) {
 			/* Stats output not initialized yet */
 			memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
+			/* reset severity to default at init */
+			appctx->cli_severity_output = bind_conf->severity_output;
 			appctx->st0 = CLI_ST_GETREQ;
 		}
 		else if (appctx->st0 == CLI_ST_END) {
@@ -600,13 +642,14 @@ static void cli_io_handler(struct appctx *appctx)
 			case CLI_ST_PROMPT:
 				break;
 			case CLI_ST_PRINT:
-				if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1)
+				if (cli_output_msg(res, appctx->ctx.cli.msg, appctx->ctx.cli.severity,
+							cli_get_severity_output(appctx)) != -1)
 					appctx->st0 = CLI_ST_PROMPT;
 				else
 					si_applet_cant_put(si);
 				break;
 			case CLI_ST_PRINT_FREE:
-				if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) {
+				if (cli_output_msg(res, appctx->ctx.cli.err, LOG_ERR, cli_get_severity_output(appctx)) != -1) {
 					free(appctx->ctx.cli.err);
 					appctx->st0 = CLI_ST_PROMPT;
 				}
@@ -931,6 +974,34 @@ static int cli_parse_set_maxconn_global(char **args, struct appctx *appctx, void
 	return 1;
 }
 
+static int set_severity_output(int *target, char *argument)
+{
+	if (!strcmp(argument, "none")) {
+		*target = CLI_SEVERITY_NONE;
+		return 1;
+	}
+	else if (!strcmp(argument, "number")) {
+		*target = CLI_SEVERITY_NUMBER;
+		return 1;
+	}
+	else if (!strcmp(argument, "string")) {
+		*target = CLI_SEVERITY_STRING;
+		return 1;
+	}
+	return 0;
+}
+
+/* parse a "set severity-output" command. */
+static int cli_parse_set_severity_output(char **args, struct appctx *appctx, void *private)
+{
+	if (*args[2] && set_severity_output(&appctx->cli_severity_output, args[2]))
+		return 0;
+
+	appctx->ctx.cli.severity = LOG_ERR;
+	appctx->ctx.cli.msg = "one of 'none', 'number', 'string' is a required argument";
+	appctx->st0 = CLI_ST_PRINT;
+	return 1;
+}
 
 int cli_parse_default(char **args, struct appctx *appctx, void *private)
 {
@@ -1038,6 +1109,22 @@ static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct b
 	return 0;
 }
 
+static int bind_parse_severity_output(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	if (!*args[cur_arg + 1]) {
+		memprintf(err, "'%s' : missing severity format", args[cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	if (set_severity_output(&conf->severity_output, args[cur_arg+1]))
+		return 0;
+	else {
+		memprintf(err, "'%s' only supports 'none', 'number', and 'string' (got '%s')",
+				args[cur_arg], args[cur_arg+1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+}
+
 /* Send all the bound sockets, always returns 1 */
 static int _getsocks(char **args, struct appctx *appctx, void *private)
 {
@@ -1231,6 +1318,7 @@ static struct applet cli_applet = {
 static struct cli_kw_list cli_kws = {{ },{
 	{ { "set", "maxconn", "global",  NULL }, "set maxconn global : change the per-process maxconn setting", cli_parse_set_maxconn_global, NULL },
 	{ { "set", "rate-limit", NULL }, "set rate-limit : change a rate limiting value", cli_parse_set_ratelimit, NULL },
+	{ { "set", "severity-output",  NULL }, "set severity-output [none|number|string] : set presence of severity level in feedback information", cli_parse_set_severity_output, NULL, NULL },
 	{ { "set", "timeout",  NULL }, "set timeout    : change a timeout setting", cli_parse_set_timeout, NULL, NULL },
 	{ { "show", "env",  NULL }, "show env [var] : dump environment variables known to the process", cli_parse_show_env, cli_io_handler_show_env, NULL },
 	{ { "show", "cli", "sockets",  NULL }, "show cli sockets : dump list of cli sockets", cli_parse_default, cli_io_handler_show_cli_sock, NULL },
@@ -1246,6 +1334,7 @@ static struct cfg_kw_list cfg_kws = {ILH, {
 static struct bind_kw_list bind_kws = { "STAT", { }, {
 	{ "level",     bind_parse_level,    1 }, /* set the unix socket admin level */
 	{ "expose-fd", bind_parse_expose_fd, 1 }, /* set the unix socket expose fd rights */
+	{ "severity-output", bind_parse_severity_output, 1 }, /* set the severity output format */
 	{ NULL, NULL, 0 },
 }};
 
-- 
2.7.4

Reply via email to