mirror of https://github.com/yandex/odyssey.git
394 lines
8.8 KiB
C
394 lines
8.8 KiB
C
|
|
/*
|
|
* Odyssey.
|
|
*
|
|
* Scalable PostgreSQL connection pooler.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <assert.h>
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <syslog.h>
|
|
|
|
#include <machinarium.h>
|
|
#include <kiwi.h>
|
|
#include <odyssey.h>
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
int id;
|
|
} od_log_syslog_facility_t;
|
|
|
|
static od_log_syslog_facility_t
|
|
od_log_syslog_facilities[] =
|
|
{
|
|
{ "daemon", LOG_DAEMON },
|
|
{ "user", LOG_USER },
|
|
{ "local0", LOG_LOCAL0 },
|
|
{ "local1", LOG_LOCAL1 },
|
|
{ "local2", LOG_LOCAL2 },
|
|
{ "local3", LOG_LOCAL3 },
|
|
{ "local4", LOG_LOCAL4 },
|
|
{ "local5", LOG_LOCAL5 },
|
|
{ "local6", LOG_LOCAL6 },
|
|
{ "local7", LOG_LOCAL7 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
static int
|
|
od_log_syslog_level[] =
|
|
{
|
|
LOG_INFO, LOG_ERR, LOG_DEBUG, LOG_CRIT
|
|
};
|
|
|
|
static char*
|
|
od_log_level[] =
|
|
{
|
|
"info", "error", "debug", "fatal"
|
|
};
|
|
|
|
void
|
|
od_logger_init(od_logger_t *logger, od_pid_t *pid)
|
|
{
|
|
logger->pid = pid;
|
|
logger->log_debug = 0;
|
|
logger->log_stdout = 1;
|
|
logger->log_syslog = 0;
|
|
logger->format = NULL;
|
|
logger->format_len = 0;
|
|
logger->fd = -1;
|
|
/* set temporary format */
|
|
od_logger_set_format(logger, "%p %t %l (%c) %h %m\n");
|
|
}
|
|
|
|
int
|
|
od_logger_open(od_logger_t *logger, char *path)
|
|
{
|
|
logger->fd = open(path, O_RDWR|O_CREAT|O_APPEND, 0644);
|
|
if (logger->fd == -1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
od_logger_open_syslog(od_logger_t *logger, char *ident, char *facility)
|
|
{
|
|
int facility_id = LOG_DAEMON;
|
|
if (facility) {
|
|
int i = 0;
|
|
od_log_syslog_facility_t *facility_ptr;
|
|
for (;;) {
|
|
facility_ptr = &od_log_syslog_facilities[i];
|
|
if (facility_ptr->name == NULL)
|
|
break;
|
|
if (strcasecmp(facility_ptr->name, facility) == 0) {
|
|
facility_id = facility_ptr->id;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
logger->log_syslog = 1;
|
|
if (ident == NULL)
|
|
ident = "odyssey";
|
|
openlog(ident, 0, facility_id);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
od_logger_close(od_logger_t *logger)
|
|
{
|
|
if (logger->fd != -1)
|
|
close(logger->fd);
|
|
logger->fd = -1;
|
|
}
|
|
|
|
static char od_logger_escape_tab[256] =
|
|
{
|
|
['\0'] = '0',
|
|
['\t'] = 't',
|
|
['\n'] = 'n',
|
|
['\r'] = 'r',
|
|
['\\'] = '\\',
|
|
['='] = '='
|
|
};
|
|
|
|
__attribute__((hot)) static inline int
|
|
od_logger_escape(char *dest, int size, char *fmt, va_list args)
|
|
{
|
|
char prefmt[512];
|
|
int prefmt_len;
|
|
prefmt_len = od_vsnprintf(prefmt, sizeof(prefmt), fmt, args);
|
|
|
|
char *dst_pos = dest;
|
|
char *dst_end = dest + size;
|
|
char *msg_pos = prefmt;
|
|
char *msg_end = prefmt + prefmt_len;
|
|
|
|
while (msg_pos < msg_end) {
|
|
char escaped_char;
|
|
escaped_char = od_logger_escape_tab[(int)*msg_pos];
|
|
if (od_unlikely(escaped_char)) {
|
|
if (od_unlikely((dst_end - dst_pos) < 2))
|
|
break;
|
|
dst_pos[0] = '\\';
|
|
dst_pos[1] = escaped_char;
|
|
dst_pos += 2;
|
|
} else {
|
|
if (od_unlikely((dst_end - dst_pos) < 1))
|
|
break;
|
|
dst_pos[0] = *msg_pos;
|
|
dst_pos += 1;
|
|
}
|
|
msg_pos++;
|
|
}
|
|
return dst_pos - dest;
|
|
}
|
|
|
|
__attribute__((hot)) static inline int
|
|
od_logger_format(od_logger_t *logger, od_logger_level_t level,
|
|
char *context,
|
|
od_client_t *client,
|
|
od_server_t *server,
|
|
char *fmt, va_list args,
|
|
char *output, int output_len)
|
|
{
|
|
char *dst_pos = output;
|
|
char *dst_end = output + output_len;
|
|
char *format_pos = logger->format;
|
|
char *format_end = logger->format + logger->format_len;
|
|
char peer[128];
|
|
|
|
int len;
|
|
while (format_pos < format_end)
|
|
{
|
|
if (*format_pos == '\\') {
|
|
format_pos++;
|
|
if (od_unlikely(format_pos == format_end))
|
|
break;
|
|
if (od_unlikely((dst_end - dst_pos) < 1))
|
|
break;
|
|
switch (*format_pos) {
|
|
case '\\':
|
|
dst_pos[0] = '\\';
|
|
dst_pos += 1;
|
|
break;
|
|
case 'n':
|
|
dst_pos[0] = '\n';
|
|
dst_pos += 1;
|
|
break;
|
|
case 't':
|
|
dst_pos[0] = '\t';
|
|
dst_pos += 1;
|
|
break;
|
|
case 'r':
|
|
dst_pos[0] = '\r';
|
|
dst_pos += 1;
|
|
break;
|
|
default:
|
|
if (od_unlikely((dst_end - dst_pos) < 2))
|
|
break;
|
|
dst_pos[0] = '\\';
|
|
dst_pos[1] = *format_pos;
|
|
dst_pos += 2;
|
|
break;
|
|
}
|
|
} else
|
|
if (*format_pos == '%') {
|
|
format_pos++;
|
|
if (od_unlikely(format_pos == format_end))
|
|
break;
|
|
switch (*format_pos) {
|
|
/* unixtime */
|
|
case 'n':
|
|
{
|
|
time_t tm = time(NULL);
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%lu", tm);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
/* timestamp */
|
|
case 't':
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
len = strftime(dst_pos, dst_end - dst_pos, "%d %b %H:%M:%S.",
|
|
localtime(&tv.tv_sec));
|
|
dst_pos += len;
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%03d",
|
|
(signed)tv.tv_usec / 1000);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
/* pid */
|
|
case 'p':
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s", logger->pid->pid_sz);
|
|
dst_pos += len;
|
|
break;
|
|
/* client id */
|
|
case 'i':
|
|
if (client && client->id.id_prefix != NULL) {
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s%.*s",
|
|
client->id.id_prefix,
|
|
(signed)sizeof(client->id.id), client->id.id);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
/* server id */
|
|
case 's':
|
|
if (server && server->id.id_prefix != NULL) {
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s%.*s",
|
|
server->id.id_prefix,
|
|
(signed)sizeof(server->id.id), server->id.id);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
/* user name */
|
|
case 'u':
|
|
if (client && client->startup.user.value_len) {
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, client->startup.user.value);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
/* database name */
|
|
case 'd':
|
|
if (client && client->startup.database.value_len) {
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, client->startup.database.value);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
/* context */
|
|
case 'c':
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s", context);
|
|
dst_pos += len;
|
|
break;
|
|
/* level */
|
|
case 'l':
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s", od_log_level[level]);
|
|
dst_pos += len;
|
|
break;
|
|
/* message */
|
|
case 'm':
|
|
len = od_vsnprintf(dst_pos, dst_end - dst_pos, fmt, args);
|
|
dst_pos += len;
|
|
break;
|
|
/* message (escaped) */
|
|
case 'M':
|
|
len = od_logger_escape(dst_pos, dst_end - dst_pos, fmt, args);
|
|
dst_pos += len;
|
|
break;
|
|
/* client host */
|
|
case 'h':
|
|
if (client && client->io.io) {
|
|
od_getpeername(client->io.io, peer, sizeof(peer), 1, 0);
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s", peer);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
/* client port */
|
|
case 'r':
|
|
if (client && client->io.io) {
|
|
od_getpeername(client->io.io, peer, sizeof(peer), 0, 1);
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "%s", peer);
|
|
dst_pos += len;
|
|
break;
|
|
}
|
|
len = od_snprintf(dst_pos, dst_end - dst_pos, "none");
|
|
dst_pos += len;
|
|
break;
|
|
case '%':
|
|
if (od_unlikely((dst_end - dst_pos) < 1))
|
|
break;
|
|
dst_pos[0] = '%';
|
|
dst_pos += 1;
|
|
break;
|
|
default:
|
|
if (od_unlikely((dst_end - dst_pos) < 2))
|
|
break;
|
|
dst_pos[0] = '%';
|
|
dst_pos[1] = *format_pos;
|
|
dst_pos += 2;
|
|
break;
|
|
}
|
|
} else {
|
|
if (od_unlikely((dst_end - dst_pos) < 1))
|
|
break;
|
|
dst_pos[0] = *format_pos;
|
|
dst_pos += 1;
|
|
}
|
|
format_pos++;
|
|
}
|
|
return dst_pos - output;
|
|
}
|
|
|
|
void
|
|
od_logger_write(od_logger_t *logger, od_logger_level_t level,
|
|
char *context,
|
|
void *client, void *server,
|
|
char *fmt, va_list args)
|
|
{
|
|
if (logger->fd == -1 && !logger->log_stdout && !logger->log_syslog)
|
|
return;
|
|
|
|
if (level == OD_DEBUG) {
|
|
int is_debug = logger->log_debug;
|
|
if (! is_debug) {
|
|
od_client_t *client_ref = client;
|
|
od_server_t *server_ref = server;
|
|
if (client_ref && client_ref->rule) {
|
|
is_debug = client_ref->rule->log_debug;
|
|
} else
|
|
if (server_ref && server_ref->route) {
|
|
od_route_t *route = server_ref->route;
|
|
is_debug = route->rule->log_debug;
|
|
}
|
|
}
|
|
if (! is_debug)
|
|
return;
|
|
}
|
|
|
|
char output[1024];
|
|
int len;
|
|
len = od_logger_format(logger, level, context, client, server,
|
|
fmt, args, output, sizeof(output));
|
|
int rc;
|
|
if (logger->fd != -1) {
|
|
rc = write(logger->fd, output, len);
|
|
}
|
|
if (logger->log_stdout) {
|
|
rc = write(STDOUT_FILENO, output, len);
|
|
}
|
|
if (logger->log_syslog) {
|
|
syslog(od_log_syslog_level[level], "%.*s", len, output);
|
|
}
|
|
(void)rc;
|
|
}
|