44 "archive/tar"
55 "bytes"
66 "context"
7+ "encoding/binary"
78 "errors"
89 "fmt"
910 "github.com/docker/docker/api/types/container"
@@ -14,10 +15,15 @@ import (
1415 "io"
1516 "os"
1617 "os/exec"
18+ "path/filepath"
1719 "strings"
1820 "sync"
1921)
2022
23+ const (
24+ DefaultCTFLogsDir = "logs"
25+ )
26+
2127func IsDockerRunning () bool {
2228 cli , err := client .NewClientWithOpts (client .FromEnv )
2329 if err != nil {
@@ -223,3 +229,69 @@ func (dc *DockerClient) copyToContainer(containerID, sourceFile, targetPath stri
223229 }
224230 return nil
225231}
232+
233+ // WriteAllContainersLogs writes all docker containers logs to default logs directory
234+ func WriteAllContainersLogs () error {
235+ L .Info ().Msg ("Writing docker containers logs" )
236+ if _ , err := os .Stat (DefaultCTFLogsDir ); os .IsNotExist (err ) {
237+ if err := os .MkdirAll (DefaultCTFLogsDir , 0755 ); err != nil {
238+ return fmt .Errorf ("failed to create directory %s: %w" , DefaultCTFLogsDir , err )
239+ }
240+ }
241+ cli , err := client .NewClientWithOpts (client .FromEnv , client .WithAPIVersionNegotiation ())
242+ if err != nil {
243+ return fmt .Errorf ("failed to create Docker client: %w" , err )
244+ }
245+ conts , err := cli .ContainerList (context .Background (), container.ListOptions {
246+ All : true ,
247+ })
248+ if err != nil {
249+ return err
250+ }
251+ for _ , cont := range conts {
252+ logOptions := container.LogsOptions {ShowStdout : true , ShowStderr : true }
253+ logs , err := cli .ContainerLogs (context .Background (), cont .Names [0 ], logOptions )
254+ if err != nil {
255+ L .Error ().Err (err ).Str ("Container" , cont .Names [0 ]).Msg ("failed to fetch logs for container" )
256+ continue
257+ }
258+ logFilePath := filepath .Join (DefaultCTFLogsDir , fmt .Sprintf ("%s.log" , cont .Names [0 ]))
259+ logFile , err := os .Create (logFilePath )
260+ if err != nil {
261+ L .Error ().Err (err ).Str ("Container" , cont .Names [0 ]).Msg ("failed to create container log file" )
262+ continue
263+ }
264+
265+ // Read and parse logs
266+ header := make ([]byte , 8 ) // Docker stream header is 8 bytes
267+ for {
268+ // Read the header
269+ _ , err := io .ReadFull (logs , header )
270+ if err == io .EOF {
271+ break // End of logs
272+ }
273+ if err != nil {
274+ L .Error ().Err (err ).Str ("Container" , cont .Names [0 ]).Msg ("failed to read log stream header" )
275+ break
276+ }
277+
278+ // Extract log message size from the header
279+ msgSize := binary .BigEndian .Uint32 (header [4 :8 ])
280+
281+ // Read the log message
282+ msg := make ([]byte , msgSize )
283+ _ , err = io .ReadFull (logs , msg )
284+ if err != nil {
285+ L .Error ().Err (err ).Str ("Container" , cont .Names [0 ]).Msg ("failed to read log message" )
286+ break
287+ }
288+
289+ // Write the log message to the file
290+ if _ , err := logFile .Write (msg ); err != nil {
291+ L .Error ().Err (err ).Str ("Container" , cont .Names [0 ]).Msg ("failed to write log message to file" )
292+ break
293+ }
294+ }
295+ }
296+ return nil
297+ }
0 commit comments