--- name: docker_container_logs kind: function lang: go domain: infra version: "2.0.0" purity: impure signature: "func DockerContainerLogs(opts DockerLogsOpts) ([]DockerLogLine, error)" description: "Tail/grep logs de container Docker via engine API. Snapshot (N lineas) o streaming (callback por linea con context cancel). Demux frame stdout/stderr. Capability docker.container.logs del device_agent." tags: [docker, docker-agent, logs, streaming, infra] uses_functions: [] uses_types: - docker_logs_opts_go_infra - docker_log_line_go_infra - error_go_core returns: - docker_log_line_go_infra returns_optional: false error_type: "error_go_core" imports: - context - encoding/binary - fmt - io - net - net/http - net/url - strings - time params: - name: opts desc: "Parametros de la peticion: container ID, tail N, since, stdout/stderr, timestamps, docker host. Ver DockerLogsOpts." output: "Slice de DockerLogLine con stream (stdout/stderr), timestamp RFC3339 opcional y texto de la linea." tested: true tests: - "snapshot stdout y stderr demuxeados" - "container no encontrado retorna error" - "timestamps parseados del prefijo Docker" - "tail y since se envian como query params" - "streaming recibe lineas via callback" - "ctx cancel detiene el stream" - "callback error cancela el stream" - "frame stdout decodificado correctamente" - "frame stderr decodificado correctamente" test_file_path: "functions/infra/docker_container_logs_test.go" file_path: "functions/infra/docker_container_logs.go" --- ## Ejemplo ```go // Snapshot: ultimas 50 lineas de stdout+stderr lines, err := DockerContainerLogs(infra.DockerLogsOpts{ ContainerID: "registry_api", Tail: 50, Since: "10m", Stdout: true, Stderr: true, Timestamps: true, }) if err != nil { log.Fatal(err) } for _, l := range lines { fmt.Printf("[%s] %s %s\n", l.Stream, l.Timestamp, l.Line) } // Streaming: follow hasta cancelacion ctx, cancel := context.WithCancel(context.Background()) defer cancel() err = infra.DockerContainerLogsStream(ctx, infra.DockerLogsOpts{ ContainerID: "registry_api", Stdout: true, Stderr: true, }, func(line infra.DockerLogLine) error { fmt.Printf("[%s] %s\n", line.Stream, line.Line) if strings.Contains(line.Line, "FATAL") { return fmt.Errorf("fatal error detectado") } return nil }) ``` ## Cuando usarla Cuando el device_agent necesite leer o monitorizar logs de un container Docker en tiempo real. Usar modo snapshot para health checks puntuales (N ultimas lineas). Usar streaming para tail -f reactivo con procesamiento por linea. ## Gotchas - Containers sin `--tty` usan el protocolo de multiplexion de 8 bytes — esta funcion lo demuxea correctamente. Containers con `--tty` mezclan stdout/stderr en un stream plano sin headers, lo que puede dar `Stream = "stdout"` para todo o parsear mal el header (byte 0 podria ser el primer caracter de texto). - Streaming consume una goroutine/conexion hasta que `ctx` se cancele o `cb` retorne error. El caller es responsable del ciclo de vida del contexto. - `Since` acepta unix timestamp en string, RFC3339 o duracion Go ("10m", "1h30m"). El daemon Docker acepta los 3 formatos directamente. - Sin reconexion automatica en streaming. Si el daemon reinicia o la conexion se corta, el caller recibe error y decide si reintentar. - `DockerHost` vacio conecta a `/var/run/docker.sock`. En sistemas donde el socket esta en otra ruta (Docker Desktop macOS, Podman), pasar la URL explicitamente. ## Capability growth log v2.0.0 (2026-05-23) — reemplaza implementacion CLI (exec docker logs) por engine API HTTP con demux de frames. Anade DockerLogsOpts, DockerLogLine, modo streaming con callback y ctx cancel. Consumidor nordvpn_container_start actualizado.