Created
May 19, 2025 20:45
-
-
Save martinamps/ab57c79acb6eb6eebe45b21f84a83e13 to your computer and use it in GitHub Desktop.
mini proxy to debug requests from Claude Code to Datadog's agentless OTLP intake endpoint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// OTLP Debug Proxy | |
// tweak datadog site url as necessary | |
const DATADOG_OTLP_METRICS_ENDPOINT = `https://api.us5.datadoghq.com/api/intake/otlp/v1/metrics`; | |
console.log(`Starting OTLP Debug Proxy on port 8080...`); | |
console.log(`Forwarding OTLP/HTTP metrics to: ${DATADOG_OTLP_METRICS_ENDPOINT}`); | |
Bun.serve({ | |
port: 8080, | |
async fetch(claudeRequest) { | |
const claudeRequestUrl = new URL(claudeRequest.url); | |
console.log(`\n--- [${new Date().toISOString()}] Received Request from Claude ---`); | |
console.log(`Claude Method: ${claudeRequest.method}`); | |
console.log(`Claude Path: ${claudeRequestUrl.pathname}${claudeRequestUrl.search}`); | |
console.log("Claude Request Headers:"); | |
const originalClaudeHeaders = new Headers(claudeRequest.headers); | |
for (const [key, value] of originalClaudeHeaders) { | |
console.log(` ${key}: ${value}`); | |
} | |
let claudeRequestBodyBuffer: ArrayBuffer | null = null; | |
if (claudeRequest.method === "POST" && claudeRequest.body) { | |
try { | |
claudeRequestBodyBuffer = await claudeRequest.arrayBuffer(); | |
console.log(`Claude Request Body received, ${claudeRequestBodyBuffer.byteLength} bytes.`); | |
const firstBytes = new Uint8Array(claudeRequestBodyBuffer.slice(0, 64)); | |
const hexDump = Array.from(firstBytes).map(b => b.toString(16).padStart(2, '0')).join(''); | |
console.log(` Claude Body HEX (first 64 bytes): ${hexDump}${claudeRequestBodyBuffer.byteLength > 64 ? '...' : ''}`); | |
} catch (e: any) { | |
console.error(" Error reading Claude request body:", e.message); | |
return new Response("Error reading client request body\n", { status: 500 }); | |
} | |
} else { | |
console.log("No body in Claude request or method not POST. Not forwarding."); | |
return new Response("Request not forwarded (no body or not POST)\n", { status: 400 }); | |
} | |
console.log(`\n--- [${new Date().toISOString()}] Forwarding Request to Datadog ---`); | |
const headersToForward = new Headers(); | |
// Forward essential headers | |
if (originalClaudeHeaders.has("content-type")) { | |
headersToForward.set("content-type", originalClaudeHeaders.get("content-type")!); | |
} | |
if (originalClaudeHeaders.has("dd-api-key")) { | |
headersToForward.set("dd-api-key", originalClaudeHeaders.get("dd-api-key")!); | |
} | |
if (originalClaudeHeaders.has("user-agent")) { | |
headersToForward.set("user-agent", originalClaudeHeaders.get("user-agent")!); | |
} | |
if (originalClaudeHeaders.has("content-encoding")) { | |
headersToForward.set("content-encoding", originalClaudeHeaders.get("content-encoding")!); | |
} | |
console.log("Forwarding these headers to Datadog:"); | |
for (const [key, value] of headersToForward) { | |
console.log(` ${key}: ${value}`); | |
} | |
try { | |
const datadogResponse = await fetch(DATADOG_OTLP_METRICS_ENDPOINT, { | |
method: claudeRequest.method, | |
headers: headersToForward, | |
body: claudeRequestBodyBuffer, | |
}); | |
console.log(`\n--- [${new Date().toISOString()}] Received Response from Datadog ---`); | |
console.log(`Datadog Status: ${datadogResponse.status} ${datadogResponse.statusText}`); | |
const ddResponseHeaders = new Headers(datadogResponse.headers); | |
console.log("Datadog Response Headers:"); | |
for (const [key, value] of ddResponseHeaders) { | |
console.log(` ${key}: ${value}`); | |
} | |
const datadogResponseBodyText = await datadogResponse.text(); | |
console.log("Datadog Response Body:"); | |
console.log(datadogResponseBodyText || "[No body content]"); | |
console.log("--- End of Datadog Interaction ---"); | |
return new Response(datadogResponseBodyText || null, { | |
status: datadogResponse.status, | |
statusText: datadogResponse.statusText, | |
headers: ddResponseHeaders, | |
}); | |
} catch (fetchError: any) { | |
console.error(`\n--- [${new Date().toISOString()}] Error Fetching from Datadog ---`); | |
console.error(fetchError.message); | |
if (fetchError.cause) console.error("Cause:", fetchError.cause); | |
console.log("--- End of Datadog Interaction (Fetch Error) ---"); | |
return new Response(`Error forwarding request to Datadog: ${fetchError.message}\n`, { | |
status: 502, | |
headers: { "Content-Type": "text/plain" }, | |
}); | |
} | |
}, | |
error(bunServeError: Error) { | |
console.error(`\n--- [${new Date().toISOString()}] Bun Serve Error (Proxy Level) ---`); | |
console.error(bunServeError); | |
return new Response("Internal Server Error in Proxy\n", { | |
status: 500, | |
headers: { "Content-Type": "text/plain" }, | |
}); | |
}, | |
}); | |
console.log("OTLP Debug Proxy is listening on http://localhost:8080"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment