kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

log.c (3553B)



      1 /*
      2  * Copyright (c) 2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 
     19 #include <time.h>
     20 #include <syslog.h>
     21 
     22 #include "kore.h"
     23 
     24 struct kore_wlog {
     25 	int		prio;
     26 	u_int16_t	wid;
     27 	size_t		loglen;
     28 	char		logmsg[];
     29 };
     30 
     31 static void	log_print(int, const char *, ...)
     32 		    __attribute__((format (printf, 2, 3)));
     33 static void	log_from_worker(struct kore_msg *, const void *);
     34 
     35 static FILE	*fp = NULL;
     36 
     37 void
     38 kore_log_init(void)
     39 {
     40 #if defined(KORE_SINGLE_BINARY)
     41 	extern const char	*__progname;
     42 	const char		*name = kore_strdup(__progname);
     43 #else
     44 	const char		*name = "kore";
     45 #endif
     46 
     47 	fp = stdout;
     48 
     49 	if (!kore_foreground)
     50 		openlog(name, LOG_NDELAY | LOG_PID, LOG_DAEMON);
     51 
     52 	kore_msg_register(KORE_MSG_WORKER_LOG, log_from_worker);
     53 }
     54 
     55 void
     56 kore_log_file(const char *path)
     57 {
     58 	if ((fp = fopen(path, "a")) == NULL) {
     59 		fp = stdout;
     60 		fatal("fopen(%s): %s", path, errno_s);
     61 	}
     62 }
     63 
     64 void
     65 kore_log(int prio, const char *fmt, ...)
     66 {
     67 	va_list			args;
     68 	const char		*str;
     69 	struct kore_wlog	wlog;
     70 	struct kore_buf		buf, pkt;
     71 
     72 	kore_buf_init(&buf, 128);
     73 
     74 	va_start(args, fmt);
     75 	kore_buf_appendv(&buf, fmt, args);
     76 	va_end(args);
     77 
     78 	if (worker != NULL) {
     79 		kore_buf_init(&pkt, sizeof(wlog) + buf.offset);
     80 
     81 		memset(&wlog, 0, sizeof(wlog));
     82 
     83 		wlog.prio = prio;
     84 		wlog.wid = worker->id;
     85 		wlog.loglen = buf.offset;
     86 
     87 		kore_buf_append(&pkt, &wlog, sizeof(wlog));
     88 		kore_buf_append(&pkt, buf.data, buf.offset);
     89 
     90 		kore_msg_send(KORE_MSG_PARENT, KORE_MSG_WORKER_LOG,
     91 		    pkt.data, pkt.offset);
     92 
     93 		kore_buf_cleanup(&pkt);
     94 	} else {
     95 		str = kore_buf_stringify(&buf, NULL);
     96 
     97 		if (kore_foreground || fp != stdout)
     98 			log_print(prio, "proc=[parent] log=[%s]\n", str);
     99 		else
    100 			syslog(prio, "proc=[parent] log=[%s]", str);
    101 	}
    102 
    103 	kore_buf_cleanup(&buf);
    104 }
    105 
    106 static void
    107 log_from_worker(struct kore_msg *msg, const void *data)
    108 {
    109 	const char		*name;
    110 	const struct kore_wlog	*wlog;
    111 
    112 	if (msg->length < sizeof(*wlog)) {
    113 		kore_log(LOG_NOTICE,
    114 		    "too short worker log received (%zu < %zu)",
    115 		    msg->length, sizeof(*wlog));
    116 		return;
    117 	}
    118 
    119 	wlog = data;
    120 	name = kore_worker_name(wlog->wid);
    121 
    122 	if (kore_foreground || fp != stdout) {
    123 		log_print(wlog->prio, "proc=%s log=[%.*s]\n",
    124 		    name, (int)wlog->loglen, wlog->logmsg);
    125 	} else {
    126 		syslog(wlog->prio, "proc=%s log=[%.*s]",
    127 		    name, (int)wlog->loglen, wlog->logmsg);
    128 	}
    129 }
    130 
    131 static void
    132 log_print(int prio, const char *fmt, ...)
    133 {
    134 	struct tm		*t;
    135 	struct timespec		ts;
    136 	va_list			args;
    137 	char			tbuf[32];
    138 
    139 	va_start(args, fmt);
    140 
    141 	switch (prio) {
    142 	case LOG_ERR:
    143 	case LOG_WARNING:
    144 	case LOG_NOTICE:
    145 	case LOG_INFO:
    146 	case LOG_DEBUG:
    147 		break;
    148 	}
    149 
    150 	(void)clock_gettime(CLOCK_REALTIME, &ts);
    151 	t = gmtime(&ts.tv_sec);
    152 
    153 	if (strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", t) > 0)
    154 		fprintf(fp, "%s.%03ld UTC ", tbuf, ts.tv_nsec / 1000000);
    155 
    156 	vfprintf(fp, fmt, args);
    157 	fflush(fp);
    158 
    159 	va_end(args);
    160 }