Implement container logs handler

- Add Docker client to handlers for container operations
- Implement HandleAppLogs() to fetch and return container logs
- Support ?tail=N query parameter (default 500 lines)
- Handle missing container gracefully
This commit is contained in:
Jeffrey Paul 2025-12-29 15:48:23 +07:00
parent 3f9d83c436
commit daaf00893c
3 changed files with 36 additions and 7 deletions

View File

@ -331,6 +331,9 @@ func (h *Handlers) HandleAppDeployments() http.HandlerFunc {
}
}
// defaultLogTail is the default number of log lines to fetch.
const defaultLogTail = "500"
// HandleAppLogs returns the container logs handler.
func (h *Handlers) HandleAppLogs() http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
@ -343,16 +346,37 @@ func (h *Handlers) HandleAppLogs() http.HandlerFunc {
return
}
// Container logs fetching not yet implemented
writer.Header().Set("Content-Type", "text/plain")
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
if !application.ContainerID.Valid {
_, _ = writer.Write([]byte("No container running"))
_, _ = writer.Write([]byte("No container running\n"))
return
}
_, _ = writer.Write([]byte("Container logs not implemented yet"))
tail := request.URL.Query().Get("tail")
if tail == "" {
tail = defaultLogTail
}
logs, logsErr := h.docker.ContainerLogs(
request.Context(),
application.ContainerID.String,
tail,
)
if logsErr != nil {
h.log.Error("failed to get container logs",
"error", logsErr,
"app", application.Name,
"container", application.ContainerID.String,
)
_, _ = writer.Write([]byte("Failed to fetch container logs\n"))
return
}
_, _ = writer.Write([]byte(logs))
}
}

View File

@ -9,6 +9,7 @@ import (
"go.uber.org/fx"
"git.eeqj.de/sneak/upaas/internal/database"
"git.eeqj.de/sneak/upaas/internal/docker"
"git.eeqj.de/sneak/upaas/internal/globals"
"git.eeqj.de/sneak/upaas/internal/healthcheck"
"git.eeqj.de/sneak/upaas/internal/logger"
@ -30,6 +31,7 @@ type Params struct {
App *app.Service
Deploy *deploy.Service
Webhook *webhook.Service
Docker *docker.Client
}
// Handlers provides HTTP request handlers.
@ -42,6 +44,7 @@ type Handlers struct {
appService *app.Service
deploy *deploy.Service
webhook *webhook.Service
docker *docker.Client
}
// New creates a new Handlers instance.
@ -55,6 +58,7 @@ func New(_ fx.Lifecycle, params Params) (*Handlers, error) {
appService: params.App,
deploy: params.Deploy,
webhook: params.Webhook,
docker: params.Docker,
}, nil
}

View File

@ -86,7 +86,7 @@ func createAppServices(
logInstance *logger.Logger,
dbInstance *database.Database,
cfg *config.Config,
) (*auth.Service, *app.Service, *deploy.Service, *webhook.Service) {
) (*auth.Service, *app.Service, *deploy.Service, *webhook.Service, *docker.Client) {
t.Helper()
authSvc, authErr := auth.New(fx.Lifecycle(nil), auth.ServiceParams{
@ -128,7 +128,7 @@ func createAppServices(
})
require.NoError(t, webhookErr)
return authSvc, appSvc, deploySvc, webhookSvc
return authSvc, appSvc, deploySvc, webhookSvc, dockerClient
}
func setupTestHandlers(t *testing.T) *testContext {
@ -138,7 +138,7 @@ func setupTestHandlers(t *testing.T) *testContext {
globalInstance, logInstance, dbInstance, hcInstance := createCoreServices(t, cfg)
authSvc, appSvc, deploySvc, webhookSvc := createAppServices(
authSvc, appSvc, deploySvc, webhookSvc, dockerClient := createAppServices(
t,
logInstance,
dbInstance,
@ -156,6 +156,7 @@ func setupTestHandlers(t *testing.T) *testContext {
App: appSvc,
Deploy: deploySvc,
Webhook: webhookSvc,
Docker: dockerClient,
},
)
require.NoError(t, handlerErr)