diff --git a/app.go b/app.go index a144d4d..066372d 100644 --- a/app.go +++ b/app.go @@ -997,6 +997,7 @@ func (a *App) startup(ctx context.Context) { // One line in the startup log too, not only in OpsLog's own: the two // together say whether a launch that produced no window got as far as our // code at all, which is the fork every "it does not start" report turns on. + startupReached.Store(true) bootLog("OnStartup reached") dataDir, err := userDataDir() diff --git a/bootlog.go b/bootlog.go index 26bcbf3..80711d1 100644 --- a/bootlog.go +++ b/bootlog.go @@ -19,6 +19,7 @@ import ( "os" "path/filepath" "strings" + "sync/atomic" "time" ) @@ -93,3 +94,18 @@ func checkDataDirWritable() error { bootLog("data dir ok: %s", dir) return nil } + +// startupReached flips as soon as OnStartup runs — the first moment OpsLog's +// own code is executing inside the window's lifecycle. +var startupReached atomic.Bool + +// startupStuckMessage is what an operator sees when the window never starts. +// +// It names the two causes and what to do about each, because "OpsLog is not +// responding" sends people to reinstall OpsLog, which is the one thing that +// cannot help: the part that has not started is not ours. +const startupStuckMessage = "OpsLog started but its window never opened.\n\n" + + "This is the WebView2 runtime failing to start, and it is almost always one of two things:\n\n" + + "• The Microsoft Edge WebView2 Runtime is missing — install it from Microsoft, then start OpsLog again.\n" + + "• An antivirus is blocking msedgewebview2.exe — add OpsLog's folder to its exceptions.\n\n" + + "The details are in the OpsLog folder under LOCALAPPDATA (startup.log)." diff --git a/main.go b/main.go index 96f5215..8041dc0 100644 --- a/main.go +++ b/main.go @@ -145,6 +145,23 @@ func main() { } // Create application with options + // A HANG has no error to report, and that is the shape being chased here: + // the process stays up, the data folder is created and stays empty, and no + // window appears — WebView2 never finishes creating its environment, and + // nothing returns to be logged. + // + // So the absence of progress is what gets reported. If OnStartup has not + // been reached a few seconds in, this says so and names the two things that + // cause it, rather than leaving an operator watching a process that is doing + // nothing at all. + go func() { + time.Sleep(12 * time.Second) + if startupReached.Load() { + return + } + bootLog("STUCK: 12 s after wails.Run and the window has not started — WebView2 never handed control back") + fatalBox("OpsLog", startupStuckMessage) + }() bootLog("entering wails.Run (window %dx%d, state %v)", width, height, startState) err := wails.Run(&options.App{ Title: "OpsLog",