Skip to content

Commit 8fcf0c2

Browse files
committed
feat(logging): enhance logging functionality with structured log entries and improved error handling
1 parent 4e8973c commit 8fcf0c2

4 files changed

Lines changed: 64 additions & 6 deletions

File tree

internal/deploy/deploy.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/dployr-io/dployr/internal/version_resolver"
1313
"github.com/dployr-io/dployr/pkg/core/deploy"
14+
coreutils "github.com/dployr-io/dployr/pkg/core/utils"
1415
"github.com/dployr-io/dployr/pkg/shared"
1516
"github.com/dployr-io/dployr/pkg/store"
1617

@@ -132,12 +133,20 @@ func (d *Deployer) Deploy(ctx context.Context, req *deploy.DeployRequest) (*depl
132133
}
133134

134135
func (d *Deployer) Build(ctx context.Context, req *deploy.BuildRequest) (*deploy.BuildResponse, error) {
136+
logDir := filepath.Join(coreutils.GetDataDir(), ".dployr", "logs")
137+
svcName := coreutils.FormatName(req.Name)
138+
139+
shared.LogInfoF(svcName, logDir, "starting build")
140+
135141
workDir, err := SetupDir(req.Name)
136142
if err != nil {
143+
shared.LogErrF(svcName, logDir, err)
137144
return nil, fmt.Errorf("failed to setup working directory: %w", err)
138145
}
139146

147+
shared.LogInfoF(svcName, logDir, "cloning repository")
140148
if err := CloneRepo(req.Remote, workDir, d.cfg); err != nil {
149+
shared.LogErrF(svcName, logDir, fmt.Errorf("clone failed: %w", err))
141150
return nil, fmt.Errorf("failed to clone repository: %w", err)
142151
}
143152

@@ -166,6 +175,8 @@ func (d *Deployer) Build(ctx context.Context, req *deploy.BuildRequest) (*deploy
166175
env[k] = s
167176
}
168177
}
178+
179+
shared.LogInfoF(svcName, logDir, "building image")
169180
image, err := BuildImage(req.Name, buildDir, d.cfg, BuildOpts{
170181
Runtime: req.Runtime,
171182
Version: resolution.Version,
@@ -176,11 +187,13 @@ func (d *Deployer) Build(ctx context.Context, req *deploy.BuildRequest) (*deploy
176187
Port: req.Port,
177188
IsNextJS: req.Runtime == "nodejs" && detectNextJS(buildDir),
178189
Env: env,
179-
}, d.dockerCli)
190+
}, d.dockerCli, svcName, logDir)
180191
if err != nil {
192+
shared.LogErrF(svcName, logDir, fmt.Errorf("build failed: %w", err))
181193
return nil, fmt.Errorf("build failed: %w", err)
182194
}
183195

196+
shared.LogInfoF(svcName, logDir, "image pushed, build complete")
184197
return &deploy.BuildResponse{Image: image}, nil
185198
}
186199

internal/deploy/utils.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func detectNextJS(dir string) bool {
9090
return inDeps || inDev
9191
}
9292

93-
func BuildImage(name, srcDir string, cfg *shared.Config, opts BuildOpts, dockerCli deployDockerAPI) (string, error) {
93+
func BuildImage(name, srcDir string, cfg *shared.Config, opts BuildOpts, dockerCli deployDockerAPI, svcName, logDir string) (string, error) {
9494
if cfg.RegistryURL == "" {
9595
return "", fmt.Errorf("REGISTRY_URL is not configured on this build node")
9696
}
@@ -155,16 +155,45 @@ func BuildImage(name, srcDir string, cfg *shared.Config, opts BuildOpts, dockerC
155155
}
156156
defer buildResp.Body.Close()
157157

158-
// Drain and check for build errors in the JSON stream.
158+
// Drain docker build output, writing to the log file and surfacing errors.
159+
// Open the log file once for the entire build stream rather than per-line.
160+
var logWriter *bufio.Writer
161+
var logFile *os.File
162+
if logDir != "" {
163+
if err := os.MkdirAll(logDir, 0755); err == nil {
164+
logFile, _ = os.OpenFile(filepath.Join(logDir, strings.ToLower(svcName)+".log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
165+
if logFile != nil {
166+
logWriter = bufio.NewWriter(logFile)
167+
}
168+
}
169+
}
170+
159171
scanner := bufio.NewScanner(buildResp.Body)
160172
for scanner.Scan() {
161173
var msg struct {
162174
Error string `json:"error"`
163175
Stream string `json:"stream"`
164176
}
165-
if json.Unmarshal(scanner.Bytes(), &msg) == nil && msg.Error != "" {
177+
if json.Unmarshal(scanner.Bytes(), &msg) != nil {
178+
continue
179+
}
180+
if msg.Error != "" {
181+
if logWriter != nil {
182+
logWriter.Flush()
183+
logFile.Close()
184+
}
166185
return "", fmt.Errorf("docker build: %s", strings.TrimSpace(msg.Error))
167186
}
187+
if line := strings.TrimSpace(msg.Stream); line != "" && logWriter != nil {
188+
entry := fmt.Sprintf(`{"time":%q,"level":"INFO","msg":%q}`+"\n",
189+
time.Now().UTC().Format(time.RFC3339Nano), line)
190+
logWriter.WriteString(entry)
191+
}
192+
}
193+
194+
if logWriter != nil {
195+
logWriter.Flush()
196+
logFile.Close()
168197
}
169198

170199
var authStr string

internal/logs/handler.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,22 @@ func (h *Handler) StreamLogs(ctx context.Context, opts logs.StreamOptions, sendC
8383
// streamTail reads from current position and follows new log entries with file rotation detection.
8484
// Handles both historical reads (with time-based cutoff) and live tailing.
8585
func (h *Handler) streamTail(ctx context.Context, logPath string, opts logs.StreamOptions, cutoffTime time.Time, sendChunk func(chunk logs.LogChunk) error) error {
86-
file, err := os.Open(logPath)
86+
var file *os.File
87+
var err error
88+
for range 30 {
89+
file, err = os.Open(logPath)
90+
if err == nil {
91+
break
92+
}
93+
if !os.IsNotExist(err) {
94+
break
95+
}
96+
select {
97+
case <-ctx.Done():
98+
return ctx.Err()
99+
case <-time.After(500 * time.Millisecond):
100+
}
101+
}
87102
if err != nil {
88103
h.logger.Error("failed to open log file", "error", err, "path", logPath)
89104
return fmt.Errorf("failed to open log file: %w", err)

pkg/shared/logger.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ func writeDeploymentLog(name, dir, level, message string) error {
284284
}
285285
defer f.Close()
286286

287-
entry := fmt.Sprintf("%s\n", message)
287+
entry := fmt.Sprintf(`{"time":%q,"level":%q,"msg":%q}`+"\n",
288+
time.Now().UTC().Format(time.RFC3339Nano), level, message)
288289

289290
_, err = f.WriteString(entry)
290291
return err

0 commit comments

Comments
 (0)