diff --git a/diagnostic/consts.go b/diagnostic/consts.go index 1a78c888..fb6cb3bd 100644 --- a/diagnostic/consts.go +++ b/diagnostic/consts.go @@ -3,12 +3,14 @@ package diagnostic import "time" const ( - defaultCollectorTimeout = time.Second * 10 // This const define the timeout value of a collector operation. - collectorField = "collector" // used for logging purposes - systemCollectorName = "system" // used for logging purposes - tunnelStateCollectorName = "tunnelState" // used for logging purposes - configurationCollectorName = "configuration" // used for logging purposes - defaultTimeout = 15 * time.Second // timeout for the collectors - twoWeeksOffset = -14 * 24 * time.Hour // maximum offset for the logs - configurationKeyUID = "uid" // Key used to set and get the UID value from the configuration map + defaultCollectorTimeout = time.Second * 10 // This const define the timeout value of a collector operation. + collectorField = "collector" // used for logging purposes + systemCollectorName = "system" // used for logging purposes + tunnelStateCollectorName = "tunnelState" // used for logging purposes + configurationCollectorName = "configuration" // used for logging purposes + defaultTimeout = 15 * time.Second // timeout for the collectors + twoWeeksOffset = -14 * 24 * time.Hour // maximum offset for the logs + logFilename = "cloudflared_logs.txt" // name of the output log file + configurationKeyUID = "uid" // Key used to set and get the UID value from the configuration map + tailMaxNumberOfLines = "10000" // maximum number of log lines from a virtual runtime (docker or kubernetes) ) diff --git a/diagnostic/log_collector_docker.go b/diagnostic/log_collector_docker.go new file mode 100644 index 00000000..b14bf41e --- /dev/null +++ b/diagnostic/log_collector_docker.go @@ -0,0 +1,83 @@ +package diagnostic + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "time" +) + +type DockerLogCollector struct { + containerID string // This member identifies the container by identifier or name +} + +func NewDockerLogCollector(containerID string) *DockerLogCollector { + return &DockerLogCollector{ + containerID, + } +} + +func (collector *DockerLogCollector) Collect(ctx context.Context) (*LogInformation, error) { + tmp := os.TempDir() + + outputHandle, err := os.Create(filepath.Join(tmp, logFilename)) + if err != nil { + return nil, fmt.Errorf("error opening output file: %w", err) + } + + defer outputHandle.Close() + + // Calculate 2 weeks ago + since := time.Now().Add(twoWeeksOffset).Format(time.RFC3339) + + command := exec.CommandContext( + ctx, + "docker", + "logs", + "--tail", + tailMaxNumberOfLines, + "--since", + since, + collector.containerID, + ) + + stdoutReader, err := command.StdoutPipe() + if err != nil { + return nil, fmt.Errorf( + "error retrieving output from command '%s': %w", + command.String(), + err, + ) + } + + if err := command.Start(); err != nil { + return nil, fmt.Errorf( + "error running command '%s': %w", + command.String(), + err, + ) + } + + _, err = io.Copy(outputHandle, stdoutReader) + if err != nil { + return nil, fmt.Errorf( + "error copying output from %s to file %s: %w", + command.String(), + outputHandle.Name(), + err, + ) + } + + if err := command.Wait(); err != nil { + return nil, fmt.Errorf( + "error waiting from command '%s': %w", + command.String(), + err, + ) + } + + return NewLogInformation(outputHandle.Name(), true, false), nil +}