Skip to content
This repository was archived by the owner on Jun 16, 2026. It is now read-only.

Commit fd19a60

Browse files
author
Piotr Stankiewicz
committed
Merge service and runtime logs
The `logs` command should include both service, and runtime (engine) logs, to provide all relevant info needed to debug/observe DMR operation. Currently the runtime logs will only be included when running `logs` with `-f` (follow). So make sure the service and runtime logs are merged, and printed, unless `logs` is invoked with `--no-engines`. Signed-off-by: Piotr Stankiewicz <piotr.stankiewicz@docker.com>
1 parent e6dea14 commit fd19a60

1 file changed

Lines changed: 101 additions & 6 deletions

File tree

commands/logs.go

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package commands
22

33
import (
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

Comments
 (0)