aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2026-05-04 22:48:19 +0200
committerPaul Oliver <contact@pauloliver.dev>2026-05-04 22:48:19 +0200
commit6cecf64dbb488949a67eb080bf27d06f51533f40 (patch)
tree8b8871e60416010e5c73eade353c0ebbc86a2a8e
parentaa6d8c2e6f9635d36819cb8325cdc93d325ce3d6 (diff)
Pulls logging functions from C into python code
-rw-r--r--core/logger.c7
-rw-r--r--data/client.c3
-rw-r--r--data/server.c4
-rwxr-xr-xsalis.py67
4 files changed, 47 insertions, 34 deletions
diff --git a/core/logger.c b/core/logger.c
index e088c55..f665908 100644
--- a/core/logger.c
+++ b/core/logger.c
@@ -1,3 +1,10 @@
+#include <assert.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
#define LOG_LINE_SIZE 1024
enum LogLevel {
diff --git a/data/client.c b/data/client.c
index 02c7687..31e18b8 100644
--- a/data/client.c
+++ b/data/client.c
@@ -1,9 +1,6 @@
-#include <assert.h>
#include <curses.h>
#include <locale.h>
#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
#include "logger.c"
#include "tui.c"
diff --git a/data/server.c b/data/server.c
index 3ed2c64..7561a1a 100644
--- a/data/server.c
+++ b/data/server.c
@@ -1,11 +1,7 @@
#include <arpa/inet.h>
-#include <assert.h>
#include <json-c/json.h>
#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
#include <threads.h>
-#include <unistd.h>
#include "logger.c"
diff --git a/salis.py b/salis.py
index 64584a5..2800d73 100755
--- a/salis.py
+++ b/salis.py
@@ -1,6 +1,6 @@
#!/usr/bin/env -S PYTHONDONTWRITEBYTECODE=1 python
-import colorlog
+import ctypes
import enum
import json
import os
@@ -18,11 +18,10 @@ from types import SimpleNamespace
# ------------------------------------------------------------------------------
# Argument parser
# ------------------------------------------------------------------------------
-description = "Salis: Simple A-Life Simulator"
prog = sys.argv[0]
epilog = f"Use '-h' to show command arguments; e.g. '{prog} new -h'"
-parser = ArgumentParser(description=description, epilog=epilog, formatter_class=RawTextHelpFormatter, prog=prog)
+parser = ArgumentParser(description="Salis: Simple A-Life Simulator", epilog=epilog, formatter_class=RawTextHelpFormatter, prog=prog)
sub_parsers = parser.add_subparsers(dest="command", required=True)
formatter_class = lambda prog: ArgumentDefaultsHelpFormatter(max_help_position=32, prog=prog)
@@ -91,22 +90,15 @@ for (name, sub_parsers, _), kwargs in options.items():
args = parser.parse_args()
# ------------------------------------------------------------------------------
-# Logging
-# ------------------------------------------------------------------------------
-handler = colorlog.StreamHandler()
-handler.setFormatter(colorlog.ColoredFormatter("%(log_color)s%(asctime)s %(process)07d [%(levelname).4s] %(reset)s%(message)s"))
-
-log = colorlog.getLogger()
-log.setLevel(colorlog.INFO)
-log.addHandler(handler)
-
-# ------------------------------------------------------------------------------
# Build class
# ------------------------------------------------------------------------------
class Build:
- def __init__(self, path, library=False):
+ def __init__(self, path, log, library=False):
+ self.log = log
+ self.library = library
+
self.tempdir = TemporaryDirectory(prefix="salis_", delete=not args.keep_temp_dir)
- log.info(f"Generated temporary directory for C builds at: {self.tempdir.name}")
+ self.log.info(f"Generated temporary directory for C builds at: {self.tempdir.name}")
self.name = os.path.splitext(os.path.basename(path))[0]
self.binfile = os.path.join(self.tempdir.name, f"{self.name}{".so" if library else ""}")
@@ -117,8 +109,8 @@ class Build:
self.links = set()
self.build_cmd = [args.compiler, f"@{self.argsfile}", path, "-o", self.binfile]
- log.info(f"Build class initialized for {"library" if library else "executable"}: {path}")
- log.info(f"Compiler flags stored at: {self.argsfile}")
+ self.log.info(f"Build class initialized for {"library" if library else "executable"}: {path}")
+ self.log.info(f"Compiler flags stored at: {self.argsfile}")
def build(self):
fmt_nl = lambda line: f"{line}\n"
@@ -129,14 +121,16 @@ class Build:
f.writelines(map(fmt_define, sorted(self.defines)))
f.writelines(map(fmt_nl, sorted(self.links)))
- log.info(f"Running build command: '{self.build_cmd}'")
+ self.log.info(f"Running build command: '{self.build_cmd}'")
subprocess.run(self.build_cmd, check=True)
def exec(self):
+ assert not self.library
+
run_cmd = args.pre_cmd.split() if args.pre_cmd else []
run_cmd.append(self.binfile)
- log.info(f"Running binary with command: '{" ".join(run_cmd)}'")
+ self.log.info(f"Running binary with command: '{" ".join(run_cmd)}'")
proc = subprocess.Popen(run_cmd, stdout=sys.stdout, stderr=sys.stderr)
# When using signals (e.g. SIGTERM), they must be sent to the entire process group
@@ -152,6 +146,26 @@ class Build:
if code != 0: raise RuntimeError(f"Binary returned code: {code}")
# ------------------------------------------------------------------------------
+# Logging
+# ------------------------------------------------------------------------------
+log_logs = []
+log_ns = SimpleNamespace()
+log_ns.info = lambda msg: log_logs.append(("info", msg))
+log_ns.warn = lambda msg: log_logs.append(("warn", msg))
+log_b = Build("core/logger.c", log_ns, library=True)
+log_b.build()
+
+# Pull in logging functions from C
+# This way there's only a single unified logging system to care about
+log = SimpleNamespace()
+log_dll = ctypes.CDLL(log_b.binfile)
+log.info = lambda msg: log_dll.log_info(msg.encode())
+log.warn = lambda msg: log_dll.log_warn(msg.encode())
+
+for log_level, log_msg in log_logs:
+ getattr(log, log_level)(log_msg)
+
+# ------------------------------------------------------------------------------
# Options dict formatter
# ------------------------------------------------------------------------------
fmt_h2u = lambda field: field.replace("-", "_")
@@ -175,7 +189,6 @@ def fmt_opts(opts):
# ------------------------------------------------------------------------------
# Source configuration
# ------------------------------------------------------------------------------
-log.info(description)
log.info(f"Called '{prog} {args.command}' with the following options: {fmt_opts(vars(args))}")
ns = SimpleNamespace()
@@ -215,7 +228,7 @@ if args.command == "new":
raise RuntimeError("Data push power must be equal or greater than core sync power")
if os.path.isdir(ns.sim_dir) and args.force:
- log.warning(f"Force flag used! Wiping old simulation at: {ns.sim_dir}")
+ log.warn(f"Force flag used! Wiping old simulation at: {ns.sim_dir}")
shutil.rmtree(ns.sim_dir)
if os.path.isdir(ns.sim_dir):
@@ -331,14 +344,14 @@ def pop_data_push_vars():
ns.b.links.add("-lz")
log.info(f"Data will be aggregated at: {ns.sim_db}")
else:
- log.warning("Data aggregation disabled")
+ log.warn("Data aggregation disabled")
if not args.no_compress:
ns.b.defines.add("-DCOMPRESS")
ns.b.links.add("-lz")
log.info("Save file compression enabled")
else:
- log.warning("Save file compression disabled")
+ log.warn("Save file compression disabled")
def pop_sim_path_vars():
ns.b.defines.add(f"-DSIM_OPTS=\"{ns.sim_opts}\"")
@@ -385,7 +398,7 @@ def pop_general():
# Populate for new
if args.command == "new":
- ns.b = Build("core/salis.c")
+ ns.b = Build("core/salis.c", log)
pop_ui_vars()
pop_data_push_vars()
pop_sim_path_vars()
@@ -396,7 +409,7 @@ if args.command == "new":
# Populate for load
if args.command == "load":
- ns.b = Build("core/salis.c")
+ ns.b = Build("core/salis.c", log)
pop_ui_vars()
pop_data_push_vars()
pop_sim_path_vars()
@@ -407,14 +420,14 @@ if args.command == "load":
# Populate for server
if args.command == "server":
- ns.b = Build("data/server.c")
+ ns.b = Build("data/server.c", log)
pop_sim_path_vars()
pop_net_vars()
pop_general()
# Populate for client
if args.command == "client":
- ns.b = Build("data/client.c")
+ ns.b = Build("data/client.c", log)
pop_net_vars()
pop_general()
ns.b.defines.add(f"-DIP={args.ip}")