diff --git a/agent/internal/agent/agent.go b/agent/internal/agent/agent.go index d67d1d4..f79c544 100644 --- a/agent/internal/agent/agent.go +++ b/agent/internal/agent/agent.go @@ -104,6 +104,8 @@ func (a *Agent) pollAndApply() { return } + slog.Debug("poll complete", "actions", len(actions)) + for _, action := range actions { switch action.Action { case ActionStart: @@ -170,6 +172,8 @@ func (a *Agent) sendHeartbeat() { a.sampleResults = append(results, a.sampleResults...) a.mu.Unlock() } + } else { + slog.Debug("heartbeat sent", "pipelines", len(hb.Pipelines), "sampleResults", len(results)) } } diff --git a/agent/internal/agent/enrollment.go b/agent/internal/agent/enrollment.go index 84364d4..72dfb28 100644 --- a/agent/internal/agent/enrollment.go +++ b/agent/internal/agent/enrollment.go @@ -2,6 +2,7 @@ package agent import ( "fmt" + "log/slog" "os" "os/exec" "path/filepath" @@ -23,10 +24,13 @@ func loadOrEnroll(cfg *config.Config, c *client.Client) (string, error) { if err == nil { token := strings.TrimSpace(string(data)) if token != "" { + slog.Debug("loaded existing node token", "path", tokenPath) return token, nil } } + slog.Debug("no existing node token", "path", tokenPath) + // Need to enroll if cfg.Token == "" { return "", fmt.Errorf("no node token found at %s and VF_TOKEN is not set — cannot enroll", tokenPath) @@ -35,6 +39,12 @@ func loadOrEnroll(cfg *config.Config, c *client.Client) (string, error) { hostname, _ := os.Hostname() vectorVersion := detectVectorVersion(cfg.VectorBin) + prefix := cfg.Token + if len(cfg.Token) > 24 { + prefix = cfg.Token[:24] + "..." + } + slog.Debug("enrolling", "hostname", hostname, "os", runtime.GOOS+"/"+runtime.GOARCH, "tokenPrefix", prefix) + resp, err := c.Enroll(client.EnrollRequest{ Token: cfg.Token, Hostname: hostname, @@ -54,7 +64,7 @@ func loadOrEnroll(cfg *config.Config, c *client.Client) (string, error) { return "", fmt.Errorf("persist node token: %w", err) } - fmt.Printf("Enrolled as node %s in environment %q\n", resp.NodeID, resp.EnvironmentName) + slog.Info("enrolled successfully", "nodeId", resp.NodeID, "environment", resp.EnvironmentName) return resp.NodeToken, nil } diff --git a/agent/internal/client/client.go b/agent/internal/client/client.go index 9707588..a922bff 100644 --- a/agent/internal/client/client.go +++ b/agent/internal/client/client.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "time" ) @@ -57,14 +58,17 @@ func (c *Client) Enroll(req EnrollRequest) (*EnrollResponse, error) { } httpReq.Header.Set("Content-Type", "application/json") + slog.Debug("http request", "method", "POST", "url", c.baseURL+"/api/agent/enroll") resp, err := c.httpClient.Do(httpReq) if err != nil { + slog.Debug("http error", "method", "POST", "url", "/api/agent/enroll", "error", err) return nil, fmt.Errorf("enroll request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { respBody, _ := io.ReadAll(resp.Body) + slog.Debug("http response", "method", "POST", "url", "/api/agent/enroll", "status", resp.StatusCode, "body", string(respBody)) return nil, fmt.Errorf("enroll failed (status %d): %s", resp.StatusCode, string(respBody)) } @@ -72,6 +76,7 @@ func (c *Client) Enroll(req EnrollRequest) (*EnrollResponse, error) { if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode enroll response: %w", err) } + slog.Debug("http response", "method", "POST", "url", "/api/agent/enroll", "status", 200) return &result, nil } @@ -118,14 +123,17 @@ func (c *Client) GetConfig() (*ConfigResponse, error) { } httpReq.Header.Set("Authorization", "Bearer "+c.nodeToken) + slog.Debug("http request", "method", "GET", "url", c.baseURL+"/api/agent/config") resp, err := c.httpClient.Do(httpReq) if err != nil { + slog.Debug("http error", "method", "GET", "url", "/api/agent/config", "error", err) return nil, fmt.Errorf("config request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { respBody, _ := io.ReadAll(resp.Body) + slog.Debug("http response", "method", "GET", "url", "/api/agent/config", "status", resp.StatusCode, "body", string(respBody)) return nil, fmt.Errorf("config request failed (status %d): %s", resp.StatusCode, string(respBody)) } @@ -133,6 +141,7 @@ func (c *Client) GetConfig() (*ConfigResponse, error) { if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode config response: %w", err) } + slog.Debug("http response", "method", "GET", "url", "/api/agent/config", "status", 200, "pipelines", len(result.Pipelines)) return &result, nil } @@ -231,14 +240,17 @@ func (c *Client) SendHeartbeat(req HeartbeatRequest) error { httpReq.Header.Set("Content-Type", "application/json") httpReq.Header.Set("Authorization", "Bearer "+c.nodeToken) + slog.Debug("http request", "method", "POST", "url", c.baseURL+"/api/agent/heartbeat") resp, err := c.httpClient.Do(httpReq) if err != nil { + slog.Debug("http error", "method", "POST", "url", "/api/agent/heartbeat", "error", err) return fmt.Errorf("heartbeat request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { respBody, _ := io.ReadAll(resp.Body) + slog.Debug("http response", "method", "POST", "url", "/api/agent/heartbeat", "status", resp.StatusCode, "body", string(respBody)) return fmt.Errorf("heartbeat failed (status %d): %s", resp.StatusCode, string(respBody)) } return nil diff --git a/src/app/api/agent/enroll/route.ts b/src/app/api/agent/enroll/route.ts index d7f1e69..d2e2ab9 100644 --- a/src/app/api/agent/enroll/route.ts +++ b/src/app/api/agent/enroll/route.ts @@ -16,6 +16,7 @@ export async function POST(request: Request) { const body = await request.json(); const parsed = enrollSchema.safeParse(body); if (!parsed.success) { + console.error("[enroll] invalid input:", parsed.error.flatten().fieldErrors); return NextResponse.json( { error: "Invalid input", details: parsed.error.flatten().fieldErrors }, { status: 400 }, @@ -23,6 +24,9 @@ export async function POST(request: Request) { } const { token, hostname, os, agentVersion, vectorVersion } = parsed.data; + const safeHostname = hostname.replace(/[\r\n\t"]/g, " "); + const safeVersion = (agentVersion ?? "unknown").replace(/[\r\n\t"]/g, " "); + console.log(`[enroll] attempt from hostname="${safeHostname}" agentVersion="${safeVersion}"`); // Find all environments that have an enrollment token const environments = await prisma.environment.findMany({ @@ -37,6 +41,7 @@ export async function POST(request: Request) { team: { select: { id: true } }, }, }); + console.log(`[enroll] found ${environments.length} candidate environment(s)`); // Try each environment's enrollment token let matchedEnv: (typeof environments)[0] | null = null; @@ -48,6 +53,7 @@ export async function POST(request: Request) { } if (!matchedEnv) { + console.error(`[enroll] REJECTED -- no matching environment (checked ${environments.length})`); return NextResponse.json( { error: "Invalid enrollment token" }, { status: 401 }, @@ -73,6 +79,7 @@ export async function POST(request: Request) { metadata: { enrolledVia: "agent" }, }, }); + console.log(`[enroll] SUCCESS -- node ${node.id} enrolled in "${matchedEnv.name}"`); return NextResponse.json({ nodeId: node.id, @@ -81,7 +88,7 @@ export async function POST(request: Request) { environmentName: matchedEnv.name, }); } catch (error) { - console.error("Agent enrollment error:", error); + console.error("[enroll] unexpected error:", error); return NextResponse.json( { error: "Enrollment failed" }, { status: 500 },