webterm: add docker watcher event logging

Add structured server-side logs for Docker watcher lifecycle, event stream connectivity/decoding failures, start/die event handling, and container add/remove actions.

This complements HTTP/WebSocket request logs with Docker-specific observability for debugging dynamic tile/session behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
GitHub Copilot
2026-02-14 18:48:30 +00:00
parent 1cfced1052
commit a49c4d8718
+14
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@@ -111,6 +112,7 @@ func (w *DockerWatcher) addContainer(container map[string]any) {
w.mu.Unlock() w.mu.Unlock()
w.sessionManager.AddApp(name, command, slug, true, theme) w.sessionManager.AddApp(name, command, slug, true, theme)
log.Printf("docker event: added container id=%s slug=%s name=%s", containerID, slug, name)
if w.onContainerAdded != nil { if w.onContainerAdded != nil {
w.onContainerAdded(slug, name, command) w.onContainerAdded(slug, name, command)
} }
@@ -135,6 +137,7 @@ func (w *DockerWatcher) removeContainer(containerID string) {
w.sessionManager.CloseSession(sessionID) w.sessionManager.CloseSession(sessionID)
} }
w.sessionManager.RemoveApp(slug) w.sessionManager.RemoveApp(slug)
log.Printf("docker event: removed container id=%s slug=%s", containerID, slug)
if w.onContainerRemoved != nil { if w.onContainerRemoved != nil {
w.onContainerRemoved(slug) w.onContainerRemoved(slug)
} }
@@ -174,13 +177,16 @@ func (w *DockerWatcher) handleEvent(event map[string]any) {
} }
switch action { switch action {
case "start": case "start":
log.Printf("docker event: action=start container=%s", containerID)
path := fmt.Sprintf("/containers/%s/json", url.PathEscape(containerID)) path := fmt.Sprintf("/containers/%s/json", url.PathEscape(containerID))
status, body, err := unixJSONRequest(w.socketPath, http.MethodGet, path, nil) status, body, err := unixJSONRequest(w.socketPath, http.MethodGet, path, nil)
if err != nil || status != http.StatusOK { if err != nil || status != http.StatusOK {
log.Printf("docker event: start inspect failed container=%s status=%d err=%v", containerID, status, err)
return return
} }
var detail map[string]any var detail map[string]any
if err := json.Unmarshal(body, &detail); err != nil { if err := json.Unmarshal(body, &detail); err != nil {
log.Printf("docker event: start decode failed container=%s err=%v", containerID, err)
return return
} }
config := toAnyMap(detail["Config"]) config := toAnyMap(detail["Config"])
@@ -195,6 +201,7 @@ func (w *DockerWatcher) handleEvent(event map[string]any) {
} }
w.addContainer(container) w.addContainer(container)
case "die": case "die":
log.Printf("docker event: action=die container=%s", containerID)
w.removeContainer(containerID) w.removeContainer(containerID)
} }
} }
@@ -212,6 +219,7 @@ func (w *DockerWatcher) watchEvents(ctx context.Context, waitDone chan struct{})
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil) req, _ := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
resp, err := w.httpClient.Do(req) resp, err := w.httpClient.Do(req)
if err != nil { if err != nil {
log.Printf("docker event stream connect failed err=%v", err)
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
@@ -219,10 +227,12 @@ func (w *DockerWatcher) watchEvents(ctx context.Context, waitDone chan struct{})
continue continue
} }
} }
log.Printf("docker event stream connected")
decoder := json.NewDecoder(resp.Body) decoder := json.NewDecoder(resp.Body)
for { for {
var event map[string]any var event map[string]any
if err := decoder.Decode(&event); err != nil { if err := decoder.Decode(&event); err != nil {
log.Printf("docker event stream decode error err=%v", err)
break break
} }
w.handleEvent(event) w.handleEvent(event)
@@ -239,8 +249,10 @@ func (w *DockerWatcher) watchEvents(ctx context.Context, waitDone chan struct{})
func (w *DockerWatcher) ScanExisting() { func (w *DockerWatcher) ScanExisting() {
containers, err := w.listLabeledContainers() containers, err := w.listLabeledContainers()
if err != nil { if err != nil {
log.Printf("docker scan failed err=%v", err)
return return
} }
log.Printf("docker scan found %d labeled container(s)", len(containers))
for _, container := range containers { for _, container := range containers {
w.addContainer(container) w.addContainer(container)
} }
@@ -258,6 +270,7 @@ func (w *DockerWatcher) Start() {
w.waitDone = waitDone w.waitDone = waitDone
w.running = true w.running = true
w.mu.Unlock() w.mu.Unlock()
log.Printf("docker watcher started")
w.ScanExisting() w.ScanExisting()
go w.watchEvents(ctx, waitDone) go w.watchEvents(ctx, waitDone)
} }
@@ -276,4 +289,5 @@ func (w *DockerWatcher) Stop() {
cancel() cancel()
} }
<-waitDone <-waitDone
log.Printf("docker watcher stopped")
} }