feat(settings): make the HTTP access log toggleable in the UI

Previous commit hard-disabled aiohttp's per-request access log via
setLevel(WARNING), with no way to turn it back on. Added a
'verbose_http_log' setting (default off) — toggle in Settings, persisted
to config.ini, applied on bridge start via _set_verbose_http_log().
This commit is contained in:
2026-07-07 01:20:34 +02:00
parent f2f4447809
commit b541aafc74
8 changed files with 27 additions and 2 deletions

View File

@@ -68,6 +68,7 @@ def _load_config_file(path: pathlib.Path):
"BRIDGE_PRINTER_NAME": (CONFIG_SECTION_BRIDGE, "printer_name"),
"BRIDGE_HOST_IP": (CONFIG_SECTION_BRIDGE, "host_ip"),
"POLL_INTERVAL": (CONFIG_SECTION_BRIDGE, "poll_interval"),
"VERBOSE_HTTP_LOG": (CONFIG_SECTION_BRIDGE, "verbose_http_log"),
"SPOOLMAN_SERVER": (CONFIG_SECTION_SPOOLMAN, "server"),
"SPOOLMAN_SYNC_RATE": (CONFIG_SECTION_SPOOLMAN, "sync_rate"),
}
@@ -450,3 +451,4 @@ SPOOLMAN_SERVER = get("SPOOLMAN_SERVER", "")
SPOOLMAN_SYNC_RATE = int(get("SPOOLMAN_SYNC_RATE", "0"))
BRIDGE_HOST_IP = get("BRIDGE_HOST_IP", "")
POLL_INTERVAL = int(get("POLL_INTERVAL", "3"))
VERBOSE_HTTP_LOG = int(get("VERBOSE_HTTP_LOG", "0"))

View File

@@ -135,10 +135,14 @@ logging.basicConfig(level=logging.INFO,
datefmt="%H:%M:%S")
log = logging.getLogger("bridge")
# aiohttp logs one INFO line per HTTP request (access log) — with 2s frontend
# polling that drowns out the bridge's own logs. Raise its own threshold so
# only WARNING+ (actual problems) surface at the default log level.
# polling that drowns out the bridge's own logs by default. Toggleable at
# runtime via the verbose_http_log setting (see handle_api_settings_post).
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
def _set_verbose_http_log(enabled: bool):
logging.getLogger("aiohttp.access").setLevel(logging.INFO if enabled else logging.WARNING)
# Web UI: subdirectory under web/themes/<name>/index.html
_UI_THEME_NAME_RE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$")
# Allowed static theme files under /kx/ui/<name>
@@ -4420,6 +4424,7 @@ class KobraXBridge:
"web_upload_warning": getattr(self._args, "web_upload_warning", 1),
"print_start_dialog": getattr(self._args, "print_start_dialog", 1),
"poll_interval": getattr(self._args, "poll_interval", 3),
"verbose_http_log": getattr(self._args, "verbose_http_log", 0),
"filament_profiles": {str(k): v for k, v in self._filament_profiles.items()},
"visible_vendors": self._visible_vendors,
"ace_dry_presets": self._ace_dry_presets,
@@ -4464,6 +4469,10 @@ class KobraXBridge:
cfg.set("bridge", "poll_interval", str(pi))
elif not cfg.has_option("bridge", "poll_interval"):
cfg.set("bridge", "poll_interval", "3")
verbose_http_log = int(bool(data.get("verbose_http_log", getattr(self._args, "verbose_http_log", 0))))
cfg.set("bridge", "verbose_http_log", str(verbose_http_log))
_set_verbose_http_log(bool(verbose_http_log))
self._args.verbose_http_log = verbose_http_log
printer_name = str(data.get("printer_name", "")).strip()
if printer_name:
cfg.set("bridge", "printer_name", printer_name)
@@ -5449,6 +5458,7 @@ def _build_per_printer_args(base_args, p: dict):
async def run_bridge(args):
_set_verbose_http_log(bool(getattr(args, "verbose_http_log", 0)))
printers = env_loader.list_printers()
multi_mode = bool(printers)
if not printers:
@@ -5599,6 +5609,8 @@ def main():
help="Mid-print filament sync interval in seconds (0 = only on print end)")
parser.add_argument("--poll-interval", type=int, default=env_loader.POLL_INTERVAL,
help="Printer poll interval in seconds")
parser.add_argument("--verbose-http-log", type=int, default=env_loader.VERBOSE_HTTP_LOG,
help="Log every HTTP request (aiohttp access log)")
parser.add_argument("--host", default="0.0.0.0",
help="Bind address for the bridge server")

