如何在 Go 程序中用 Compose SDK 的 EventProcessor 捕获操作进度并选择输出渲染器?
【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/compose
当你在自己的 Go 程序里嵌入 Compose SDK(github.com/docker/compose/v5)来执行up、down、build这类操作时,SDK 对 Docker 资源(镜像、容器、卷、网络)的每一次改动默认只发生在后台,程序拿不到中间进度。要捕获这些进度并决定“打印成文本”还是“输出成 JSON 供下游解析”,做法是在NewComposeService时传入compose.WithEventProcessor(...)选项,再从cmd/display包中选择一个内置渲染器(或自己实现api.EventProcessor接口)。
前提:先按标准方式创建 Compose SDK 服务
事件处理是挂在 Compose service 实例上的,所以先要有可用的 service。按 docs/sdk.md 的示例,初始化顺序是:创建 Docker CLI、初始化、创建 service:
package main import ( "context" "log" "os" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/flags" "github.com/docker/compose/v5/cmd/display" "github.com/docker/compose/v5/pkg/api" "github.com/docker/compose/v5/pkg/compose" ) func main() { ctx := context.Background() dockerCLI, err := command.NewDockerCli() if err != nil { log.Fatalf("Failed to create docker CLI: %v", err) } err = dockerCLI.Initialize(&flags.ClientOptions{}) if err != nil { log.Fatalf("Failed to initialize docker CLI: %v", err) } // 传入 WithEventProcessor,捕获操作进度 service, err := compose.NewComposeService(dockerCLI, compose.WithEventProcessor(display.JSON(os.Stdout)), ) if err != nil { log.Fatalf("Failed to create compose service: %v", err) } project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ ConfigPaths: []string{"compose.yaml"}, ProjectName: "my-app", }) if err != nil { log.Fatalf("Failed to load project: %v", err) } err = service.Up(ctx, project, api.UpOptions{ Create: api.CreateOptions{}, Start: api.StartOptions{}, }) if err != nil { log.Fatalf("Failed to start services: %v", err) } }其中ConfigPaths和ProjectName是示例值,替换为你自己的 compose 文件路径与项目名;compose.yaml需位于程序工作目录。完整示例代码见 docs/examples/sdk/main.go 与 docs/examples/sdk/options.go,这两个文件与docs/sdk.md中的代码块由测试锁定保持一致。
一个必须知道的行为:如果不传WithEventProcessor选项,事件会被静默丢弃(见 docs/sdk.md “Built-inEventProcessorimplementations” 一节)。所以“想拿到进度”时这一步是必做的,不是可选项。
EventProcessor 接口的三个回调
EventProcessor定义在github.com/docker/compose/v5/pkg/api,源码见 pkg/api/event.go。一个 Compose 操作(如up、down、build)会对 Docker 资源做一系列变更,SDK 通过三个方法通知你:
Start(ctx, operation)—— 操作开始时调用,operation例如up;On(events...)—— 针对单个资源变更的进度事件,例如容器正在启动、镜像正在拉取;Done(operation, success)—— 操作结束时调用,success指示成功还是失败。
每个事件是一个api.Resource,包含资源 ID(ID)、父资源(ParentID)、状态文本(Text)、细节(Details)、状态枚举(Status),以及进度指标Current、Total、Percent(镜像拉取这类操作会带下载进度)。Status的取值为四种:
| 状态 | 含义 |
|---|---|
Working | 操作进行中,例如 creating、starting、pulling |
Done | 操作成功完成 |
Warning | 完成但带有警告 |
Error | 操作失败 |
Text字段常见的状态文本包括Creating、Created、Starting、Started、Running、Stopping、Stopped、Removing、Removed、Building、Built、Pulling、Pulled等,完整常量列表见 pkg/api/event.go 中的Status*定义。
选择输出渲染器:cmd/display里的四个构造函数
Docker Compose CLI 自己用的渲染器位于github.com/docker/compose/v5/cmd/display包,SDK 用户可以直接复用。对应关系如下(来源:docs/sdk.md):
| 构造函数 | 输出形式 | 适用场景 |
|---|---|---|
display.Full(out, info io.Writer, detached bool) | 带进度条和任务列表的交互式终端 UI(Docker Compose CLI 的默认输出) | 直接面向终端的用户界面 |
display.Plain(out io.Writer) | 简单文本进度消息 | 非交互环境或写入日志文件 |
display.JSON(out io.Writer) | 每个事件一个 JSON 对象 | 供程序解析的机器可读流 |
display.Quiet() | 静默丢弃事件 | 与不传选项的默认行为相同 |
选择依据只有一条:你的程序输出流向哪里。写终端选Full;写日志或管道选Plain;要被下游程序逐行解析选JSON;不需要进度输出选Quiet()(或不传选项)。
Plain 渲染器的输出内容
cmd/display/plain.go 中,Plain对每个事件输出一行e.ID、e.Text、e.Details。它不区分操作阶段,Start和Done回调为空实现,即 Plain 只反映资源级事件,不额外打印“操作开始/结束”行。
JSON 渲染器的输出结构
cmd/display/json.go 中,JSON对每个事件输出一行 JSON 对象,字段来自api.Resource:id、parent_id、status(取值为StatusText()的Working/Warning/Done/Error)、text、details、current、total、percent,另带dry-run和tail标记字段。序列化失败的单条事件会被跳过,不会中断整个输出。下游程序可以按行读取os.Stdout并用encoding/json反序列化来跟踪进度。
自己实现 EventProcessor(可选分支)
如果内置渲染器不满足需求(例如要把进度上报到自己的监控系统),文档明确支持自接 UI:“UsingEventProcessor, a custom UI can be plugged intodocker/compose.” 做法是实现api.EventProcessor的三个方法后传入compose.WithEventProcessor(...):
type myProcessor struct{} func (m myProcessor) Start(ctx context.Context, operation string) { log.Printf("operation %s started", operation) } func (m myProcessor) On(events ...api.Resource) { for _, e := range events { log.Printf("resource %s: %s (%s)", e.ID, e.Text, e.StatusText()) } } func (m myProcessor) Done(operation string, success bool) { log.Printf("operation %s done: %v", operation, success) }事件里带Current/Total/Percent时(典型是镜像拉取),可以据此上报百分比进度。
验证与限制
验证方式:
- 用
display.JSON(os.Stdout)或display.Plain(os.Stdout)运行service.Up(...)后,标准输出应有逐行的进度输出,且每行 JSON 可被encoding/json解析、status字段落在Working/Warning/Done/Error四个取值内; - 自定义
EventProcessor的验证点是Done回调收到的success参数与service.Up返回的 error 一致地反映操作结果。
需要注意的限制:
- 不传
WithEventProcessor时事件被静默丢弃,不会有任何输出或报错提示; - 渲染器的构造函数签名需以源码为准:
Full需要三个参数(out、info、detached bool),Plain与JSON各需要一个io.Writer,Quiet()无参数(见 cmd/display/tty.go 与 docs/sdk.md); WithEventProcessor与其它compose.Option(如WithOutputStream、WithMaxConcurrency、WithPrompt)可自由组合,事件处理器只负责进度事件,不影响标准输出流的去向。
完成接入后的自然延伸是 docs/sdk.md “Customizing the SDK” 一节列出的其余选项:结合WithDryRun先做不落盘的演练,用WithOutputStream/WithErrorStream把日志与进度分流到不同目标。
【免费下载链接】composeDefine and run multi-container applications with Docker项目地址: https://gitcode.com/GitHub_Trending/compose/compose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考