Skip to content

Commit

Permalink
add traces proxy including CORS
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Jan 16, 2025
1 parent 5045222 commit e8ef435
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 46 deletions.
131 changes: 85 additions & 46 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,58 +9,97 @@ export default {
// console.log(Object.fromEntries(request.headers))
const { pathname } = new URL(request.url)
if (request.method === 'GET' && pathname === '/') {
return new Response(`See https://github.com/pydantic/logfire-logs-proxy for details (commit ${env.GITHUB_SHA}).`)
} else if (pathname !== '/v1/logs' || request.method !== 'POST') {
return new Response('Only POST requests to `/v1/logs` are supported', { status: 404 })
return new Response(index_html(env), { headers: { 'Content-Type': 'text/html' } })
} else if (pathname === '/v1/logs' && request.method === 'POST') {
return await logProxy(request)
} else if (pathname === '/v1/traces' && request.method === 'POST') {
return await traceProxy(request)
} else if (pathname === '/v1/traces' && request.method === 'OPTIONS') {
return tracePreflight()
} else {
return new Response(`404: '${request.method} ${pathname}' not found`, { status: 404 })
}
},
} satisfies ExportedHandler<Env>

const auth = request.headers.get('Authorization')
if (!auth) {
return new Response('No "Authorization" header', { status: 401 })
}
const index_html = (env: Env) => `
<h1>logfire-logs-proxy</h1>
<p>
See <a href="https://github.com/pydantic/logfire-logs-proxy">github.com/pydantic/logfire-logs-proxy</a>
for details (commit <code>${env.GITHUB_SHA}</code>).
</p>
`

let body: ArrayBuffer
try {
body = await getBody(request)
} catch (e) {
console.log('Error parsing request body:', e)
return new Response(`Error collecting request body: ${e}`, { status: 400 })
}
async function logProxy(request: Request): Promise<Response> {
const auth = request.headers.get('Authorization')
if (!auth) {
return new Response('No "Authorization" header', { status: 401 })
}

let logRequest
try {
logRequest = decodeLogs(body)
} catch (e) {
console.log('Error parsing protobuf:', e)
return new Response(`Error parsing protobuf: ${e}`, { status: 400 })
}
let body: ArrayBuffer
try {
body = await getBody(request)
} catch (e) {
console.log('Error parsing request body:', e)
return new Response(`Error collecting request body: ${e}`, { status: 400 })
}

const traceRequest = convert(logRequest)
if (!traceRequest || !traceRequest.resourceSpans) {
return new Response('no data to proxy', { status: 202 })
}
let logRequest
try {
logRequest = decodeLogs(body)
} catch (e) {
console.log('Error parsing protobuf:', e)
return new Response(`Error parsing protobuf: ${e}`, { status: 400 })
}

console.log('Sending trace to logfire')
// console.log('Sending trace to logfire', JSON.stringify(traceRequest.resourceSpans, null, 2))
const response = await fetch('https://logfire-api.pydantic.dev/v1/traces', {
method: 'POST',
headers: {
'Content-Type': 'application/x-protobuf',
Authorization: auth,
'User-Agent': `logfire-logs-proxy ${request.headers.get('User-Agent')}`,
},
body: encodeTraces(traceRequest),
})
if (response.ok) {
console.log('Successfully sent trace to logfire')
return response
} else {
const text = await response.text()
console.warn('Unexpected response:', { status: response.status, text })
return new Response(text, response)
}
},
} satisfies ExportedHandler<Env>
const traceRequest = convert(logRequest)
if (!traceRequest || !traceRequest.resourceSpans) {
return new Response('no data to proxy', { status: 202 })
}

console.log('Sending trace to logfire')
// console.log('Sending trace to logfire', JSON.stringify(traceRequest.resourceSpans, null, 2))
const response = await fetch('https://logfire-api.pydantic.dev/v1/traces', {
method: 'POST',
headers: {
'Content-Type': 'application/x-protobuf',
Authorization: auth,
'User-Agent': `logfire-logs-proxy ${request.headers.get('User-Agent')}`,
},
body: encodeTraces(traceRequest),
})
if (response.ok) {
console.log('Successfully sent trace to logfire')
return response
} else {
const text = await response.text()
console.warn('Unexpected response:', { status: response.status, text })
return new Response(text, response)
}
}

async function traceProxy(request: Request): Promise<Response> {
const response = await fetch('https://logfire-api.pydantic.dev/v1/traces', request)
// add CORS headers
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
...Object.fromEntries(response.headers),
'Access-Control-Allow-Origin': '*',
},
})
}

const tracePreflight = () =>
new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST',
'Access-Control-Allow-Headers': 'Authorization, Content-Type',
},
})

async function getBody(request: Request): Promise<ArrayBuffer> {
if (request.body === null) {
Expand Down
4 changes: 4 additions & 0 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ enabled = true

[vars]
GITHUB_SHA = "unknown"

[env.staging]
name = "logfire-logs-proxy-staging"
vars = { GITHUB_SHA = "staging" }

0 comments on commit e8ef435

Please sign in to comment.