Skip to content

Commit

Permalink
chore(medusa): Add handler path to the http tracing to be able to gro…
Browse files Browse the repository at this point in the history
…up by (medusajs#10835)

RESOLVES FRMW-2835

In this PR, we trace HTTP requests using the route pattern and not the request URL. This allows us to aggregate all HTTP requests under a single route.

In terms of implementation, we have to self find the route for a given request, since there is no API in express to do the same and we begin tracing even before the request is handed over to Express.

Since, the route matching happens via RegExp matches, we ensure that this does not add much performance overhead. The matching takes between 0.8ms-1.5ms for various different routes

Co-authored-by: Harminder Virk <[email protected]>
  • Loading branch information
adrien2p and thetutlage authored Jan 7, 2025
1 parent 5216ad2 commit 899b1fb
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-walls-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---

chore(medusa): Add handler path to the http tracing to be able to group by
47 changes: 46 additions & 1 deletion packages/medusa/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { logger } from "@medusajs/framework/logger"
import loaders from "../loaders"
import { MedusaModule } from "@medusajs/framework/modules-sdk"
import { MedusaContainer } from "@medusajs/framework/types"
import { parse } from "url"

const EVERY_SIXTH_HOUR = "0 */6 * * *"
const CRON_SCHEDULE = EVERY_SIXTH_HOUR
Expand Down Expand Up @@ -88,6 +89,44 @@ function displayAdminUrl({
logger.info(`Admin URL → http://${host || "localhost"}:${port}${adminPath}`)
}

type ExpressStack = {
name: string
match: (url: string) => boolean
route: { path: string }
handle: { stack: ExpressStack[] }
}

/**
* Retrieve the route path from the express stack based on the input url
* @param stack - The express stack
* @param url - The input url
* @returns The route path
*/
function findExpressRoutePath({
stack,
url,
}: {
stack: ExpressStack[]
url: string
}): string | void {
const stackToProcess = [...stack]

while (stackToProcess.length > 0) {
const layer = stackToProcess.pop()!

if (layer.name === "bound dispatch" && layer.match(url)) {
return layer.route.path
}

// Add nested stack items to be processed if they exist
if (layer.handle?.stack?.length) {
stackToProcess.push(...layer.handle.stack)
}
}

return undefined
}

async function start(args: {
directory: string
host?: string
Expand All @@ -104,15 +143,21 @@ async function start(args: {
const app = express()

const http_ = http.createServer(async (req, res) => {
const stack = app._router.stack
await new Promise((resolve) => {
res.on("finish", resolve)
if (traceRequestHandler) {
const expressHandlerPath = findExpressRoutePath({
stack,
url: parse(req.url!, false).pathname!,
})
void traceRequestHandler(
async () => {
app(req, res)
},
req,
res
res,
expressHandlerPath
)
} else {
app(req, res)
Expand Down
13 changes: 10 additions & 3 deletions packages/medusa/src/instrumentation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,20 @@ export function instrumentHttpLayer() {
const HTTPTracer = new Tracer("@medusajs/http", "2.0.0")
const { SpanStatusCode } = require("@opentelemetry/api")

startCommand.traceRequestHandler = async (requestHandler, req, res) => {
startCommand.traceRequestHandler = async (
requestHandler,
req,
res,
handlerPath
) => {
if (shouldExcludeResource(req.url!)) {
return await requestHandler()
}

const traceName = `${req.method} ${req.url}`
const traceName = handlerPath ?? `${req.method} ${req.url}`
await HTTPTracer.trace(traceName, async (span) => {
span.setAttributes({
"http.route": handlerPath,
"http.url": req.url,
"http.method": req.method,
...req.headers,
Expand Down Expand Up @@ -66,7 +72,8 @@ export function instrumentHttpLayer() {
return await handler(req, res)
}

const traceName = `route: ${req.method} ${req.originalUrl}`
const label = req.route?.path ?? `${req.method} ${req.originalUrl}`
const traceName = `route handler: ${label}`

await HTTPTracer.trace(traceName, async (span) => {
try {
Expand Down

0 comments on commit 899b1fb

Please sign in to comment.