Skip to content

Conversation

@Fryuni
Copy link
Member

@Fryuni Fryuni commented May 12, 2025

Changes

This PR adds low-level tracing events to allow user and third-party integrations to implement telemetry abstractions taking into account Astro's request handling and rendering pipeline.

Related

Implementation details

The API from core will be very simple and provide specific guarantees:

  • Integration can call three functions: onBeforeTrace, onCompleteTrace and onAfterTrace. The functions adding listeners to all trace events that Astro provides tracking of, before, on completion, and after what the event describes happen respectively.
    • Listeners passed to onBeforeTrace are guaranteed to be synchronously called in the same context that will call the operation described by the event.
    • Listeners passed to onCompleteTrace are guaranteed to be synchronously called in the same context that follows the execution of the operation. This context is the same as the context where onBeforeTrace when the operation is synchronous and a different context when it is asynchronous.
    • Listeners passed to onAfterTrace are guaranteed to be synchronously called in the same context that called the operation. Tracking of nested events is responsibility of the integration providing the listeners. More precisely the operation is called:
      • After the operation was called
      • Before its result is awaited if the operation is asynchronous
      • Before any other operation that is not nested inside of it is initiated in the same synchronous context (this guarantee allows tracking parallel operations)
  • The signatures of all three functions is (listener: TraceListener, signal?: AbortSignal) => void. When the AbortSignal is given, the listener will no longer be called once the signal aborts.

Those guarantees and API are (so far) enough to satisfy all the requirements of the core changes from the proposal.

Events being tracked

  • instantiateComponent: When a component is instantiated for rendering. Rendering a component is done lazily, so this event is tracked separately. A component might be instantiated and never rendered depending on how it is used and what happens during a request.
  • componentFrontmatter: When the frontmatter of a component executes to define what needs to be rendered. This is different from rendering the component, which drills down into resolving streaming promises and rendering nested components.
  • componentRender: When a component is effectively rendered to HTML.
  • slotRender: When a slot passed to a component is rendered. Due to optimizations this is not necessarily at the time the component is rendered, neither the component who passed the slots nor the component receiving them. A slot used by a component might be rendered even if the component itself is never rendered.
  • routeRender: When a route is rendered, meaning the process that surrounds the rendering of the page component or calling the route endpoint.
  • middleware: When a middleware is executed.

Pending decision

  • New initialization API for early code execution from integrations.
  • Which initializations to provide out-of-the-box on the OpenTelemetry integration.
  • What to do for each detected

Testing

  • Testing TBD once the API has been decided

Docs

@changeset-bot
Copy link

changeset-bot bot commented May 12, 2025

🦋 Changeset detected

Latest commit: 069ec6b

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Fryuni Fryuni self-assigned this May 12, 2025
@github-actions github-actions bot added pkg: astro Related to the core `astro` package (scope) docs pr labels May 12, 2025
@Fryuni Fryuni added feat: ssr Related to SSR (scope) core feat: integration api related to the public integration APIs (scope) labels May 12, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented May 12, 2025

CodSpeed Performance Report

Merging #13782 will not alter performance

Comparing fryuni/tracing-hooks (069ec6b) with main (13f7d36)

Summary

✅ 6 untouched

@Fryuni
Copy link
Member Author

Fryuni commented Aug 3, 2025

How tracing a request with DB access on the top-level page and some components look like:
image

@github-actions github-actions bot added pkg: example Related to an example package (scope) pkg: integration Related to any renderer integration (scope) labels Aug 3, 2025
@github-actions github-actions bot added the semver: minor Change triggers a `minor` release label Aug 9, 2025
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is blocked because it contains a minor changeset. A reviewer will merge this at the next release if approved.

@Fryuni Fryuni added the pr preview Apply this label to a PR to generate a preview release label Aug 11, 2025
@github-actions github-actions bot removed the pr preview Apply this label to a PR to generate a preview release label Aug 11, 2025
@Fryuni Fryuni added the pr preview Apply this label to a PR to generate a preview release label Aug 15, 2025
@github-actions github-actions bot removed the pr preview Apply this label to a PR to generate a preview release label Aug 15, 2025
Copy link
Contributor

@ascorbic ascorbic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to make the APIs here explicitly experimental. While I know it needs an integration before it will do anything, I think the integraiton API should be prefixed with experimental, and ideally the whole feature should be behind an experimental flag, so we can make breaking changes to the external API.

@Fryuni
Copy link
Member Author

Fryuni commented Sep 4, 2025

I think we need to make the APIs here explicitly experimental. While I know it needs an integration before it will do anything, I think the integraiton API should be prefixed with experimental and ideally the whole feature should be behind an experimental flag, so we can make breaking changes to the external API.

The integration API would be just for adding initializers, so would it be experimental_addInitializer plus an experimental.initializers flag? Wouldn't both be too much?

As for tracing, that would be a bit tricky to put behind a flag. The implementation has to touch methods as they are defined so it can remain as invisible as possible to everything else and the configuration is not available at that point. I'll have to think about it.

@dvelasquez
Copy link

Are we going to be able to trace events like TTFB and TTLB?

Circling back to the conversation on discord, there can be a huge difference between one and the other since we are using http streaming with astro. So having both metrics is super useful, because if a faulty API is lagging the ending of the streaming, the server could start to accumulate incoming requests and active handles, leading to cpu or resource starvation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚨 action Modifies GitHub Actions core docs pr feat: integration api related to the public integration APIs (scope) feat: ssr Related to SSR (scope) pkg: astro Related to the core `astro` package (scope) pkg: example Related to an example package (scope) pkg: integration Related to any renderer integration (scope) semver: minor Change triggers a `minor` release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants