[MEDIUM] introduce the "stats" keyword in global section

Removed old unused MODE_LOG and MODE_STATS, and replaced the "stats"
keyword in the global section. The new "stats" keyword in the global
section is used to create a UNIX socket on which the statistics will
be accessed.  The client must issue a "show stat\n" command in order
to get a CSV-formated output similar to the output on the HTTP socket
in CSV mode.
This commit is contained in:
Willy Tarreau 2007-10-18 13:53:22 +02:00
parent 3e76e728ce
commit fbee71331d
6 changed files with 152 additions and 17 deletions

View File

@ -4,7 +4,7 @@
----------------------
version 1.3.13
willy tarreau
2007/10/15
2007/10/18
This document covers the configuration language as implemented in the version
@ -39,6 +39,7 @@ The following keywords are supported in the "global" section :
- uid
- ulimit-n
- user
- stats
* Performance tuning
- maxconn
@ -52,7 +53,6 @@ The following keywords are supported in the "global" section :
* Debugging
- debug
- quiet
- stats
1.1) Process management and security
@ -111,6 +111,28 @@ pidfile <pidfile>
the "-p" command line argument. The file must be accessible to the user
starting the process. See also "daemon".
stats socket <path> [{uid | user} <uid>] [{gid | group} <gid>] [mode <mode>]
Creates a UNIX socket in stream mode at location <path>. Any previously
existing socket will be backed up then replaced. Connections to this socket
will get a CSV-formated output of the process statistics in response to the
"show stat" command followed by a line feed. On platforms which support it,
it is possible to restrict access to this socket by specifying numerical IDs
after "uid" and "gid", or valid user and group names after the "user" and
"group" keywords. It is also possible to restrict permissions on the socket
by passing an octal value after the "mode" keyword (same syntax as chmod).
Depending on the platform, the permissions on the socket will be inherited
from the directory which hosts it, or from the user the process is started
with.
stats timeout <timeout, in milliseconds>
The default timeout on the stats socket is set to 10 seconds. It is possible
to change this value with "stats timeout". The value must be passed in
milliseconds.
stats maxconn <connections>
By default, the stats socket is limited to 10 concurrent connections. It is
possible to change this value with "stats maxconn".
uid <number>
Changes the process' user ID to <number>. It is recommended that the user ID
is dedicated to HAProxy or to a small set of similar daemons. HAProxy must
@ -186,10 +208,6 @@ quiet
Do not display any message during startup. It is equivalent to the command-
line argument "-q".
stats
Dump internal statistics to stdout at regular interval. It is available for
development purposes only and should never be set.
2) Proxies
----------

View File

@ -29,6 +29,7 @@
#define STAT_FMT_HTML 0x1
int stats_parse_global(const char **args, char *err, int errlen);
int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags);
int stats_dump_http(struct session *s, struct uri_auth *uri, int flags);
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags);

View File

@ -29,15 +29,13 @@
#include <types/task.h>
/* modes of operation (global.mode) */
#define MODE_DEBUG 1
#define MODE_STATS 2
#define MODE_LOG 4
#define MODE_DAEMON 8
#define MODE_QUIET 16
#define MODE_CHECK 32
#define MODE_VERBOSE 64
#define MODE_STARTING 128
#define MODE_FOREGROUND 256
#define MODE_DEBUG 0x01
#define MODE_DAEMON 0x02
#define MODE_QUIET 0x04
#define MODE_CHECK 0x08
#define MODE_VERBOSE 0x10
#define MODE_STARTING 0x20
#define MODE_FOREGROUND 0x40
/* list of last checks to perform, depending on config options */
#define LSTCHK_CAP_BIND 0x00000001 /* check that we can bind to any port */

View File

@ -41,6 +41,7 @@
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/checks.h>
#include <proto/dumpstats.h>
#include <proto/httperr.h>
#include <proto/log.h>
#include <proto/proxy.h>
@ -279,7 +280,11 @@ int cfg_parse_global(const char *file, int linenum, char **args)
global.mode |= MODE_QUIET;
}
else if (!strcmp(args[0], "stats")) {
global.mode |= MODE_STATS;
memcpy(trash, "error near 'stats'", 19);
if (stats_parse_global((const char **)args + 1, trash, sizeof(trash)) < 0) {
Alert("parsing [%s:%d] : %s\n", file, linenum, trash);
return -1;
}
}
else if (!strcmp(args[0], "tune.maxpollevents")) {
if (global.tune.maxpollevents != 0) {

View File

@ -17,6 +17,8 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <sys/socket.h>
#include <sys/stat.h>
@ -42,9 +44,114 @@
#include <proto/buffers.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
#include <proto/proto_uxst.h>
#include <proto/senddata.h>
#include <proto/session.h>
/* This function parses a "stats" statement in the "global" section. It returns
* -1 if there is any error, otherwise zero. If it returns -1, it may write an
* error message into ther <err> buffer, for at most <errlen> bytes, trailing
* zero included. The trailing '\n' must not be written. The function must be
* called with <args> pointing to the first word after "stats".
*/
int stats_parse_global(const char **args, char *err, int errlen)
{
if (!strcmp(args[0], "socket")) {
struct sockaddr_un su;
int cur_arg;
if (*args[1] == 0) {
snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
return -1;
}
if (global.stats_sock.state != LI_NEW) {
snprintf(err, errlen, "'stats socket' already specified in global section");
return -1;
}
su.sun_family = AF_UNIX;
strncpy(su.sun_path, args[1], sizeof(su.sun_path));
su.sun_path[sizeof(su.sun_path) - 1] = 0;
memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit
global.stats_sock.state = LI_INIT;
global.stats_sock.accept = uxst_event_accept;
global.stats_sock.handler = process_uxst_stats;
global.stats_sock.private = NULL;
cur_arg = 2;
while (*args[cur_arg]) {
if (!strcmp(args[cur_arg], "uid")) {
global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "gid")) {
global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "mode")) {
global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "user")) {
struct passwd *user;
user = getpwnam(args[cur_arg + 1]);
if (!user) {
snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
args[cur_arg + 1]);
return -1;
}
global.stats_sock.perm.ux.uid = user->pw_uid;
cur_arg += 2;
}
else if (!strcmp(args[cur_arg], "group")) {
struct group *group;
group = getgrnam(args[cur_arg + 1]);
if (!group) {
snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
args[cur_arg + 1]);
return -1;
}
global.stats_sock.perm.ux.gid = group->gr_gid;
cur_arg += 2;
}
else {
snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', and 'mode'");
return -1;
}
}
uxst_add_listener(&global.stats_sock);
global.maxsock++;
}
else if (!strcmp(args[0], "timeout")) {
int timeout = atol(args[1]);
if (timeout <= 0) {
snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
return -1;
}
__tv_from_ms(&global.stats_timeout, timeout);
}
else if (!strcmp(args[0], "maxconn")) {
int maxconn = atol(args[1]);
if (maxconn <= 0) {
snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
return -1;
}
global.maxsock -= global.stats_sock.maxconn;
global.stats_sock.maxconn = maxconn;
global.maxsock += global.stats_sock.maxconn;
}
else {
snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
return -1;
}
return 0;
}
/*
* Produces statistics data for the session <s>. Expects to be called with
* s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>

View File

@ -116,6 +116,12 @@ struct global global = {
logfac2 : -1,
loglev1 : 7, /* max syslog level : debug */
loglev2 : 7,
.stats_timeout = { .tv_sec = 10, .tv_usec = 0 }, /* stats timeout = 10 seconds */
.stats_sock.timeout = &global.stats_timeout,
.stats_sock.maxconn = 10, /* 10 concurrent stats connections */
.stats_sock.perm.ux.uid = -1,
.stats_sock.perm.ux.gid = -1,
.stats_sock.perm.ux.mode = 0,
/* others NULL OK */
};
@ -542,7 +548,7 @@ void init(int argc, char **argv)
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
}
global.mode |= (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
MODE_VERBOSE | MODE_DEBUG | MODE_STATS | MODE_LOG));
MODE_VERBOSE | MODE_DEBUG ));
if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");