11package commands
22
33import (
4+ "bufio"
45 "context"
56 "errors"
67 "fmt"
78 "io"
89 "os"
910 "os/signal"
1011 "path/filepath"
12+ "regexp"
1113 "runtime"
14+ "time"
1215
1316 "github.com/docker/docker/api/types/container"
1417 "github.com/docker/docker/pkg/stdcopy"
@@ -60,24 +63,39 @@ func newLogsCmd() *cobra.Command {
6063 return err
6164 }
6265
63- var logFilePath string
66+ var serviceLogPath string
67+ var runtimeLogPath string
6468 switch {
6569 case runtime .GOOS == "darwin" :
66- logFilePath = filepath .Join (homeDir , "Library/Containers/com.docker.docker/Data/log/host/inference.log" )
70+ serviceLogPath = filepath .Join (homeDir , "Library/Containers/com.docker.docker/Data/log/host/inference.log" )
71+ runtimeLogPath = filepath .Join (homeDir , "Library/Containers/com.docker.docker/Data/log/host/inference-llama.cpp-server.log" )
6772 case runtime .GOOS == "windows" :
68- logFilePath = filepath .Join (homeDir , "AppData/Local/Docker/log/host/inference.log" )
73+ serviceLogPath = filepath .Join (homeDir , "AppData/Local/Docker/log/host/inference.log" )
74+ runtimeLogPath = filepath .Join (homeDir , "AppData/Local/Docker/log/host/inference-llama.cpp-server.log" )
6975 default :
7076 return fmt .Errorf ("unsupported OS: %s" , runtime .GOOS )
7177 }
7278
79+ if noEngines {
80+ err = printMergedLog (serviceLogPath , "" )
81+ if err != nil {
82+ return err
83+ }
84+ } else {
85+ err = printMergedLog (serviceLogPath , runtimeLogPath )
86+ if err != nil {
87+ return err
88+ }
89+ }
90+
7391 ctx , cancel := signal .NotifyContext (context .Background (), os .Interrupt , os .Kill )
7492 defer cancel ()
7593
7694 g , ctx := errgroup .WithContext (ctx )
7795
7896 g .Go (func () error {
7997 t , err := tail .TailFile (
80- logFilePath , tail.Config {Follow : follow , ReOpen : follow },
98+ serviceLogPath , tail.Config {Location : & tail. SeekInfo { Offset : 0 , Whence : io . SeekEnd }, Follow : follow , ReOpen : follow },
8199 )
82100 if err != nil {
83101 return err
@@ -100,8 +118,7 @@ func newLogsCmd() *cobra.Command {
100118 // and the engines logs have not been skipped by setting `--no-engines`.
101119 g .Go (func () error {
102120 t , err := tail .TailFile (
103- filepath .Join (filepath .Dir (logFilePath ), "inference-llama.cpp-server.log" ),
104- tail.Config {Location : & tail.SeekInfo {Offset : 0 , Whence : io .SeekEnd }, Follow : follow , ReOpen : follow },
121+ runtimeLogPath , tail.Config {Location : & tail.SeekInfo {Offset : 0 , Whence : io .SeekEnd }, Follow : follow , ReOpen : follow },
105122 )
106123 if err != nil {
107124 return err
@@ -129,3 +146,81 @@ func newLogsCmd() *cobra.Command {
129146 c .Flags ().BoolVar (& noEngines , "no-engines" , false , "Skip inference engines logs" )
130147 return c
131148}
149+
150+ var timestampRe = regexp .MustCompile (`\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\].*` )
151+
152+ const timeFmt = "2006-01-02T15:04:05.000000000Z"
153+
154+ func printTillFirstTimestamp (logScanner * bufio.Scanner ) (time.Time , string ) {
155+ if logScanner == nil {
156+ return time.Time {}, ""
157+ }
158+
159+ for logScanner .Scan () {
160+ text := logScanner .Text ()
161+ match := timestampRe .FindStringSubmatch (text )
162+ if len (match ) == 2 {
163+ timestamp , err := time .Parse (timeFmt , match [1 ])
164+ if err != nil {
165+ println (text )
166+ continue
167+ }
168+ return timestamp , text
169+ } else {
170+ println (text )
171+ }
172+ }
173+ return time.Time {}, ""
174+ }
175+
176+ func printMergedLog (logPath1 , logPath2 string ) error {
177+ var logScanner1 * bufio.Scanner
178+ if logPath1 != "" {
179+ logFile1 , err := os .Open (logPath1 )
180+ if err == nil {
181+ defer logFile1 .Close ()
182+ logScanner1 = bufio .NewScanner (logFile1 )
183+ }
184+ }
185+
186+ var logScanner2 * bufio.Scanner
187+ if logPath2 != "" {
188+ logFile2 , err := os .Open (logPath2 )
189+ if err == nil {
190+ defer logFile2 .Close ()
191+ logScanner2 = bufio .NewScanner (logFile2 )
192+ }
193+ }
194+
195+ var timestamp1 time.Time
196+ var timestamp2 time.Time
197+ var log1Line string
198+ var log2Name string
199+
200+ timestamp1 , log1Line = printTillFirstTimestamp (logScanner1 )
201+ timestamp2 , log2Name = printTillFirstTimestamp (logScanner2 )
202+
203+ for log1Line != "" && log2Name != "" {
204+ for log1Line != "" && timestamp1 .Before (timestamp2 ) {
205+ println (log1Line )
206+ timestamp1 , log1Line = printTillFirstTimestamp (logScanner1 )
207+ }
208+ for log2Name != "" && timestamp2 .Before (timestamp1 ) {
209+ println (log2Name )
210+ timestamp2 , log2Name = printTillFirstTimestamp (logScanner2 )
211+ }
212+ }
213+
214+ if log1Line != "" {
215+ for logScanner1 .Scan () {
216+ println (logScanner1 .Text ())
217+ }
218+ }
219+ if log2Name != "" {
220+ for logScanner2 .Scan () {
221+ println (logScanner2 .Text ())
222+ }
223+ }
224+
225+ return nil
226+ }
0 commit comments