lint: support golangci-lint v2.12
- commit
- af7d4c50c73b1b6d1e223007de11e16f4a04afc3
- author
- brice · · 2026-05-01 15:13 UTC
- parent
- 44ab0c7
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
12 files · +164 -83
M .golangci.yml +3 -1
| @@ -40,7 +40,9 @@ linters: | |||
| 40 | 40 | statements: 50 | |
| 41 | 41 | goconst: | |
| 42 | 42 | min-len: 2 | |
| 43 | - | min-occurrences: 3 | |
| 43 | + | min-occurrences: 20 | |
| 44 | + | ignore-string-values: | |
| 45 | + | - "^(bool|int|string|error|engine\\.webkit|>=0)$" | |
| 44 | 46 | gocritic: | |
| 45 | 47 | enabled-tags: | |
| 46 | 48 | - diagnostic | |
| @@ -40,7 +40,9 @@ linters: | |||||
| 40 | statements: 50 | 40 | statements: 50 | ||
| 41 | goconst: | 41 | goconst: | ||
| 42 | min-len: 2 | 42 | min-len: 2 | ||
| 43 | - | min-occurrences: 3 | 43 | + | min-occurrences: 20 |
| 44 | + | ignore-string-values: | |||
| 45 | + | - "^(bool|int|string|error|engine\\.webkit|>=0)$" | |||
| 44 | gocritic: | 46 | gocritic: | ||
| 45 | enabled-tags: | 47 | enabled-tags: | ||
| 46 | - diagnostic | 48 | - diagnostic | ||
M cmd/dumber/main.go +3 -1
| @@ -327,11 +327,13 @@ func applyCEFRenderStackDefault(ctx context.Context, cfg *config.Config) { | |||
| 327 | 327 | if cfg == nil || cfg.Engine.ResolveEngineType() != config.EngineTypeCEF { | |
| 328 | 328 | return | |
| 329 | 329 | } | |
| 330 | - | infracef.ApplyDefaultRenderStackEnvironment(ctx) | |
| 330 | + | logger := logging.NewPortLogger(ctx) | |
| 331 | + | infracef.ApplyDefaultRenderStackEnvironment(logger) | |
| 331 | 332 | infracef.ApplyDefaultHardwareDecodeEnvironment(ctx, infracef.HardwareDecodeEnvironmentOptions{ | |
| 332 | 333 | EngineType: cfg.Engine.ResolveEngineType(), | |
| 333 | 334 | HardwareDecodingDisabled: cfg.Media.HardwareDecodingMode == config.HardwareDecodingDisable, | |
| 334 | 335 | RenderingEnvManager: renderenv.NewManager(), | |
| 336 | + | Logger: logger, | |
| 335 | 337 | }) | |
| 336 | 338 | } | |
| 337 | 339 | ||
| @@ -327,11 +327,13 @@ func applyCEFRenderStackDefault(ctx context.Context, cfg *config.Config) { | |||||
| 327 | if cfg == nil || cfg.Engine.ResolveEngineType() != config.EngineTypeCEF { | 327 | if cfg == nil || cfg.Engine.ResolveEngineType() != config.EngineTypeCEF { | ||
| 328 | return | 328 | return | ||
| 329 | } | 329 | } | ||
| 330 | - | infracef.ApplyDefaultRenderStackEnvironment(ctx) | 330 | + | logger := logging.NewPortLogger(ctx) |
| 331 | + | infracef.ApplyDefaultRenderStackEnvironment(logger) | |||
| 331 | infracef.ApplyDefaultHardwareDecodeEnvironment(ctx, infracef.HardwareDecodeEnvironmentOptions{ | 332 | infracef.ApplyDefaultHardwareDecodeEnvironment(ctx, infracef.HardwareDecodeEnvironmentOptions{ | ||
| 332 | EngineType: cfg.Engine.ResolveEngineType(), | 333 | EngineType: cfg.Engine.ResolveEngineType(), | ||
| 333 | HardwareDecodingDisabled: cfg.Media.HardwareDecodingMode == config.HardwareDecodingDisable, | 334 | HardwareDecodingDisabled: cfg.Media.HardwareDecodingMode == config.HardwareDecodingDisable, | ||
| 334 | RenderingEnvManager: renderenv.NewManager(), | 335 | RenderingEnvManager: renderenv.NewManager(), | ||
| 336 | + | Logger: logger, | |||
| 335 | }) | 337 | }) | ||
| 336 | } | 338 | } | ||
| 337 | 339 | ||||
M internal/application/port/logger.go +19 -0
| @@ -9,3 +9,22 @@ import ( | |||
| 9 | 9 | // LoggerFromContext resolves the current request/session logger from context. | |
| 10 | 10 | // Use cases depend on this boundary instead of importing infrastructure logging. | |
| 11 | 11 | type LoggerFromContext func(ctx context.Context) *zerolog.Logger | |
| 12 | + | ||
| 13 | + | // LogField is a structured logging field carried across application boundaries. | |
| 14 | + | type LogField struct { | |
| 15 | + | Key string | |
| 16 | + | Value any | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | // Field creates a structured log field. | |
| 20 | + | func Field(key string, value any) LogField { | |
| 21 | + | return LogField{Key: key, Value: value} | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // Logger is the logging boundary used by adapters that must not import a | |
| 25 | + | // concrete logging implementation directly. | |
| 26 | + | type Logger interface { | |
| 27 | + | Debug(msg string, fields ...LogField) | |
| 28 | + | Info(msg string, fields ...LogField) | |
| 29 | + | Warn(msg string, fields ...LogField) | |
| 30 | + | } | |
| @@ -9,3 +9,22 @@ import ( | |||||
| 9 | // LoggerFromContext resolves the current request/session logger from context. | 9 | // LoggerFromContext resolves the current request/session logger from context. | ||
| 10 | // Use cases depend on this boundary instead of importing infrastructure logging. | 10 | // Use cases depend on this boundary instead of importing infrastructure logging. | ||
| 11 | type LoggerFromContext func(ctx context.Context) *zerolog.Logger | 11 | type LoggerFromContext func(ctx context.Context) *zerolog.Logger | ||
| 12 | + | ||||
| 13 | + | // LogField is a structured logging field carried across application boundaries. | |||
| 14 | + | type LogField struct { | |||
| 15 | + | Key string | |||
| 16 | + | Value any | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | // Field creates a structured log field. | |||
| 20 | + | func Field(key string, value any) LogField { | |||
| 21 | + | return LogField{Key: key, Value: value} | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // Logger is the logging boundary used by adapters that must not import a | |||
| 25 | + | // concrete logging implementation directly. | |||
| 26 | + | type Logger interface { | |||
| 27 | + | Debug(msg string, fields ...LogField) | |||
| 28 | + | Info(msg string, fields ...LogField) | |||
| 29 | + | Warn(msg string, fields ...LogField) | |||
| 30 | + | } | |||
M internal/infrastructure/cef/engine.go +1 -1
| @@ -436,7 +436,7 @@ func (e *Engine) recordChildProcessLaunch(processType, useAngle, ozonePlatform, | |||
| 436 | 436 | Str("use_angle", useAngle). | |
| 437 | 437 | Str("ozone_platform", ozonePlatform). | |
| 438 | 438 | Str("render_node_override", renderNodeOverride). | |
| 439 | - | Str("command_line", commandLine). | |
| 439 | + | Strs("command_line_flags", safeChromiumCmdlineFlags(commandLine)). | |
| 440 | 440 | Uint64("renderer_launches", e.childLaunchRenderer.Load()). | |
| 441 | 441 | Uint64("gpu_launches", e.childLaunchGPU.Load()). | |
| 442 | 442 | Uint64("other_launches", e.childLaunchOther.Load()). | |
| @@ -436,7 +436,7 @@ func (e *Engine) recordChildProcessLaunch(processType, useAngle, ozonePlatform, | |||||
| 436 | Str("use_angle", useAngle). | 436 | Str("use_angle", useAngle). | ||
| 437 | Str("ozone_platform", ozonePlatform). | 437 | Str("ozone_platform", ozonePlatform). | ||
| 438 | Str("render_node_override", renderNodeOverride). | 438 | Str("render_node_override", renderNodeOverride). | ||
| 439 | - | Str("command_line", commandLine). | 439 | + | Strs("command_line_flags", safeChromiumCmdlineFlags(commandLine)). |
| 440 | Uint64("renderer_launches", e.childLaunchRenderer.Load()). | 440 | Uint64("renderer_launches", e.childLaunchRenderer.Load()). | ||
| 441 | Uint64("gpu_launches", e.childLaunchGPU.Load()). | 441 | Uint64("gpu_launches", e.childLaunchGPU.Load()). | ||
| 442 | Uint64("other_launches", e.childLaunchOther.Load()). | 442 | Uint64("other_launches", e.childLaunchOther.Load()). | ||
M internal/infrastructure/cef/process_diagnostics.go +11 -7
| @@ -206,6 +206,7 @@ func procThreadCount(pid int) int { | |||
| 206 | 206 | } | |
| 207 | 207 | ||
| 208 | 208 | type procThreadSummary struct { | |
| 209 | + | tid int | |
| 209 | 210 | text string | |
| 210 | 211 | ticks uint64 | |
| 211 | 212 | } | |
| @@ -234,18 +235,21 @@ func procThreadSummariesWithDetails(pid, limit int, detailed bool) []string { | |||
| 234 | 235 | continue | |
| 235 | 236 | } | |
| 236 | 237 | text, ticks := parseThreadStat(tid, string(b)) | |
| 237 | - | if detailed { | |
| 238 | - | text = text + " wchan=" + procThreadWchan(pid, tid) | |
| 239 | - | if stack := procThreadKernelStack(pid, tid); stack != "" { | |
| 240 | - | text = text + " kernel_stack=" + stack | |
| 241 | - | } | |
| 242 | - | } | |
| 243 | - | items = append(items, procThreadSummary{text: text, ticks: ticks}) | |
| 238 | + | items = append(items, procThreadSummary{tid: tid, text: text, ticks: ticks}) | |
| 244 | 239 | } | |
| 245 | 240 | sort.Slice(items, func(i, j int) bool { return items[i].ticks > items[j].ticks }) | |
| 246 | 241 | if limit > 0 && len(items) > limit { | |
| 247 | 242 | items = items[:limit] | |
| 248 | 243 | } | |
| 244 | + | if detailed { | |
| 245 | + | for i := range items { | |
| 246 | + | text := items[i].text + " wchan=" + procThreadWchan(pid, items[i].tid) | |
| 247 | + | if stack := procThreadKernelStack(pid, items[i].tid); stack != "" { | |
| 248 | + | text = text + " kernel_stack=" + stack | |
| 249 | + | } | |
| 250 | + | items[i].text = text | |
| 251 | + | } | |
| 252 | + | } | |
| 249 | 253 | out := make([]string, 0, len(items)) | |
| 250 | 254 | for _, item := range items { | |
| 251 | 255 | out = append(out, item.text) | |
| @@ -206,6 +206,7 @@ func procThreadCount(pid int) int { | |||||
| 206 | } | 206 | } | ||
| 207 | 207 | ||||
| 208 | type procThreadSummary struct { | 208 | type procThreadSummary struct { | ||
| 209 | + | tid int | |||
| 209 | text string | 210 | text string | ||
| 210 | ticks uint64 | 211 | ticks uint64 | ||
| 211 | } | 212 | } | ||
| @@ -234,18 +235,21 @@ func procThreadSummariesWithDetails(pid, limit int, detailed bool) []string { | |||||
| 234 | continue | 235 | continue | ||
| 235 | } | 236 | } | ||
| 236 | text, ticks := parseThreadStat(tid, string(b)) | 237 | text, ticks := parseThreadStat(tid, string(b)) | ||
| 237 | - | if detailed { | 238 | + | items = append(items, procThreadSummary{tid: tid, text: text, ticks: ticks}) |
| 238 | - | text = text + " wchan=" + procThreadWchan(pid, tid) | |||
| 239 | - | if stack := procThreadKernelStack(pid, tid); stack != "" { | |||
| 240 | - | text = text + " kernel_stack=" + stack | |||
| 241 | - | } | |||
| 242 | - | } | |||
| 243 | - | items = append(items, procThreadSummary{text: text, ticks: ticks}) | |||
| 244 | } | 239 | } | ||
| 245 | sort.Slice(items, func(i, j int) bool { return items[i].ticks > items[j].ticks }) | 240 | sort.Slice(items, func(i, j int) bool { return items[i].ticks > items[j].ticks }) | ||
| 246 | if limit > 0 && len(items) > limit { | 241 | if limit > 0 && len(items) > limit { | ||
| 247 | items = items[:limit] | 242 | items = items[:limit] | ||
| 248 | } | 243 | } | ||
| 244 | + | if detailed { | |||
| 245 | + | for i := range items { | |||
| 246 | + | text := items[i].text + " wchan=" + procThreadWchan(pid, items[i].tid) | |||
| 247 | + | if stack := procThreadKernelStack(pid, items[i].tid); stack != "" { | |||
| 248 | + | text = text + " kernel_stack=" + stack | |||
| 249 | + | } | |||
| 250 | + | items[i].text = text | |||
| 251 | + | } | |||
| 252 | + | } | |||
| 249 | out := make([]string, 0, len(items)) | 253 | out := make([]string, 0, len(items)) | ||
| 250 | for _, item := range items { | 254 | for _, item := range items { | ||
| 251 | out = append(out, item.text) | 255 | out = append(out, item.text) | ||
M internal/infrastructure/cef/process_diagnostics_test.go +2 -0
| @@ -18,6 +18,8 @@ func TestSafeChromiumCmdlineFlagsOmitsPathBearingAndUnknownFlags(t *testing.T) { | |||
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | 20 | func TestReserveRenderStallProcessDiagnosticsAppliesCooldown(t *testing.T) { | |
| 21 | + | prev := renderStallProcessDiagnosticsLastUnixNS.Load() | |
| 22 | + | t.Cleanup(func() { renderStallProcessDiagnosticsLastUnixNS.Store(prev) }) | |
| 21 | 23 | renderStallProcessDiagnosticsLastUnixNS.Store(0) | |
| 22 | 24 | now := time.Unix(100, 0) | |
| 23 | 25 | ||
| @@ -18,6 +18,8 @@ func TestSafeChromiumCmdlineFlagsOmitsPathBearingAndUnknownFlags(t *testing.T) { | |||||
| 18 | } | 18 | } | ||
| 19 | 19 | ||||
| 20 | func TestReserveRenderStallProcessDiagnosticsAppliesCooldown(t *testing.T) { | 20 | func TestReserveRenderStallProcessDiagnosticsAppliesCooldown(t *testing.T) { | ||
| 21 | + | prev := renderStallProcessDiagnosticsLastUnixNS.Load() | |||
| 22 | + | t.Cleanup(func() { renderStallProcessDiagnosticsLastUnixNS.Store(prev) }) | |||
| 21 | renderStallProcessDiagnosticsLastUnixNS.Store(0) | 23 | renderStallProcessDiagnosticsLastUnixNS.Store(0) | ||
| 22 | now := time.Unix(100, 0) | 24 | now := time.Unix(100, 0) | ||
| 23 | 25 | ||||
M internal/infrastructure/cef/render_stack_env.go +62 -63
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | 6 | "strings" | |
| 7 | 7 | ||
| 8 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| 9 | - | "github.com/bnema/dumber/internal/logging" | |
| 10 | 9 | ) | |
| 11 | 10 | ||
| 12 | 11 | const ( | |
| @@ -39,38 +38,35 @@ const ( | |||
| 39 | 38 | // Low-level env vars are treated as library-development escape hatches only. | |
| 40 | 39 | // Dumber keeps the selected stack coherent by default and overwrites conflicts; | |
| 41 | 40 | // set DUMBER_RENDER_STACK_ALLOW_SPLIT=1 to preserve explicit split-stack values. | |
| 42 | - | func ApplyDefaultRenderStackEnvironment(ctx context.Context) string { | |
| 43 | - | return applyDefaultRenderStackEnvironment(ctx) | |
| 41 | + | func ApplyDefaultRenderStackEnvironment(logger port.Logger) string { | |
| 42 | + | return applyDefaultRenderStackEnvironment(logger) | |
| 44 | 43 | } | |
| 45 | 44 | ||
| 46 | - | func applyDefaultRenderStackEnvironment(ctx context.Context) string { | |
| 47 | - | stack := normalizeRenderStack(ctx, os.Getenv(dumberRenderStackEnvVar)) | |
| 45 | + | func applyDefaultRenderStackEnvironment(logger port.Logger) string { | |
| 46 | + | stack := normalizeRenderStack(logger, os.Getenv(dumberRenderStackEnvVar)) | |
| 48 | 47 | switch stack { | |
| 49 | 48 | case renderStackLegacyGL: | |
| 50 | - | setRenderStackEnv(ctx, gskRendererEnvVar, gskRendererOpenGL) | |
| 51 | - | setRenderStackEnv(ctx, cef2gtkBackendEnvVar, cef2gtkBackendGLArea) | |
| 52 | - | setRenderStackEnv(ctx, cef2gtkAngleBackendVar, cef2gtkAngleGLEGL) | |
| 49 | + | setRenderStackEnv(logger, gskRendererEnvVar, gskRendererOpenGL) | |
| 50 | + | setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGLArea) | |
| 51 | + | setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleGLEGL) | |
| 53 | 52 | default: | |
| 54 | 53 | stack = renderStackVulkanDMABUF | |
| 55 | - | setRenderStackEnv(ctx, gskRendererEnvVar, gskRendererVulkan) | |
| 56 | - | setRenderStackEnv(ctx, cef2gtkBackendEnvVar, cef2gtkBackendGDKDMABUF) | |
| 57 | - | setRenderStackEnv(ctx, cef2gtkAngleBackendVar, cef2gtkAngleVulkan) | |
| 54 | + | setRenderStackEnv(logger, gskRendererEnvVar, gskRendererVulkan) | |
| 55 | + | setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGDKDMABUF) | |
| 56 | + | setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleVulkan) | |
| 58 | 57 | } | |
| 59 | - | logRenderStackEnvironment(ctx, stack) | |
| 58 | + | logRenderStackEnvironment(logger, stack) | |
| 60 | 59 | return stack | |
| 61 | 60 | } | |
| 62 | 61 | ||
| 63 | - | func logRenderStackEnvironment(ctx context.Context, stack string) { | |
| 64 | - | if ctx == nil { | |
| 65 | - | ctx = context.Background() | |
| 66 | - | } | |
| 67 | - | logging.FromContext(ctx).Info(). | |
| 68 | - | Str("render_stack", stack). | |
| 69 | - | Str("gsk_renderer", os.Getenv(gskRendererEnvVar)). | |
| 70 | - | Str("cef2gtk_backend", os.Getenv(cef2gtkBackendEnvVar)). | |
| 71 | - | Str("cef2gtk_angle_backend", os.Getenv(cef2gtkAngleBackendVar)). | |
| 72 | - | Bool("split_stack_allowed", envBoolEnabled(dumberRenderStackAllowSplitEnvVar)). | |
| 73 | - | Msg("cef: render stack environment configured") | |
| 62 | + | func logRenderStackEnvironment(logger port.Logger, stack string) { | |
| 63 | + | logInfo(logger, "cef: render stack environment configured", | |
| 64 | + | port.Field("render_stack", stack), | |
| 65 | + | port.Field("gsk_renderer", os.Getenv(gskRendererEnvVar)), | |
| 66 | + | port.Field("cef2gtk_backend", os.Getenv(cef2gtkBackendEnvVar)), | |
| 67 | + | port.Field("cef2gtk_angle_backend", os.Getenv(cef2gtkAngleBackendVar)), | |
| 68 | + | port.Field("split_stack_allowed", envBoolEnabled(dumberRenderStackAllowSplitEnvVar)), | |
| 69 | + | ) | |
| 74 | 70 | } | |
| 75 | 71 | ||
| 76 | 72 | // HardwareDecodeEnvironmentOptions carries config-derived CEF environment | |
| @@ -80,6 +76,7 @@ type HardwareDecodeEnvironmentOptions struct { | |||
| 80 | 76 | EngineType string | |
| 81 | 77 | HardwareDecodingDisabled bool | |
| 82 | 78 | RenderingEnvManager port.RenderingEnvManager | |
| 79 | + | Logger port.Logger | |
| 83 | 80 | } | |
| 84 | 81 | ||
| 85 | 82 | // ApplyDefaultHardwareDecodeEnvironment maps Dumber's media config to CEF's | |
| @@ -90,32 +87,28 @@ func ApplyDefaultHardwareDecodeEnvironment(ctx context.Context, opts HardwareDec | |||
| 90 | 87 | return | |
| 91 | 88 | } | |
| 92 | 89 | if opts.HardwareDecodingDisabled { | |
| 93 | - | setEnvDefault(ctx, cefEnableVAAPIEnvVar, "0") | |
| 90 | + | setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "0") | |
| 94 | 91 | return | |
| 95 | 92 | } | |
| 96 | - | setEnvDefault(ctx, cefEnableVAAPIEnvVar, "1") | |
| 97 | - | applyDefaultLIBVADriverEnvironment(ctx, opts.RenderingEnvManager) | |
| 93 | + | setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "1") | |
| 94 | + | applyDefaultLIBVADriverEnvironment(ctx, opts.RenderingEnvManager, opts.Logger) | |
| 98 | 95 | } | |
| 99 | 96 | ||
| 100 | - | func applyDefaultLIBVADriverEnvironment(ctx context.Context, manager port.RenderingEnvManager) { | |
| 97 | + | func applyDefaultLIBVADriverEnvironment(ctx context.Context, manager port.RenderingEnvManager, logger port.Logger) { | |
| 101 | 98 | if strings.TrimSpace(os.Getenv("LIBVA_DRIVER_NAME")) != "" || manager == nil { | |
| 102 | 99 | return | |
| 103 | 100 | } | |
| 104 | 101 | switch manager.DetectGPUVendor(ctx) { | |
| 105 | 102 | case port.GPUVendorAMD: | |
| 106 | - | setEnvDefault(ctx, "LIBVA_DRIVER_NAME", "radeonsi") | |
| 103 | + | setEnvDefault(logger, "LIBVA_DRIVER_NAME", "radeonsi") | |
| 107 | 104 | case port.GPUVendorIntel: | |
| 108 | - | setEnvDefault(ctx, "LIBVA_DRIVER_NAME", "iHD") | |
| 105 | + | setEnvDefault(logger, "LIBVA_DRIVER_NAME", "iHD") | |
| 109 | 106 | case port.GPUVendorNVIDIA: | |
| 110 | - | if ctx == nil { | |
| 111 | - | ctx = context.Background() | |
| 112 | - | } | |
| 113 | - | logging.FromContext(ctx).Warn(). | |
| 114 | - | Msg("cef: not defaulting LIBVA_DRIVER_NAME for NVIDIA; Chromium/CEF VAAPI support is driver-dependent") | |
| 107 | + | logWarn(logger, "cef: not defaulting LIBVA_DRIVER_NAME for NVIDIA; Chromium/CEF VAAPI support is driver-dependent") | |
| 115 | 108 | } | |
| 116 | 109 | } | |
| 117 | 110 | ||
| 118 | - | func normalizeRenderStack(ctx context.Context, value string) string { | |
| 111 | + | func normalizeRenderStack(logger port.Logger, value string) string { | |
| 119 | 112 | normalized := strings.ToLower(strings.TrimSpace(value)) | |
| 120 | 113 | switch normalized { | |
| 121 | 114 | case "", renderStackAuto, renderStackVulkanDMABUF: | |
| @@ -123,49 +116,55 @@ func normalizeRenderStack(ctx context.Context, value string) string { | |||
| 123 | 116 | case renderStackLegacyGL: | |
| 124 | 117 | return renderStackLegacyGL | |
| 125 | 118 | default: | |
| 126 | - | if ctx == nil { | |
| 127 | - | ctx = context.Background() | |
| 128 | - | } | |
| 129 | - | logging.FromContext(ctx).Warn(). | |
| 130 | - | Str("render_stack", value). | |
| 131 | - | Str("fallback", renderStackVulkanDMABUF). | |
| 132 | - | Msg("cef: unknown render stack, falling back to default") | |
| 119 | + | logWarn(logger, "cef: unknown render stack, falling back to default", | |
| 120 | + | port.Field("render_stack", value), | |
| 121 | + | port.Field("fallback", renderStackVulkanDMABUF), | |
| 122 | + | ) | |
| 133 | 123 | return renderStackVulkanDMABUF | |
| 134 | 124 | } | |
| 135 | 125 | } | |
| 136 | 126 | ||
| 137 | - | func setRenderStackEnv(ctx context.Context, key, value string) { | |
| 127 | + | func setRenderStackEnv(logger port.Logger, key, value string) { | |
| 138 | 128 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | |
| 139 | - | if ctx == nil { | |
| 140 | - | ctx = context.Background() | |
| 129 | + | fields := []port.LogField{ | |
| 130 | + | port.Field("key", key), | |
| 131 | + | port.Field("value", existing), | |
| 132 | + | port.Field("stack_value", value), | |
| 141 | 133 | } | |
| 142 | 134 | if envBoolEnabled(dumberRenderStackAllowSplitEnvVar) { | |
| 143 | - | logging.FromContext(ctx).Warn(). | |
| 144 | - | Str("key", key). | |
| 145 | - | Str("value", existing). | |
| 146 | - | Str("stack_value", value). | |
| 147 | - | Msg("cef: preserving explicit split render stack environment override") | |
| 135 | + | logWarn(logger, "cef: preserving explicit split render stack environment override", fields...) | |
| 148 | 136 | return | |
| 149 | 137 | } | |
| 150 | - | logging.FromContext(ctx).Warn(). | |
| 151 | - | Str("key", key). | |
| 152 | - | Str("value", existing). | |
| 153 | - | Str("stack_value", value). | |
| 154 | - | Msg("cef: overriding low-level render stack environment to keep Dumber stack coherent") | |
| 138 | + | logWarn(logger, "cef: overriding low-level render stack environment to keep Dumber stack coherent", fields...) | |
| 155 | 139 | } | |
| 156 | 140 | _ = os.Setenv(key, value) | |
| 157 | 141 | } | |
| 158 | 142 | ||
| 159 | - | func setEnvDefault(ctx context.Context, key, value string) { | |
| 143 | + | func setEnvDefault(logger port.Logger, key, value string) { | |
| 160 | 144 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | |
| 161 | - | if ctx == nil { | |
| 162 | - | ctx = context.Background() | |
| 163 | - | } | |
| 164 | - | logging.FromContext(ctx).Debug(). | |
| 165 | - | Str("key", key). | |
| 166 | - | Str("value", existing). | |
| 167 | - | Msg("cef: preserving explicit environment override") | |
| 145 | + | logDebug(logger, "cef: preserving explicit environment override", | |
| 146 | + | port.Field("key", key), | |
| 147 | + | port.Field("value", existing), | |
| 148 | + | ) | |
| 168 | 149 | return | |
| 169 | 150 | } | |
| 170 | 151 | _ = os.Setenv(key, value) | |
| 171 | 152 | } | |
| 153 | + | ||
| 154 | + | func logDebug(logger port.Logger, msg string, fields ...port.LogField) { | |
| 155 | + | if logger != nil { | |
| 156 | + | logger.Debug(msg, fields...) | |
| 157 | + | } | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | func logInfo(logger port.Logger, msg string, fields ...port.LogField) { | |
| 161 | + | if logger != nil { | |
| 162 | + | logger.Info(msg, fields...) | |
| 163 | + | } | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | func logWarn(logger port.Logger, msg string, fields ...port.LogField) { | |
| 167 | + | if logger != nil { | |
| 168 | + | logger.Warn(msg, fields...) | |
| 169 | + | } | |
| 170 | + | } | |
| @@ -6,7 +6,6 @@ import ( | |||||
| 6 | "strings" | 6 | "strings" | ||
| 7 | 7 | ||||
| 8 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| 9 | - | "github.com/bnema/dumber/internal/logging" | |||
| 10 | ) | 9 | ) | ||
| 11 | 10 | ||||
| 12 | const ( | 11 | const ( | ||
| @@ -39,38 +38,35 @@ const ( | |||||
| 39 | // Low-level env vars are treated as library-development escape hatches only. | 38 | // Low-level env vars are treated as library-development escape hatches only. | ||
| 40 | // Dumber keeps the selected stack coherent by default and overwrites conflicts; | 39 | // Dumber keeps the selected stack coherent by default and overwrites conflicts; | ||
| 41 | // set DUMBER_RENDER_STACK_ALLOW_SPLIT=1 to preserve explicit split-stack values. | 40 | // set DUMBER_RENDER_STACK_ALLOW_SPLIT=1 to preserve explicit split-stack values. | ||
| 42 | - | func ApplyDefaultRenderStackEnvironment(ctx context.Context) string { | 41 | + | func ApplyDefaultRenderStackEnvironment(logger port.Logger) string { |
| 43 | - | return applyDefaultRenderStackEnvironment(ctx) | 42 | + | return applyDefaultRenderStackEnvironment(logger) |
| 44 | } | 43 | } | ||
| 45 | 44 | ||||
| 46 | - | func applyDefaultRenderStackEnvironment(ctx context.Context) string { | 45 | + | func applyDefaultRenderStackEnvironment(logger port.Logger) string { |
| 47 | - | stack := normalizeRenderStack(ctx, os.Getenv(dumberRenderStackEnvVar)) | 46 | + | stack := normalizeRenderStack(logger, os.Getenv(dumberRenderStackEnvVar)) |
| 48 | switch stack { | 47 | switch stack { | ||
| 49 | case renderStackLegacyGL: | 48 | case renderStackLegacyGL: | ||
| 50 | - | setRenderStackEnv(ctx, gskRendererEnvVar, gskRendererOpenGL) | 49 | + | setRenderStackEnv(logger, gskRendererEnvVar, gskRendererOpenGL) |
| 51 | - | setRenderStackEnv(ctx, cef2gtkBackendEnvVar, cef2gtkBackendGLArea) | 50 | + | setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGLArea) |
| 52 | - | setRenderStackEnv(ctx, cef2gtkAngleBackendVar, cef2gtkAngleGLEGL) | 51 | + | setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleGLEGL) |
| 53 | default: | 52 | default: | ||
| 54 | stack = renderStackVulkanDMABUF | 53 | stack = renderStackVulkanDMABUF | ||
| 55 | - | setRenderStackEnv(ctx, gskRendererEnvVar, gskRendererVulkan) | 54 | + | setRenderStackEnv(logger, gskRendererEnvVar, gskRendererVulkan) |
| 56 | - | setRenderStackEnv(ctx, cef2gtkBackendEnvVar, cef2gtkBackendGDKDMABUF) | 55 | + | setRenderStackEnv(logger, cef2gtkBackendEnvVar, cef2gtkBackendGDKDMABUF) |
| 57 | - | setRenderStackEnv(ctx, cef2gtkAngleBackendVar, cef2gtkAngleVulkan) | 56 | + | setRenderStackEnv(logger, cef2gtkAngleBackendVar, cef2gtkAngleVulkan) |
| 58 | } | 57 | } | ||
| 59 | - | logRenderStackEnvironment(ctx, stack) | 58 | + | logRenderStackEnvironment(logger, stack) |
| 60 | return stack | 59 | return stack | ||
| 61 | } | 60 | } | ||
| 62 | 61 | ||||
| 63 | - | func logRenderStackEnvironment(ctx context.Context, stack string) { | 62 | + | func logRenderStackEnvironment(logger port.Logger, stack string) { |
| 64 | - | if ctx == nil { | 63 | + | logInfo(logger, "cef: render stack environment configured", |
| 65 | - | ctx = context.Background() | 64 | + | port.Field("render_stack", stack), |
| 66 | - | } | 65 | + | port.Field("gsk_renderer", os.Getenv(gskRendererEnvVar)), |
| 67 | - | logging.FromContext(ctx).Info(). | 66 | + | port.Field("cef2gtk_backend", os.Getenv(cef2gtkBackendEnvVar)), |
| 68 | - | Str("render_stack", stack). | 67 | + | port.Field("cef2gtk_angle_backend", os.Getenv(cef2gtkAngleBackendVar)), |
| 69 | - | Str("gsk_renderer", os.Getenv(gskRendererEnvVar)). | 68 | + | port.Field("split_stack_allowed", envBoolEnabled(dumberRenderStackAllowSplitEnvVar)), |
| 70 | - | Str("cef2gtk_backend", os.Getenv(cef2gtkBackendEnvVar)). | 69 | + | ) |
| 71 | - | Str("cef2gtk_angle_backend", os.Getenv(cef2gtkAngleBackendVar)). | |||
| 72 | - | Bool("split_stack_allowed", envBoolEnabled(dumberRenderStackAllowSplitEnvVar)). | |||
| 73 | - | Msg("cef: render stack environment configured") | |||
| 74 | } | 70 | } | ||
| 75 | 71 | ||||
| 76 | // HardwareDecodeEnvironmentOptions carries config-derived CEF environment | 72 | // HardwareDecodeEnvironmentOptions carries config-derived CEF environment | ||
| @@ -80,6 +76,7 @@ type HardwareDecodeEnvironmentOptions struct { | |||||
| 80 | EngineType string | 76 | EngineType string | ||
| 81 | HardwareDecodingDisabled bool | 77 | HardwareDecodingDisabled bool | ||
| 82 | RenderingEnvManager port.RenderingEnvManager | 78 | RenderingEnvManager port.RenderingEnvManager | ||
| 79 | + | Logger port.Logger | |||
| 83 | } | 80 | } | ||
| 84 | 81 | ||||
| 85 | // ApplyDefaultHardwareDecodeEnvironment maps Dumber's media config to CEF's | 82 | // ApplyDefaultHardwareDecodeEnvironment maps Dumber's media config to CEF's | ||
| @@ -90,32 +87,28 @@ func ApplyDefaultHardwareDecodeEnvironment(ctx context.Context, opts HardwareDec | |||||
| 90 | return | 87 | return | ||
| 91 | } | 88 | } | ||
| 92 | if opts.HardwareDecodingDisabled { | 89 | if opts.HardwareDecodingDisabled { | ||
| 93 | - | setEnvDefault(ctx, cefEnableVAAPIEnvVar, "0") | 90 | + | setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "0") |
| 94 | return | 91 | return | ||
| 95 | } | 92 | } | ||
| 96 | - | setEnvDefault(ctx, cefEnableVAAPIEnvVar, "1") | 93 | + | setEnvDefault(opts.Logger, cefEnableVAAPIEnvVar, "1") |
| 97 | - | applyDefaultLIBVADriverEnvironment(ctx, opts.RenderingEnvManager) | 94 | + | applyDefaultLIBVADriverEnvironment(ctx, opts.RenderingEnvManager, opts.Logger) |
| 98 | } | 95 | } | ||
| 99 | 96 | ||||
| 100 | - | func applyDefaultLIBVADriverEnvironment(ctx context.Context, manager port.RenderingEnvManager) { | 97 | + | func applyDefaultLIBVADriverEnvironment(ctx context.Context, manager port.RenderingEnvManager, logger port.Logger) { |
| 101 | if strings.TrimSpace(os.Getenv("LIBVA_DRIVER_NAME")) != "" || manager == nil { | 98 | if strings.TrimSpace(os.Getenv("LIBVA_DRIVER_NAME")) != "" || manager == nil { | ||
| 102 | return | 99 | return | ||
| 103 | } | 100 | } | ||
| 104 | switch manager.DetectGPUVendor(ctx) { | 101 | switch manager.DetectGPUVendor(ctx) { | ||
| 105 | case port.GPUVendorAMD: | 102 | case port.GPUVendorAMD: | ||
| 106 | - | setEnvDefault(ctx, "LIBVA_DRIVER_NAME", "radeonsi") | 103 | + | setEnvDefault(logger, "LIBVA_DRIVER_NAME", "radeonsi") |
| 107 | case port.GPUVendorIntel: | 104 | case port.GPUVendorIntel: | ||
| 108 | - | setEnvDefault(ctx, "LIBVA_DRIVER_NAME", "iHD") | 105 | + | setEnvDefault(logger, "LIBVA_DRIVER_NAME", "iHD") |
| 109 | case port.GPUVendorNVIDIA: | 106 | case port.GPUVendorNVIDIA: | ||
| 110 | - | if ctx == nil { | 107 | + | logWarn(logger, "cef: not defaulting LIBVA_DRIVER_NAME for NVIDIA; Chromium/CEF VAAPI support is driver-dependent") |
| 111 | - | ctx = context.Background() | |||
| 112 | - | } | |||
| 113 | - | logging.FromContext(ctx).Warn(). | |||
| 114 | - | Msg("cef: not defaulting LIBVA_DRIVER_NAME for NVIDIA; Chromium/CEF VAAPI support is driver-dependent") | |||
| 115 | } | 108 | } | ||
| 116 | } | 109 | } | ||
| 117 | 110 | ||||
| 118 | - | func normalizeRenderStack(ctx context.Context, value string) string { | 111 | + | func normalizeRenderStack(logger port.Logger, value string) string { |
| 119 | normalized := strings.ToLower(strings.TrimSpace(value)) | 112 | normalized := strings.ToLower(strings.TrimSpace(value)) | ||
| 120 | switch normalized { | 113 | switch normalized { | ||
| 121 | case "", renderStackAuto, renderStackVulkanDMABUF: | 114 | case "", renderStackAuto, renderStackVulkanDMABUF: | ||
| @@ -123,49 +116,55 @@ func normalizeRenderStack(ctx context.Context, value string) string { | |||||
| 123 | case renderStackLegacyGL: | 116 | case renderStackLegacyGL: | ||
| 124 | return renderStackLegacyGL | 117 | return renderStackLegacyGL | ||
| 125 | default: | 118 | default: | ||
| 126 | - | if ctx == nil { | 119 | + | logWarn(logger, "cef: unknown render stack, falling back to default", |
| 127 | - | ctx = context.Background() | 120 | + | port.Field("render_stack", value), |
| 128 | - | } | 121 | + | port.Field("fallback", renderStackVulkanDMABUF), |
| 129 | - | logging.FromContext(ctx).Warn(). | 122 | + | ) |
| 130 | - | Str("render_stack", value). | |||
| 131 | - | Str("fallback", renderStackVulkanDMABUF). | |||
| 132 | - | Msg("cef: unknown render stack, falling back to default") | |||
| 133 | return renderStackVulkanDMABUF | 123 | return renderStackVulkanDMABUF | ||
| 134 | } | 124 | } | ||
| 135 | } | 125 | } | ||
| 136 | 126 | ||||
| 137 | - | func setRenderStackEnv(ctx context.Context, key, value string) { | 127 | + | func setRenderStackEnv(logger port.Logger, key, value string) { |
| 138 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | 128 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | ||
| 139 | - | if ctx == nil { | 129 | + | fields := []port.LogField{ |
| 140 | - | ctx = context.Background() | 130 | + | port.Field("key", key), |
| 131 | + | port.Field("value", existing), | |||
| 132 | + | port.Field("stack_value", value), | |||
| 141 | } | 133 | } | ||
| 142 | if envBoolEnabled(dumberRenderStackAllowSplitEnvVar) { | 134 | if envBoolEnabled(dumberRenderStackAllowSplitEnvVar) { | ||
| 143 | - | logging.FromContext(ctx).Warn(). | 135 | + | logWarn(logger, "cef: preserving explicit split render stack environment override", fields...) |
| 144 | - | Str("key", key). | |||
| 145 | - | Str("value", existing). | |||
| 146 | - | Str("stack_value", value). | |||
| 147 | - | Msg("cef: preserving explicit split render stack environment override") | |||
| 148 | return | 136 | return | ||
| 149 | } | 137 | } | ||
| 150 | - | logging.FromContext(ctx).Warn(). | 138 | + | logWarn(logger, "cef: overriding low-level render stack environment to keep Dumber stack coherent", fields...) |
| 151 | - | Str("key", key). | |||
| 152 | - | Str("value", existing). | |||
| 153 | - | Str("stack_value", value). | |||
| 154 | - | Msg("cef: overriding low-level render stack environment to keep Dumber stack coherent") | |||
| 155 | } | 139 | } | ||
| 156 | _ = os.Setenv(key, value) | 140 | _ = os.Setenv(key, value) | ||
| 157 | } | 141 | } | ||
| 158 | 142 | ||||
| 159 | - | func setEnvDefault(ctx context.Context, key, value string) { | 143 | + | func setEnvDefault(logger port.Logger, key, value string) { |
| 160 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | 144 | if existing := strings.TrimSpace(os.Getenv(key)); existing != "" { | ||
| 161 | - | if ctx == nil { | 145 | + | logDebug(logger, "cef: preserving explicit environment override", |
| 162 | - | ctx = context.Background() | 146 | + | port.Field("key", key), |
| 163 | - | } | 147 | + | port.Field("value", existing), |
| 164 | - | logging.FromContext(ctx).Debug(). | 148 | + | ) |
| 165 | - | Str("key", key). | |||
| 166 | - | Str("value", existing). | |||
| 167 | - | Msg("cef: preserving explicit environment override") | |||
| 168 | return | 149 | return | ||
| 169 | } | 150 | } | ||
| 170 | _ = os.Setenv(key, value) | 151 | _ = os.Setenv(key, value) | ||
| 171 | } | 152 | } | ||
| 153 | + | ||||
| 154 | + | func logDebug(logger port.Logger, msg string, fields ...port.LogField) { | |||
| 155 | + | if logger != nil { | |||
| 156 | + | logger.Debug(msg, fields...) | |||
| 157 | + | } | |||
| 158 | + | } | |||
| 159 | + | ||||
| 160 | + | func logInfo(logger port.Logger, msg string, fields ...port.LogField) { | |||
| 161 | + | if logger != nil { | |||
| 162 | + | logger.Info(msg, fields...) | |||
| 163 | + | } | |||
| 164 | + | } | |||
| 165 | + | ||||
| 166 | + | func logWarn(logger port.Logger, msg string, fields ...port.LogField) { | |||
| 167 | + | if logger != nil { | |||
| 168 | + | logger.Warn(msg, fields...) | |||
| 169 | + | } | |||
| 170 | + | } | |||
M internal/infrastructure/cef/render_stack_env_test.go +5 -5
| @@ -14,7 +14,7 @@ func TestApplyDefaultRenderStackEnvironment_DefaultsToGDKDMABUFWithANGLEVulkan(t | |||
| 14 | 14 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | |
| 15 | 15 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | |
| 16 | 16 | ||
| 17 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | |
| 17 | + | got := applyDefaultRenderStackEnvironment(nil) | |
| 18 | 18 | ||
| 19 | 19 | if got != renderStackVulkanDMABUF { | |
| 20 | 20 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | |
| @@ -37,7 +37,7 @@ func TestApplyDefaultRenderStackEnvironment_OverridesConflictingLowLevelDefaults | |||
| 37 | 37 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | |
| 38 | 38 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | |
| 39 | 39 | ||
| 40 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | |
| 40 | + | got := applyDefaultRenderStackEnvironment(nil) | |
| 41 | 41 | ||
| 42 | 42 | if got != renderStackVulkanDMABUF { | |
| 43 | 43 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | |
| @@ -60,7 +60,7 @@ func TestApplyDefaultRenderStackEnvironment_AllowSplitPreservesExplicitLowLevelO | |||
| 60 | 60 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | |
| 61 | 61 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | |
| 62 | 62 | ||
| 63 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | |
| 63 | + | got := applyDefaultRenderStackEnvironment(nil) | |
| 64 | 64 | ||
| 65 | 65 | if got != renderStackVulkanDMABUF { | |
| 66 | 66 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | |
| @@ -82,7 +82,7 @@ func TestApplyDefaultRenderStackEnvironment_LegacyGLUsesGLArea(t *testing.T) { | |||
| 82 | 82 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | |
| 83 | 83 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | |
| 84 | 84 | ||
| 85 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | |
| 85 | + | got := applyDefaultRenderStackEnvironment(nil) | |
| 86 | 86 | ||
| 87 | 87 | if got != renderStackLegacyGL { | |
| 88 | 88 | t.Fatalf("render stack = %q, want %q", got, renderStackLegacyGL) | |
| @@ -96,7 +96,7 @@ func TestApplyDefaultRenderStackEnvironment_LegacyGLUsesGLArea(t *testing.T) { | |||
| 96 | 96 | } | |
| 97 | 97 | ||
| 98 | 98 | func TestNormalizeRenderStackUnknownFallsBackToGDKDMABUF(t *testing.T) { | |
| 99 | - | got := normalizeRenderStack(context.Background(), "vulkan-dmabuff") | |
| 99 | + | got := normalizeRenderStack(nil, "vulkan-dmabuff") | |
| 100 | 100 | ||
| 101 | 101 | if got != renderStackVulkanDMABUF { | |
| 102 | 102 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | |
| @@ -14,7 +14,7 @@ func TestApplyDefaultRenderStackEnvironment_DefaultsToGDKDMABUFWithANGLEVulkan(t | |||||
| 14 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | 14 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | ||
| 15 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | 15 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | ||
| 16 | 16 | ||||
| 17 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | 17 | + | got := applyDefaultRenderStackEnvironment(nil) |
| 18 | 18 | ||||
| 19 | if got != renderStackVulkanDMABUF { | 19 | if got != renderStackVulkanDMABUF { | ||
| 20 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | 20 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | ||
| @@ -37,7 +37,7 @@ func TestApplyDefaultRenderStackEnvironment_OverridesConflictingLowLevelDefaults | |||||
| 37 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | 37 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | ||
| 38 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | 38 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | ||
| 39 | 39 | ||||
| 40 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | 40 | + | got := applyDefaultRenderStackEnvironment(nil) |
| 41 | 41 | ||||
| 42 | if got != renderStackVulkanDMABUF { | 42 | if got != renderStackVulkanDMABUF { | ||
| 43 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | 43 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | ||
| @@ -60,7 +60,7 @@ func TestApplyDefaultRenderStackEnvironment_AllowSplitPreservesExplicitLowLevelO | |||||
| 60 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | 60 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "glarea") | ||
| 61 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | 61 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "gl-egl") | ||
| 62 | 62 | ||||
| 63 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | 63 | + | got := applyDefaultRenderStackEnvironment(nil) |
| 64 | 64 | ||||
| 65 | if got != renderStackVulkanDMABUF { | 65 | if got != renderStackVulkanDMABUF { | ||
| 66 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | 66 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | ||
| @@ -82,7 +82,7 @@ func TestApplyDefaultRenderStackEnvironment_LegacyGLUsesGLArea(t *testing.T) { | |||||
| 82 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | 82 | t.Setenv("PUREGO_CEF2GTK_BACKEND", "") | ||
| 83 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | 83 | t.Setenv("PUREGO_CEF2GTK_ANGLE_BACKEND", "") | ||
| 84 | 84 | ||||
| 85 | - | got := applyDefaultRenderStackEnvironment(context.Background()) | 85 | + | got := applyDefaultRenderStackEnvironment(nil) |
| 86 | 86 | ||||
| 87 | if got != renderStackLegacyGL { | 87 | if got != renderStackLegacyGL { | ||
| 88 | t.Fatalf("render stack = %q, want %q", got, renderStackLegacyGL) | 88 | t.Fatalf("render stack = %q, want %q", got, renderStackLegacyGL) | ||
| @@ -96,7 +96,7 @@ func TestApplyDefaultRenderStackEnvironment_LegacyGLUsesGLArea(t *testing.T) { | |||||
| 96 | } | 96 | } | ||
| 97 | 97 | ||||
| 98 | func TestNormalizeRenderStackUnknownFallsBackToGDKDMABUF(t *testing.T) { | 98 | func TestNormalizeRenderStackUnknownFallsBackToGDKDMABUF(t *testing.T) { | ||
| 99 | - | got := normalizeRenderStack(context.Background(), "vulkan-dmabuff") | 99 | + | got := normalizeRenderStack(nil, "vulkan-dmabuff") |
| 100 | 100 | ||||
| 101 | if got != renderStackVulkanDMABUF { | 101 | if got != renderStackVulkanDMABUF { | ||
| 102 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | 102 | t.Fatalf("render stack = %q, want %q", got, renderStackVulkanDMABUF) | ||
M internal/infrastructure/cef/render_stall_watchdog.go +4 -4
| @@ -192,10 +192,10 @@ func (wv *WebView) logRenderStall(bridge *Cef2gtkAdapter, now time.Time, diag re | |||
| 192 | 192 | Bool("cef_io_alive", classification.CEFIOAlive). | |
| 193 | 193 | Uint64("webview_id", uint64(wv.id)). | |
| 194 | 194 | Int32("browser_id", diag.browser.GetIdentifier()). | |
| 195 | - | Str("uri", logging.TruncateURL(diag.uri, logging.PermissionLogURLMaxLen)). | |
| 196 | - | Str("title", diag.title). | |
| 195 | + | Bool("has_uri", diag.uri != ""). | |
| 196 | + | Bool("has_title", diag.title != ""). | |
| 197 | 197 | Bool("is_loading", diag.isLoading). | |
| 198 | - | Str("pending_uri", logging.TruncateURL(diag.pendingURI, logging.PermissionLogURLMaxLen)). | |
| 198 | + | Bool("has_pending_uri", diag.pendingURI != ""). | |
| 199 | 199 | Bool("audio_playing", diag.audioPlaying). | |
| 200 | 200 | Int64("cef_ui_heartbeat_ack_age_ms", durationMillis(uiHeartbeat.LastAckAge)). | |
| 201 | 201 | Int64("cef_ui_heartbeat_latency_ms", durationMillis(uiHeartbeat.Latency)). | |
| @@ -233,7 +233,7 @@ func (wv *WebView) logRenderStall(bridge *Cef2gtkAdapter, now time.Time, diag re | |||
| 233 | 233 | Int("pending_schedule_failures", diag.diag.PendingScheduleFailures). | |
| 234 | 234 | Int("pending_idle_callbacks", diag.diag.PendingIdleCallbacks). | |
| 235 | 235 | Str("latest_render_event_kind", diag.latestEvent.Kind). | |
| 236 | - | Str("latest_render_event_message", diag.latestEvent.Message). | |
| 236 | + | Bool("latest_render_event_has_message", diag.latestEvent.Message != ""). | |
| 237 | 237 | Msg("cef: accelerated rendering appears stalled") | |
| 238 | 238 | } | |
| 239 | 239 | ||
| @@ -192,10 +192,10 @@ func (wv *WebView) logRenderStall(bridge *Cef2gtkAdapter, now time.Time, diag re | |||||
| 192 | Bool("cef_io_alive", classification.CEFIOAlive). | 192 | Bool("cef_io_alive", classification.CEFIOAlive). | ||
| 193 | Uint64("webview_id", uint64(wv.id)). | 193 | Uint64("webview_id", uint64(wv.id)). | ||
| 194 | Int32("browser_id", diag.browser.GetIdentifier()). | 194 | Int32("browser_id", diag.browser.GetIdentifier()). | ||
| 195 | - | Str("uri", logging.TruncateURL(diag.uri, logging.PermissionLogURLMaxLen)). | 195 | + | Bool("has_uri", diag.uri != ""). |
| 196 | - | Str("title", diag.title). | 196 | + | Bool("has_title", diag.title != ""). |
| 197 | Bool("is_loading", diag.isLoading). | 197 | Bool("is_loading", diag.isLoading). | ||
| 198 | - | Str("pending_uri", logging.TruncateURL(diag.pendingURI, logging.PermissionLogURLMaxLen)). | 198 | + | Bool("has_pending_uri", diag.pendingURI != ""). |
| 199 | Bool("audio_playing", diag.audioPlaying). | 199 | Bool("audio_playing", diag.audioPlaying). | ||
| 200 | Int64("cef_ui_heartbeat_ack_age_ms", durationMillis(uiHeartbeat.LastAckAge)). | 200 | Int64("cef_ui_heartbeat_ack_age_ms", durationMillis(uiHeartbeat.LastAckAge)). | ||
| 201 | Int64("cef_ui_heartbeat_latency_ms", durationMillis(uiHeartbeat.Latency)). | 201 | Int64("cef_ui_heartbeat_latency_ms", durationMillis(uiHeartbeat.Latency)). | ||
| @@ -233,7 +233,7 @@ func (wv *WebView) logRenderStall(bridge *Cef2gtkAdapter, now time.Time, diag re | |||||
| 233 | Int("pending_schedule_failures", diag.diag.PendingScheduleFailures). | 233 | Int("pending_schedule_failures", diag.diag.PendingScheduleFailures). | ||
| 234 | Int("pending_idle_callbacks", diag.diag.PendingIdleCallbacks). | 234 | Int("pending_idle_callbacks", diag.diag.PendingIdleCallbacks). | ||
| 235 | Str("latest_render_event_kind", diag.latestEvent.Kind). | 235 | Str("latest_render_event_kind", diag.latestEvent.Kind). | ||
| 236 | - | Str("latest_render_event_message", diag.latestEvent.Message). | 236 | + | Bool("latest_render_event_has_message", diag.latestEvent.Message != ""). |
| 237 | Msg("cef: accelerated rendering appears stalled") | 237 | Msg("cef: accelerated rendering appears stalled") | ||
| 238 | } | 238 | } | ||
| 239 | 239 | ||||
A internal/logging/port_logger.go +41 -0
| @@ -0,0 +1,41 @@ | |||
| 1 | + | package logging | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | ||
| 6 | + | "github.com/rs/zerolog" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // NewPortLogger adapts the zerolog logger stored in ctx to the application | |
| 12 | + | // logging boundary. | |
| 13 | + | func NewPortLogger(ctx context.Context) port.Logger { | |
| 14 | + | if ctx == nil { | |
| 15 | + | ctx = context.Background() | |
| 16 | + | } | |
| 17 | + | return portLogger{ctx: ctx} | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | type portLogger struct { | |
| 21 | + | ctx context.Context | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | func (l portLogger) Debug(msg string, fields ...port.LogField) { | |
| 25 | + | addLogFields(FromContext(l.ctx).Debug(), fields).Msg(msg) | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | func (l portLogger) Info(msg string, fields ...port.LogField) { | |
| 29 | + | addLogFields(FromContext(l.ctx).Info(), fields).Msg(msg) | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | func (l portLogger) Warn(msg string, fields ...port.LogField) { | |
| 33 | + | addLogFields(FromContext(l.ctx).Warn(), fields).Msg(msg) | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | func addLogFields(event *zerolog.Event, fields []port.LogField) *zerolog.Event { | |
| 37 | + | for _, field := range fields { | |
| 38 | + | event = event.Interface(field.Key, field.Value) | |
| 39 | + | } | |
| 40 | + | return event | |
| 41 | + | } | |
| @@ -0,0 +1,41 @@ | |||||
| 1 | + | package logging | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | ||||
| 6 | + | "github.com/rs/zerolog" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // NewPortLogger adapts the zerolog logger stored in ctx to the application | |||
| 12 | + | // logging boundary. | |||
| 13 | + | func NewPortLogger(ctx context.Context) port.Logger { | |||
| 14 | + | if ctx == nil { | |||
| 15 | + | ctx = context.Background() | |||
| 16 | + | } | |||
| 17 | + | return portLogger{ctx: ctx} | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | type portLogger struct { | |||
| 21 | + | ctx context.Context | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | func (l portLogger) Debug(msg string, fields ...port.LogField) { | |||
| 25 | + | addLogFields(FromContext(l.ctx).Debug(), fields).Msg(msg) | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | func (l portLogger) Info(msg string, fields ...port.LogField) { | |||
| 29 | + | addLogFields(FromContext(l.ctx).Info(), fields).Msg(msg) | |||
| 30 | + | } | |||
| 31 | + | ||||
| 32 | + | func (l portLogger) Warn(msg string, fields ...port.LogField) { | |||
| 33 | + | addLogFields(FromContext(l.ctx).Warn(), fields).Msg(msg) | |||
| 34 | + | } | |||
| 35 | + | ||||
| 36 | + | func addLogFields(event *zerolog.Event, fields []port.LogField) *zerolog.Event { | |||
| 37 | + | for _, field := range fields { | |||
| 38 | + | event = event.Interface(field.Key, field.Value) | |||
| 39 | + | } | |||
| 40 | + | return event | |||
| 41 | + | } | |||
M internal/ui/input/global_shortcuts.go +3 -0
| @@ -373,6 +373,9 @@ func (h *GlobalShortcutHandler) ReloadShortcuts(ctx context.Context, workspace * | |||
| 373 | 373 | // but we clear our references here. | |
| 374 | 374 | func (h *GlobalShortcutHandler) Detach() { | |
| 375 | 375 | h.generation++ | |
| 376 | + | if h.window != nil && h.controller != nil { | |
| 377 | + | h.window.RemoveController(&h.controller.EventController) | |
| 378 | + | } | |
| 376 | 379 | h.controller = nil | |
| 377 | 380 | h.callbacks = nil | |
| 378 | 381 | h.registered = nil | |
| @@ -373,6 +373,9 @@ func (h *GlobalShortcutHandler) ReloadShortcuts(ctx context.Context, workspace * | |||||
| 373 | // but we clear our references here. | 373 | // but we clear our references here. | ||
| 374 | func (h *GlobalShortcutHandler) Detach() { | 374 | func (h *GlobalShortcutHandler) Detach() { | ||
| 375 | h.generation++ | 375 | h.generation++ | ||
| 376 | + | if h.window != nil && h.controller != nil { | |||
| 377 | + | h.window.RemoveController(&h.controller.EventController) | |||
| 378 | + | } | |||
| 376 | h.controller = nil | 379 | h.controller = nil | ||
| 377 | h.callbacks = nil | 380 | h.callbacks = nil | ||
| 378 | h.registered = nil | 381 | h.registered = nil | ||
M scripts/cef-gpu-diagnostics.sh +10 -1
| @@ -49,6 +49,14 @@ require_value() { | |||
| 49 | 49 | fi | |
| 50 | 50 | } | |
| 51 | 51 | ||
| 52 | + | validate_devtools_port() { | |
| 53 | + | local value="$1" | |
| 54 | + | if [[ ! "$value" =~ ^[1-9][0-9]*$ ]] || (( value < 1 || value > 65535 )); then | |
| 55 | + | echo "--devtools-port must be an integer between 1 and 65535" >&2 | |
| 56 | + | exit 2 | |
| 57 | + | fi | |
| 58 | + | } | |
| 59 | + | ||
| 52 | 60 | while [[ $# -gt 0 ]]; do | |
| 53 | 61 | case "$1" in | |
| 54 | 62 | -p|--pid) require_value "$1" "${2:-}"; pid="$2"; shift 2 ;; | |
| @@ -58,7 +66,7 @@ while [[ $# -gt 0 ]]; do | |||
| 58 | 66 | --session-log) require_value "$1" "${2:-}"; session_log="$2"; shift 2 ;; | |
| 59 | 67 | --codec-stats) require_value "$1" "${2:-}"; codec_stats="$2"; shift 2 ;; | |
| 60 | 68 | --codec-stats-file) require_value "$1" "${2:-}"; codec_stats_file="$2"; shift 2 ;; | |
| 61 | - | --devtools-port) require_value "$1" "${2:-}"; devtools_port="$2"; shift 2 ;; | |
| 69 | + | --devtools-port) require_value "$1" "${2:-}"; validate_devtools_port "$2"; devtools_port="$2"; shift 2 ;; | |
| 62 | 70 | -h|--help) usage; exit 0 ;; | |
| 63 | 71 | *) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;; | |
| 64 | 72 | esac | |
| @@ -188,6 +196,7 @@ EOF | |||
| 188 | 196 | fi | |
| 189 | 197 | ||
| 190 | 198 | if [[ -n "$devtools_port" ]]; then | |
| 199 | + | validate_devtools_port "$devtools_port" | |
| 191 | 200 | log "capturing DevTools metadata from port $devtools_port" | |
| 192 | 201 | if ! curl --connect-timeout 1 --max-time 3 -fsS "http://127.0.0.1:${devtools_port}/json/version" > "$out_dir/devtools_version.json" 2> "$out_dir/devtools.stderr"; then | |
| 193 | 202 | log "failed to fetch DevTools version metadata from port $devtools_port" | |
| @@ -49,6 +49,14 @@ require_value() { | |||||
| 49 | fi | 49 | fi | ||
| 50 | } | 50 | } | ||
| 51 | 51 | ||||
| 52 | + | validate_devtools_port() { | |||
| 53 | + | local value="$1" | |||
| 54 | + | if [[ ! "$value" =~ ^[1-9][0-9]*$ ]] || (( value < 1 || value > 65535 )); then | |||
| 55 | + | echo "--devtools-port must be an integer between 1 and 65535" >&2 | |||
| 56 | + | exit 2 | |||
| 57 | + | fi | |||
| 58 | + | } | |||
| 59 | + | ||||
| 52 | while [[ $# -gt 0 ]]; do | 60 | while [[ $# -gt 0 ]]; do | ||
| 53 | case "$1" in | 61 | case "$1" in | ||
| 54 | -p|--pid) require_value "$1" "${2:-}"; pid="$2"; shift 2 ;; | 62 | -p|--pid) require_value "$1" "${2:-}"; pid="$2"; shift 2 ;; | ||
| @@ -58,7 +66,7 @@ while [[ $# -gt 0 ]]; do | |||||
| 58 | --session-log) require_value "$1" "${2:-}"; session_log="$2"; shift 2 ;; | 66 | --session-log) require_value "$1" "${2:-}"; session_log="$2"; shift 2 ;; | ||
| 59 | --codec-stats) require_value "$1" "${2:-}"; codec_stats="$2"; shift 2 ;; | 67 | --codec-stats) require_value "$1" "${2:-}"; codec_stats="$2"; shift 2 ;; | ||
| 60 | --codec-stats-file) require_value "$1" "${2:-}"; codec_stats_file="$2"; shift 2 ;; | 68 | --codec-stats-file) require_value "$1" "${2:-}"; codec_stats_file="$2"; shift 2 ;; | ||
| 61 | - | --devtools-port) require_value "$1" "${2:-}"; devtools_port="$2"; shift 2 ;; | 69 | + | --devtools-port) require_value "$1" "${2:-}"; validate_devtools_port "$2"; devtools_port="$2"; shift 2 ;; |
| 62 | -h|--help) usage; exit 0 ;; | 70 | -h|--help) usage; exit 0 ;; | ||
| 63 | *) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;; | 71 | *) echo "unknown argument: $1" >&2; usage >&2; exit 2 ;; | ||
| 64 | esac | 72 | esac | ||
| @@ -188,6 +196,7 @@ EOF | |||||
| 188 | fi | 196 | fi | ||
| 189 | 197 | ||||
| 190 | if [[ -n "$devtools_port" ]]; then | 198 | if [[ -n "$devtools_port" ]]; then | ||
| 199 | + | validate_devtools_port "$devtools_port" | |||
| 191 | log "capturing DevTools metadata from port $devtools_port" | 200 | log "capturing DevTools metadata from port $devtools_port" | ||
| 192 | if ! curl --connect-timeout 1 --max-time 3 -fsS "http://127.0.0.1:${devtools_port}/json/version" > "$out_dir/devtools_version.json" 2> "$out_dir/devtools.stderr"; then | 201 | if ! curl --connect-timeout 1 --max-time 3 -fsS "http://127.0.0.1:${devtools_port}/json/version" > "$out_dir/devtools_version.json" 2> "$out_dir/devtools.stderr"; then | ||
| 193 | log "failed to fetch DevTools version metadata from port $devtools_port" | 202 | log "failed to fetch DevTools version metadata from port $devtools_port" | ||