detached 8582952

feat(logging): add config option to capture GTK/WebKit logs

Closes #124
commit
858295236ce7970634fcf3df12d3c3c02e93c888
author
brice · · 2026-01-23 04:48 UTC
parent
7d32950
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
5 files · +82 -0
M internal/bootstrap/webkit_stack.go +7 -0
@@ -11,6 +11,7 @@ import (
1111 "github.com/bnema/dumber/internal/infrastructure/env"
1212 "github.com/bnema/dumber/internal/infrastructure/filtering"
1313 "github.com/bnema/dumber/internal/infrastructure/webkit"
14+ "github.com/bnema/dumber/internal/logging"
1415 "github.com/bnema/dumber/internal/ui/theme"
1516 "github.com/rs/zerolog"
1617 )
@@ -222,4 +223,10 @@ func configureRenderingEnvironment(
222223 Str("gpu", string(gpuVendor)).
223224 Interface("vars", renderEnv.GetAppliedVars()).
224225 Msg("rendering environment configured")
226+
227+ // Install GLib log handler if configured (captures GTK4/WebKitGTK6 messages)
228+ if cfg.Logging.CaptureGTKLogs {
229+ enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace"
230+ logging.InstallGLibLogHandler(ctx, logger, enableDebug)
231+ }
225232 }
@@ -11,6 +11,7 @@ import (
11 "github.com/bnema/dumber/internal/infrastructure/env"11 "github.com/bnema/dumber/internal/infrastructure/env"
12 "github.com/bnema/dumber/internal/infrastructure/filtering"12 "github.com/bnema/dumber/internal/infrastructure/filtering"
13 "github.com/bnema/dumber/internal/infrastructure/webkit"13 "github.com/bnema/dumber/internal/infrastructure/webkit"
14+ "github.com/bnema/dumber/internal/logging"
14 "github.com/bnema/dumber/internal/ui/theme"15 "github.com/bnema/dumber/internal/ui/theme"
15 "github.com/rs/zerolog"16 "github.com/rs/zerolog"
16 )17 )
@@ -222,4 +223,10 @@ func configureRenderingEnvironment(
222 Str("gpu", string(gpuVendor)).223 Str("gpu", string(gpuVendor)).
223 Interface("vars", renderEnv.GetAppliedVars()).224 Interface("vars", renderEnv.GetAppliedVars()).
224 Msg("rendering environment configured")225 Msg("rendering environment configured")
226+
227+ // Install GLib log handler if configured (captures GTK4/WebKitGTK6 messages)
228+ if cfg.Logging.CaptureGTKLogs {
229+ enableDebug := cfg.Logging.Level == "debug" || cfg.Logging.Level == "trace"
230+ logging.InstallGLibLogHandler(ctx, logger, enableDebug)
231+ }
225 }232 }
M internal/infrastructure/config/defaults.go +1 -0
@@ -110,6 +110,7 @@ func DefaultConfig() *Config {
110110 LogDir: getDefaultLogDir(),
111111 EnableFileLog: true,
112112 CaptureConsole: false, // Disabled by default
113+ CaptureGTKLogs: false, // Disabled by default
113114 },
114115 Appearance: AppearanceConfig{
115116 SansFont: "Fira Sans",
@@ -110,6 +110,7 @@ func DefaultConfig() *Config {
110 LogDir: getDefaultLogDir(),110 LogDir: getDefaultLogDir(),
111 EnableFileLog: true,111 EnableFileLog: true,
112 CaptureConsole: false, // Disabled by default112 CaptureConsole: false, // Disabled by default
113+ CaptureGTKLogs: false, // Disabled by default
113 },114 },
114 Appearance: AppearanceConfig{115 Appearance: AppearanceConfig{
115 SansFont: "Fira Sans",116 SansFont: "Fira Sans",
M internal/infrastructure/config/loader.go +1 -0
@@ -397,6 +397,7 @@ func (m *Manager) setLoggingDefaults(defaults *Config) {
397397 m.viper.SetDefault("logging.log_dir", defaults.Logging.LogDir)
398398 m.viper.SetDefault("logging.enable_file_log", defaults.Logging.EnableFileLog)
399399 m.viper.SetDefault("logging.capture_console", defaults.Logging.CaptureConsole)
400+ m.viper.SetDefault("logging.capture_gtk_logs", defaults.Logging.CaptureGTKLogs)
400401 }
401402
402403 func (m *Manager) setDebugDefaults(defaults *Config) {
@@ -397,6 +397,7 @@ func (m *Manager) setLoggingDefaults(defaults *Config) {
397 m.viper.SetDefault("logging.log_dir", defaults.Logging.LogDir)397 m.viper.SetDefault("logging.log_dir", defaults.Logging.LogDir)
398 m.viper.SetDefault("logging.enable_file_log", defaults.Logging.EnableFileLog)398 m.viper.SetDefault("logging.enable_file_log", defaults.Logging.EnableFileLog)
399 m.viper.SetDefault("logging.capture_console", defaults.Logging.CaptureConsole)399 m.viper.SetDefault("logging.capture_console", defaults.Logging.CaptureConsole)
400+ m.viper.SetDefault("logging.capture_gtk_logs", defaults.Logging.CaptureGTKLogs)
400 }401 }
401 402
402 func (m *Manager) setDebugDefaults(defaults *Config) {403 func (m *Manager) setDebugDefaults(defaults *Config) {
M internal/infrastructure/config/schema.go +3 -0
@@ -298,6 +298,9 @@ type LoggingConfig struct {
298298
299299 // Capture browser console to logs
300300 CaptureConsole bool `mapstructure:"capture_console" yaml:"capture_console" toml:"capture_console" json:"capture_console"`
301+
302+ // Capture GTK4/WebKitGTK6 messages to session logs
303+ CaptureGTKLogs bool `mapstructure:"capture_gtk_logs" yaml:"capture_gtk_logs" toml:"capture_gtk_logs" json:"capture_gtk_logs"`
301304 }
302305
303306 // AppearanceConfig holds UI/rendering preferences.
@@ -298,6 +298,9 @@ type LoggingConfig struct {
298 298
299 // Capture browser console to logs299 // Capture browser console to logs
300 CaptureConsole bool `mapstructure:"capture_console" yaml:"capture_console" toml:"capture_console" json:"capture_console"`300 CaptureConsole bool `mapstructure:"capture_console" yaml:"capture_console" toml:"capture_console" json:"capture_console"`
301+
302+ // Capture GTK4/WebKitGTK6 messages to session logs
303+ CaptureGTKLogs bool `mapstructure:"capture_gtk_logs" yaml:"capture_gtk_logs" toml:"capture_gtk_logs" json:"capture_gtk_logs"`
301 }304 }
302 305
303 // AppearanceConfig holds UI/rendering preferences.306 // AppearanceConfig holds UI/rendering preferences.
A internal/logging/glib.go +70 -0
@@ -0,0 +1,70 @@
1+package logging
2+
3+import (
4+ "context"
5+ "sync"
6+
7+ "github.com/jwijenbergh/puregotk/v4/glib"
8+ "github.com/rs/zerolog"
9+)
10+
11+// glibLogger holds the logger for the GLib handler.
12+// We need this because the GLib callback doesn't support passing Go pointers directly.
13+var (
14+ glibLogger zerolog.Logger
15+ glibLoggerOnce sync.Once
16+)
17+
18+// InstallGLibLogHandler installs a custom GLib log handler that routes
19+// GTK4/WebKitGTK6/GLib messages to the provided zerolog logger.
20+// This must be called before GTK/WebKit initialization.
21+// The handler captures messages from all GLib-based libraries (GTK, GDK, WebKit, etc.)
22+// If enableDebug is true, GLib debug messages will also be captured (useful when log level is debug/trace).
23+func InstallGLibLogHandler(ctx context.Context, logger zerolog.Logger, enableDebug bool) {
24+ log := FromContext(ctx)
25+
26+ glibLoggerOnce.Do(func() {
27+ glibLogger = logger
28+
29+ // Only enable GLib debug messages if requested (matches app log level)
30+ if enableDebug {
31+ glib.LogSetDebugEnabled(true)
32+ }
33+
34+ // Install our custom handler for all log messages
35+ handler := glib.LogFunc(glibLogHandler)
36+ glib.LogSetDefaultHandler(&handler, 0)
37+
38+ log.Info().Bool("debug_enabled", enableDebug).Msg("GLib log handler installed")
39+ })
40+}
41+
42+// glibLogHandler is the callback invoked by GLib for all log messages.
43+func glibLogHandler(domain string, level glib.LogLevelFlags, message string, _ uintptr) {
44+ // Map GLib log levels to zerolog levels
45+ var event *zerolog.Event
46+
47+ switch {
48+ case level&glib.GLogLevelErrorValue != 0:
49+ event = glibLogger.Error()
50+ case level&glib.GLogLevelCriticalValue != 0:
51+ event = glibLogger.Error()
52+ case level&glib.GLogLevelWarningValue != 0:
53+ event = glibLogger.Warn()
54+ case level&glib.GLogLevelMessageValue != 0:
55+ event = glibLogger.Info()
56+ case level&glib.GLogLevelInfoValue != 0:
57+ event = glibLogger.Info()
58+ case level&glib.GLogLevelDebugValue != 0:
59+ event = glibLogger.Debug()
60+ default:
61+ event = glibLogger.Debug()
62+ }
63+
64+ // Add domain if present
65+ if domain != "" {
66+ event = event.Str("glib_domain", domain)
67+ }
68+
69+ event.Msg(message)
70+}
@@ -0,0 +1,70 @@
1+package logging
2+
3+import (
4+ "context"
5+ "sync"
6+
7+ "github.com/jwijenbergh/puregotk/v4/glib"
8+ "github.com/rs/zerolog"
9+)
10+
11+// glibLogger holds the logger for the GLib handler.
12+// We need this because the GLib callback doesn't support passing Go pointers directly.
13+var (
14+ glibLogger zerolog.Logger
15+ glibLoggerOnce sync.Once
16+)
17+
18+// InstallGLibLogHandler installs a custom GLib log handler that routes
19+// GTK4/WebKitGTK6/GLib messages to the provided zerolog logger.
20+// This must be called before GTK/WebKit initialization.
21+// The handler captures messages from all GLib-based libraries (GTK, GDK, WebKit, etc.)
22+// If enableDebug is true, GLib debug messages will also be captured (useful when log level is debug/trace).
23+func InstallGLibLogHandler(ctx context.Context, logger zerolog.Logger, enableDebug bool) {
24+ log := FromContext(ctx)
25+
26+ glibLoggerOnce.Do(func() {
27+ glibLogger = logger
28+
29+ // Only enable GLib debug messages if requested (matches app log level)
30+ if enableDebug {
31+ glib.LogSetDebugEnabled(true)
32+ }
33+
34+ // Install our custom handler for all log messages
35+ handler := glib.LogFunc(glibLogHandler)
36+ glib.LogSetDefaultHandler(&handler, 0)
37+
38+ log.Info().Bool("debug_enabled", enableDebug).Msg("GLib log handler installed")
39+ })
40+}
41+
42+// glibLogHandler is the callback invoked by GLib for all log messages.
43+func glibLogHandler(domain string, level glib.LogLevelFlags, message string, _ uintptr) {
44+ // Map GLib log levels to zerolog levels
45+ var event *zerolog.Event
46+
47+ switch {
48+ case level&glib.GLogLevelErrorValue != 0:
49+ event = glibLogger.Error()
50+ case level&glib.GLogLevelCriticalValue != 0:
51+ event = glibLogger.Error()
52+ case level&glib.GLogLevelWarningValue != 0:
53+ event = glibLogger.Warn()
54+ case level&glib.GLogLevelMessageValue != 0:
55+ event = glibLogger.Info()
56+ case level&glib.GLogLevelInfoValue != 0:
57+ event = glibLogger.Info()
58+ case level&glib.GLogLevelDebugValue != 0:
59+ event = glibLogger.Debug()
60+ default:
61+ event = glibLogger.Debug()
62+ }
63+
64+ // Add domain if present
65+ if domain != "" {
66+ event = event.Str("glib_domain", domain)
67+ }
68+
69+ event.Msg(message)
70+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close