aboutsummaryrefslogtreecommitdiffstats
path: root/util/log.c
diff options
context:
space:
mode:
authorturret <turret@duck.com>2024-03-30 16:04:45 -0500
committerturret <turret@duck.com>2024-03-30 16:04:45 -0500
commit80a67b7d20393a29aa5d2cb92197f3381be7fd96 (patch)
treef0c313da5ef509e79e5067972edd91976513ce0e /util/log.c
parentf01745a2ee84f11b8cc54e37c5f7f596184ab785 (diff)
downloaddiscord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.tar.gz
discord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.tar.bz2
discord-bot-skeleton-80a67b7d20393a29aa5d2cb92197f3381be7fd96.zip
*: directory changes
since this project is a skeleton and not meant to clutter up the code that will actually consume the bot, i've opted to consolidate the majority of files under a single directory and minimise extra files *: move code to util/ *: move include files to include/dbs/ net: consolidate net functions into single file config: remove config
Diffstat (limited to 'util/log.c')
-rw-r--r--util/log.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/util/log.c b/util/log.c
new file mode 100644
index 0000000..afbc2e3
--- /dev/null
+++ b/util/log.c
@@ -0,0 +1,210 @@
+#include <execinfo.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <dbs/log.h>
+#include <dbs/util.h>
+
+extern int subsystem_change_mode(int pid, char mode);
+extern char *subsystem_get_name(int pid);
+extern int mainpid;
+
+static const char *colors[] = {
+ [EMERG_LOGLEVEL] = ANSI_BLINK ANSI_REVERSE ANSI_BOLD ANSI_RED,
+ [ALERT_LOGLEVEL] = ANSI_REVERSE ANSI_BOLD ANSI_RED,
+ [CRIT_LOGLEVEL] = ANSI_BOLD ANSI_RED,
+ [ERR_LOGLEVEL] = ANSI_RED,
+ [WARNING_LOGLEVEL] = ANSI_BOLD,
+ [NOTICE_LOGLEVEL] = ANSI_BRIGHT_WHITE,
+ [INFO_LOGLEVEL] = ANSI_RESET,
+ [DEBUG_LOGLEVEL] = ANSI_ITALIC ANSI_BRIGHT_BLUE,
+};
+
+static const char *mode_to_string[] = {
+ [PANICMODE_DEBUGONLY] = "subsystem OOPS",
+ [PANICMODE_RESPAWN] = "subsystem failure",
+ [PANICMODE_DIE] = "catastrophic failure",
+};
+
+static int console_lock = 0;
+
+#define MAX_TRY_COUNT 1 << 17
+static void obtain_console_lock(void)
+{
+ int try_count = 0;
+ register int rax asm("rax");
+retry:
+ while(console_lock && try_count <= MAX_TRY_COUNT)
+ try_count += 1;
+
+ asm("mov %0, 1 \n"
+ "xchg %0, %1" : "=r" (rax), "=m" (console_lock));
+
+ if(rax > 0 && try_count <= MAX_TRY_COUNT)
+ goto retry;
+
+ if(try_count > MAX_TRY_COUNT) {
+ print(LOG_SOH "\3" "4" "log: broken console lock");
+ }
+
+ return;
+}
+
+static int vaprint(const char *fmt, va_list ap)
+{
+ int loglevel = DEFAULT_LOGLEVEL;
+ int dolocks = 1;
+ int parsecolon = 1;
+ if(fmt[0] == LOG_SOH_ASCII) {
+ loglevel = (fmt[2] - 0x30) % 10;
+ char flags = fmt[1];
+ if(flags & 1 << 1)
+ dolocks = 0;
+ if(flags & 1 << 2)
+ parsecolon = 0;
+ fmt += 3;
+ }
+
+ /* not going to be printed? dont bother! */
+ if(loglevel > CONSOLE_LOGLEVEL)
+ return 0;
+
+ /* we essentially print the user's raw input to its own buffer,
+ later we will parse it and print out ANSI colors and what not */
+ char buf[512];
+
+ vsnprintf(buf, 512, fmt, ap);
+ buf[512 - 1] = '\0';
+
+ size_t colon = 0;
+ if(parsecolon) {
+ for(; colon < strlen(buf); ++colon) {
+ if(buf[colon] == ':')
+ break;
+ }
+ }
+
+ char tsbuf[64] = "\0";
+ struct timeval time;
+ gettimeofday(&time, NULL);
+ snprintf(tsbuf, sizeof(tsbuf), "[%5ld.%06ld] ",
+ (long)time.tv_sec % 100000, (long)time.tv_usec);
+
+ /* spin lock, at the cost of architecture portability
+ concurrency is something that we need to adjust for, and the
+ console will be scrambled and unreadable if we allow writing all
+ at the same time. I considered simply writing all at once, but
+ ended up just not caring enough to the point where spinlocks
+ prevail. */
+ if(dolocks)
+ obtain_console_lock();
+
+
+ /* we want to support stuff without colons, but frankly I havent
+ tested this at time of writing. will find out later */
+ writeputs(ANSI_RESET ANSI_GREEN);
+ writeputs(tsbuf);
+ writeputs(ANSI_RESET);
+ if(parsecolon && buf[colon] == ':') {
+ writeputs(colors[loglevel]);
+ writeputs(ANSI_YELLOW);
+ write(STDOUT_FILENO, buf, colon);
+ writeputs(ANSI_RESET);
+ }
+ writeputs(colors[loglevel]);
+ if(colon && *(buf + colon)) {
+ writeputs(buf + colon);
+ } else {
+ writeputs(buf);
+ }
+ writeputs(ANSI_RESET);
+ write(STDOUT_FILENO, "\n", 1);
+ if(dolocks)
+ console_lock = 0;
+ return 0;
+}
+
+void _panic(const char *fileorigin,
+ const int lineorigin,
+ const char *fmt, ...)
+{
+ char mode = PANICMODE_DIE;
+ int pid = getpid();
+ if(fmt[0] == LOG_SOH_ASCII) {
+ mode = fmt[1];
+ /* cannot respawn main thread */
+ if(pid == mainpid && mode == PANICMODE_RESPAWN)
+ mode = PANICMODE_DIE;
+ fmt += 2;
+ }
+
+#define NOLOCK(loglevel) LOG_SOH "\3" loglevel
+ va_list ap;
+ va_start(ap, fmt);
+ char *_fmt = malloc(strlen(fmt) + 4 * sizeof(char));
+ sprintf(_fmt, NOLOCK("1") "%s", fmt);
+
+ void **backtrace_addresses = malloc(sizeof(void*) * 32);
+ int backtrace_count = backtrace(backtrace_addresses, 32);
+ char **backtrace_symbolnames =
+ backtrace_symbols(backtrace_addresses, backtrace_count);
+
+ obtain_console_lock();
+
+ print(NOLOCK("5") "------------[ cut here ]------------");
+ print(LOG_SOH "\7""0" "%s at %s:%d", mode_to_string[(int)mode],
+ fileorigin, lineorigin);
+ vaprint(_fmt, ap);
+ print(LOG_SOH "\7""7" "Call Trace:");
+ for(int i = 0; i < backtrace_count; ++i) {
+ print(NOLOCK("7") " [0x%016x] %s", backtrace_addresses[i],
+ backtrace_symbolnames[i]);
+ }
+ if(mainpid == pid){
+ print(NOLOCK("7") " <start of main thread>");
+ } else {
+ print(NOLOCK("7") " <start of %s[%d]>",
+ subsystem_get_name(pid), pid);
+ }
+
+ /* if we are going to die, we dont really need to clean up */
+ if(mode == PANICMODE_DIE) {
+ kill(0, SIGTERM);
+ raise(SIGTERM);
+ exit(0);
+ }
+
+ print(NOLOCK("5") "------------[ cut here ]------------");
+
+ console_lock = 0;
+ free(_fmt);
+ free(backtrace_symbolnames);
+ free(backtrace_addresses);
+ va_end(ap);
+
+ if(mode == PANICMODE_DEBUGONLY)
+ return;
+
+ if(pid != mainpid && mode == PANICMODE_RESPAWN) {
+ /* we want to let the main process handle the rest */
+ subsystem_change_mode(pid, mode);
+ syscall(SYS_exit_group, 0);
+ }
+}
+
+int print(const char *fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = vaprint(fmt, ap);
+ va_end(ap);
+ return ret;
+}