Collect the logs of the local Docker containers
- docker.go follows every running container through the Docker API (events + logs with follow), resumes after a restart from the last position saved in /data/docker-state.json, reads DOCKER_BACKFILL (1h) of history for new containers, strips terminal color codes and guesses the severity from the line (JSON, logfmt, [ERROR], ERROR ...). - Logs carry source_type=docker, container, container_id, image, compose_project, compose_service and stream; host is the Docker host. - Settings > Sources: one switch per container (grouped by compose project), enable/disable all, follow new containers automatically. Choices are saved per compose service in /data/docker.json. - Source filter (syslog / docker) in the filter bar and the live view. - docker-compose: read-only docker-socket-proxy; Logstream and the proxy are labelled logstream.exclude=true and never collected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aad0fb7c16
commit
085095c46f
12 files changed
+974
-10
No files matched your search
@@ -19,6 +19,7 @@ type API struct {
|
||||
rdns *ReverseDNS
|
||||
allowPurge bool
|
||||
exportMax int
|
||||
docker *DockerManager // nil when DOCKER_LOGS is off
|
||||
}
|
||||
|
||||
func (a *API) Routes(mux *http.ServeMux) {
|
||||
@@ -36,6 +37,38 @@ func (a *API) Routes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /api/purge", a.purgeStatus)
|
||||
mux.HandleFunc("POST /api/purge", a.purge)
|
||||
mux.HandleFunc("GET /api/export.csv", a.exportCSV)
|
||||
mux.HandleFunc("GET /api/docker", a.dockerStatus)
|
||||
mux.HandleFunc("PUT /api/docker", a.dockerConfigure)
|
||||
}
|
||||
|
||||
// GET /api/docker: Docker source state and containers (Settings > Sources).
|
||||
func (a *API) dockerStatus(w http.ResponseWriter, r *http.Request) {
|
||||
if a.docker == nil {
|
||||
writeJSON(w, http.StatusOK, map[string]any{"enabled": false})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, a.docker.Status())
|
||||
}
|
||||
|
||||
// PUT /api/docker {"defaultEnabled": bool, "containers": {"project/service": bool}}
|
||||
func (a *API) dockerConfigure(w http.ResponseWriter, r *http.Request) {
|
||||
if a.docker == nil {
|
||||
writeErr(w, http.StatusConflict, &codedError{code: "docker_off", msg: "the Docker source is disabled (DOCKER_LOGS=off)"})
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
DefaultEnabled *bool `json:"defaultEnabled"`
|
||||
Containers map[string]bool `json:"containers"`
|
||||
}
|
||||
if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 256*1024)).Decode(&body); err != nil {
|
||||
writeErr(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
if err := a.docker.Configure(body.DefaultEnabled, body.Containers); err != nil {
|
||||
writeErr(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, a.docker.Status())
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||||
|
||||
Reference in new issue
Block a user