View File

@@ -408,6 +408,7 @@ function applyLang(){
setText('lbl-set-lang',T.settings_cat_language||'Sprache');
setText('lbl-set-theme',T.settings_cat_theme||'Hell / Dunkel umschalten');
setText('lbl-poll-interval',T.settings_poll||'Poll-Intervall (Sekunden)');
setText('lbl-verbose-http-log',T.settings_verbose_http_log||'Log every HTTP request (verbose)');
setText('lbl-filament-mapping',T.settings_filament_mapping||'Filament-Profil-Mapping (pro Slot)');
setText('lbl-filament-mapping-save',T.settings_filament_mapping_save||'Mapping speichern');
setText('lbl-visible-vendors',T.settings_visible_vendors||'Sichtbare Hersteller (Profil-Dropdown)');
@@ -1099,6 +1100,7 @@ function openSettings(){
var sec=d.poll_interval||Math.round((parseInt(localStorage.getItem('pollInterval')||'2000'))/1000)||3;
pi.value=sec;
}
var vhl=document.getElementById('s-verbose-http-log');if(vhl)vhl.checked=!!d.verbose_http_log;
renderFilamentMapping(d.filament_profiles||{});
renderSpoolmanSlotCard();
// Spoolman
@@ -1861,6 +1863,7 @@ function saveSettings(){
print_start_dialog: parseInt((document.getElementById('s-file-ready-mode')||{}).value||'1',10),
web_upload_warning:webUploadWarning,
poll_interval: Math.min(60,Math.max(1,parseInt((document.getElementById('s-poll-interval')||{}).value,10)||3)),
verbose_http_log: (document.getElementById('s-verbose-http-log')||{}).checked?1:0,
spoolman_server: (document.getElementById('s-spoolman-url')||{}).value||'',
spoolman_sync_rate: Math.max(0,parseInt((document.getElementById('s-spoolman-sync-rate')||{}).value||'30',10)),
}).then(function(){

View File

@@ -554,6 +554,10 @@
<input type="number" id="s-poll-interval" min="1" max="60" step="1" placeholder="3" oninput="onPollIntervalInput()">
<small style="color:var(--txt2)" id="lbl-poll-hint">Wie oft die Bridge den Drucker-Status abfragt</small>
</div>
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
<input type="checkbox" id="s-verbose-http-log" style="width:auto;margin:0">
<label id="lbl-verbose-http-log" style="margin:0;cursor:pointer" for="s-verbose-http-log">Log every HTTP request (verbose)</label>
</div>
</div>
</div>

View File

@@ -246,6 +246,7 @@
"settings_password": "MQTT-Passwort",
"settings_poll": "Poll-Intervall (Sekunden)",
"settings_poll_interval_hint": "Wie oft die Bridge den Druckerstatus abfragt",
"settings_verbose_http_log": "Jede HTTP-Anfrage loggen (ausführlich)",
"settings_poll_interval_label": "Poll-Intervall (Sekunden)",
"settings_print": "Druckeinstellungen",
"settings_printer_ip": "Drucker-IP",

View File

@@ -246,6 +246,7 @@
"settings_password": "MQTT Password",
"settings_poll": "Poll Interval (seconds)",
"settings_poll_interval_hint": "How often the bridge queries printer status",
"settings_verbose_http_log": "Log every HTTP request (verbose)",
"settings_poll_interval_label": "Poll Interval (seconds)",
"settings_print": "Print Settings",
"settings_printer_ip": "Printer IP",

View File

@@ -246,6 +246,7 @@
"settings_password": "Contraseña MQTT",
"settings_poll": "Intervalo de sondeo (segundos)",
"settings_poll_interval_hint": "Con qué frecuencia el bridge consulta el estado de la impresora",
"settings_verbose_http_log": "Registrar cada solicitud HTTP (detallado)",
"settings_poll_interval_label": "Intervalo de sondeo (segundos)",
"settings_print": "Ajustes de impresión",
"settings_printer_ip": "IP de impresora",

View File

@@ -246,6 +246,7 @@
"settings_password": "MQTT 密码",
"settings_poll": "轮询间隔(秒)",
"settings_poll_interval_hint": "Bridge 查询打印机状态的频率",
"settings_verbose_http_log": "记录每个 HTTP 请求(详细模式)",
"settings_poll_interval_label": "轮询间隔(秒)",
"settings_print": "打印设置",
"settings_printer_ip": "打印机 IP",