-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add diagnostic logging to agent and enrollment endpoint #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
632632c
174c5ea
0e8872c
f85defd
756db54
dfbb45c
620e7aa
0bb83a4
b61070c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import ( | |
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "log/slog" | ||
| "net/http" | ||
| "time" | ||
| ) | ||
|
|
@@ -57,21 +58,25 @@ 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)) | ||
| } | ||
|
|
||
| var result EnrollResponse | ||
| 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) | ||
|
Comment on lines
+61
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent URL representation in request vs. error/response debug logs The request log uses the fully-qualified URL ( In practice this means that when an Consider using the same full URL in all three log sites for consistency and debuggability. |
||
| return &result, nil | ||
| } | ||
|
|
||
|
|
@@ -118,21 +123,25 @@ 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)) | ||
| } | ||
|
|
||
| var result ConfigResponse | ||
| 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,13 +16,17 @@ 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); | ||||||
|
Comment on lines
18
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always-on validation-error log on unauthenticated endpoint
The payload is |
||||||
| return NextResponse.json( | ||||||
| { error: "Invalid input", details: parsed.error.flatten().fieldErrors }, | ||||||
| { status: 400 }, | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| 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)`); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always-on pre-auth environment count log This Two concerns:
Consider removing this line, or gating it behind a debug logger, since the rejected/success lines already include the checked count. |
||||||
|
|
||||||
| // 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}"`); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For consistency, apply the same sanitization here:
Suggested change
Context Used: Rule from When reviewing changes to authentication, authorization, en... (source) |
||||||
|
|
||||||
| 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 }, | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent URL representation in request vs. error/response debug logs
The request log uses the fully-qualified URL (
c.baseURL + "/api/agent/enroll") while the subsequent error and response logs drop back to just the relative path ("/api/agent/enroll"). This pattern is repeated for all three methods (Enroll,GetConfig,SendHeartbeat).In practice this means that when an
http errororhttp responselog line appears you lose the hostname/port context needed to identify which server instance the agent was talking to — which is exactly the information most useful during a 401 enrollment failure investigation, particularly when debugging multi-environment deployments.Consider using the same full URL in all three log sites:
Apply the same fix to
GetConfig()andSendHeartbeat()methods for consistency.