Skip to content
Draft
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2c0d424
Basic tracing hooks
Fryuni May 12, 2025
ce2de17
Merge branch 'main' into feat/tracing-hooks
Fryuni May 12, 2025
15fe631
Add extra trace points, data and make tracing platform independent
Fryuni May 12, 2025
f7ed4da
Run formatter
Fryuni May 12, 2025
741be1e
Rename trace event
Fryuni May 12, 2025
b1833ed
Merge branch 'main' into fryuni/tracing-hooks
Fryuni May 23, 2025
cb8fdcb
Merge branch 'main' into fryuni/tracing-hooks
Fryuni May 31, 2025
f116c3f
Merge remote-tracking branch 'origin/main' into fryuni/tracing-hooks
Fryuni Jul 21, 2025
ebac4ec
format
Fryuni Jul 21, 2025
e32b685
Merge remote-tracking branch 'origin/main' into fryuni/tracing-hooks
Fryuni Jul 21, 2025
628fcf1
Merge remote-tracking branch 'origin/main' into fryuni/tracing-hooks
Fryuni Aug 1, 2025
c46fa13
Make type future-proof
Fryuni Aug 2, 2025
2e1caaa
Add hook for async operation completion
Fryuni Aug 2, 2025
17d26db
Add function for integrations to add early initialization logic
Fryuni Aug 3, 2025
d4e29bc
Working exemple of tracing using OpenTelemetry
Fryuni Aug 3, 2025
cfe14a0
wip changeset for CI
Fryuni Aug 9, 2025
0eae15d
Merge branch 'main' into fryuni/tracing-hooks
Fryuni Aug 9, 2025
0cbc2d9
Preview unreleased package
Fryuni Aug 9, 2025
dc3a5f8
Refactor OpenTelemetry integration to be configurable and adaptable
Fryuni Aug 9, 2025
cfbe4bc
Add tracing to middlewares
Fryuni Aug 10, 2025
65601f7
Make logger export to console by default
Fryuni Aug 10, 2025
6979108
Allow integration to be wrapped
Fryuni Aug 10, 2025
c8f7291
Implement helper imports for each telemetry resource
Fryuni Aug 10, 2025
d804ded
Fix package.json
Fryuni Aug 11, 2025
aca5126
Merge remote-tracking branch 'origin/main' into fryuni/tracing-hooks
Fryuni Aug 11, 2025
06b4a5c
Format
Fryuni Aug 11, 2025
ddb6232
WIP
Fryuni Aug 13, 2025
2c970bc
Merge branch 'main' into fryuni/tracing-hooks
Fryuni Aug 15, 2025
fc5a126
Setup telemetry and initialization for dev server
Fryuni Aug 15, 2025
04d8081
API bash instructions
Fryuni Aug 15, 2025
202fc6f
WIP
Fryuni Aug 15, 2025
effa5e3
WIP
Fryuni Aug 15, 2025
3c624ac
Improve example
Fryuni Aug 24, 2025
b43d090
Merge remote-tracking branch 'origin/main' into fryuni/tracing-hooks
Fryuni Aug 29, 2025
b74a37a
Update packages/integrations/opentelemetry/src/initialization/dev.ts
Fryuni Sep 4, 2025
9410c07
Simplify tracing hooks and prevent consumers from breaking internal code
Fryuni Aug 29, 2025
4189a6d
WIP tests
Fryuni Aug 29, 2025
45da97e
Merge branch 'main' into fryuni/tracing-hooks
Fryuni Sep 10, 2025
0b62164
Put tracing behind an experimental flag
Fryuni Sep 10, 2025
268c593
Dedupe deps
Fryuni Sep 10, 2025
684dc12
Fix default env values for Node telemetry
Fryuni Sep 10, 2025
7042a1e
Docs and unit tests
Fryuni Sep 11, 2025
069ec6b
Adjust integration tests
Fryuni Sep 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/great-chairs-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astrojs/opentelemetry': minor
'astro': minor
---

Tracing hooks
2 changes: 1 addition & 1 deletion .github/workflows/preview-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
run: |
packages=$(echo $AFFECTED_PACKAGES | jq -r '.[]' | tr '\n' ' ')
if [ -n "$packages" ]; then
pnpm dlx pkg-pr-new publish --pnpm --compact --no-template $packages
pnpm dlx pkg-pr-new publish --pnpm --no-template $packages
else
echo "No affected packages to publish"
fi
40 changes: 40 additions & 0 deletions API-bash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
1. Pick a project:

- Example: https://github.com/withastro/astro/tree/fryuni/tracing-hooks/examples/with-telemetry
- New project
- Existing project

2. Use this version of Astro: https://pkg.pr.new/withastro/astro@fc5a126
3. Add this integration: https://pkg.pr.new/withastro/astro/@astrojs/opentelemetry@fc5a126
4. Add the integration to `astro.config.mjs/ts`:

```ts
import node from '@astrojs/node';
import opentelemetry from '@astrojs/opentelemetry';
export default defineConfig({
adapter: node({ mode: 'standalone' }),
integrations: [opentelemetry()],
});
```

5. Start this docker compose: https://github.com/withastro/astro/blob/fryuni/tracing-hooks/examples/with-telemetry/docker-compose.yml

Or run `otel-tui`, if you have that installed.

6. Have fun!

## See the data

Traces will be available on http://localhost:16686/

Metrics will be availabe on http://localhost:9090/query

Logs will be shown on the console

## New APIs:

```ts
import tracer from 'astro:otel:tracer';
import meter from 'astro:otel:meter';
import logger from 'astro:otel:logger';
```
18 changes: 18 additions & 0 deletions examples/with-telemetry/astro.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import node from '@astrojs/node';
import opentelemetry from '@astrojs/opentelemetry';
import svelte from '@astrojs/svelte';
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone',
}),
integrations: [svelte(), opentelemetry()],
vite: {
build: {
sourcemap: true,
},
},
});
31 changes: 31 additions & 0 deletions examples/with-telemetry/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
volumes:
developmentData:
driver: local
driver_opts:
type: none
device: ./data/development

services:
jaeger:
image: jaegertracing/all-in-one:1.71.0
ports:
- '6831:6831/udp'
- '16686:16686'

prometheus:
image: prom/prometheus
volumes:
- './prometheus.yaml:/etc/prometheus/prometheus.yml'
ports:
- '9090:9090'

otel:
image: otel/opentelemetry-collector-contrib:0.131.0
command: --config /config/otel.yaml
volumes:
- ./otel.yaml:/config/otel.yaml:ro
ports:
- '4317:4317'
- '4318:4318'
- '8888:8888'
- '8889:8889'
40 changes: 40 additions & 0 deletions examples/with-telemetry/otel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
batch:

exporters:
otlp/jaeger:
endpoint: 'jaeger:4317'
tls:
insecure: true

prometheus:
endpoint: '0.0.0.0:8889'

service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger]

metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]

telemetry:
metrics:
readers:
- pull:
exporter:
prometheus:
host: '0.0.0.0'
port: 8888
22 changes: 22 additions & 0 deletions examples/with-telemetry/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@example/with-telemetry",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"server": "node dist/server/entry.mjs"
},
"dependencies": {
"@astrojs/node": "^9.3.3",
"@astrojs/opentelemetry": "0.0.1",
"@astrojs/svelte": "^7.1.0",
"@opentelemetry/api": "^1",
"@opentelemetry/sdk-node": "^0.203.0",
"astro": "^5.12.8",
"svelte": "^5.25.7"
}
}
11 changes: 11 additions & 0 deletions examples/with-telemetry/prometheus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
global:
scrape_interval: 10s
scrape_configs:
- job_name: otel_collector
static_configs:
- targets:
- otel:8888
- job_name: app
static_configs:
- targets:
- otel:8889
9 changes: 9 additions & 0 deletions examples/with-telemetry/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 90 additions & 0 deletions examples/with-telemetry/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import logger from 'astro:otel:logger';
import meter from 'astro:otel:meter';
import tracer from 'astro:otel:tracer';

export interface Product {
id: number;
name: string;
price: number;
image: string;
}

interface User {
id: number;
}

interface Cart {
items: Array<{
id: number;
name: string;
count: number;
}>;
}

const readCounter = meter.createCounter('api.reads');

async function getJson<T>(incomingReq: Request, endpoint: string): Promise<T> {
readCounter.add(1, { endpoint });

const origin = new URL(incomingReq.url).origin;
try {
const response = await fetch(`${origin}${endpoint}`, {
credentials: 'same-origin',
headers: incomingReq.headers,
});
if (!response.ok) {
throw new Error(`GET ${endpoint} failed: ${response.statusText}`);
}
return (await response.json()) as Promise<T>;
} catch (error) {
if (error instanceof DOMException || error instanceof TypeError) {
throw new Error(`GET ${endpoint} failed: ${error.message}`);
}
logger.emit({
severityText: 'error',
eventName: 'api.error',
body: {
message: `GET ${endpoint} failed: ${error instanceof Error ? error.message : String(error)}`,
endpoint,
stack: error instanceof Error ? error.stack : undefined,
},
});
throw error;
}
}

export function getProducts(incomingReq: Request): Promise<Product[]> {
return tracer.startActiveSpan('getProducts', () =>
getJson<Product[]>(incomingReq, '/api/products'),
);
}

export function getProduct(incomingReq: Request, id: number): Promise<Product> {
return tracer.startActiveSpan('getProduct', () =>
getJson<Product>(incomingReq, `/api/products/${id}`),
);
}

export function getUser(incomingReq: Request): Promise<User> {
return tracer.startActiveSpan('getUser', () => getJson<User>(incomingReq, `/api/user`));
}

export function getCart(incomingReq: Request): Promise<Cart> {
return tracer.startActiveSpan('getCart', () => getJson<Cart>(incomingReq, `/api/cart`));
}

export async function addToUserCart(id: number | string, name: string): Promise<void> {
await fetch(`${location.origin}/api/cart`, {
credentials: 'same-origin',
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
Cache: 'no-cache',
},
body: JSON.stringify({
id,
name,
}),
});
}
54 changes: 54 additions & 0 deletions examples/with-telemetry/src/components/AddToCart.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<script>
import { addToUserCart } from '../api';

let { id, name } = $props()

function notifyCartItem(id) {
window.dispatchEvent(new CustomEvent('add-to-cart', {
detail: id
}));
}

async function addToCart() {
await addToUserCart(id, name);
notifyCartItem(id);
}
</script>
<style>
button {
display:block;
padding:0.5em 1em 0.5em 1em;
border-radius:100px;
border:none;
font-size: 1.4em;
position:relative;
background:#0652DD;
cursor:pointer;
height:2em;
width:10em;
overflow:hidden;
transition:transform 0.1s;
z-index:1;
}
button:hover {
transform:scale(1.1);
}

.pretext {
color:#fff;
background:#0652DD;
position:absolute;
top:0;
left:0;
height:100%;
width:100%;
display:flex;
justify-content:center;
align-items:center;
font-family: 'Quicksand', sans-serif;
text-transform: uppercase;
}
</style>
<button click={addToCart}>
<span class="pretext">Add to cart</span>
</button>
34 changes: 34 additions & 0 deletions examples/with-telemetry/src/components/Cart.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<script>
let { count } = $props()
let items = new Set();

function onAddToCart(ev) {
const id = ev.detail;
items.add(id);
count++;
}
</script>
<style>
.cart {
display: flex;
align-items: center;
text-decoration: none;
color: inherit;
}
.cart :first-child {
margin-right: 5px;
}

.cart-icon {
font-size: 36px;
}

.count {
font-size: 24px;
}
</style>
<svelte:window onadd-to-cart={onAddToCart}/>
<a href="/cart" class="cart">
<span class="material-icons cart-icon">shopping_cart</span>
<span class="count">{count}</span>
</a>
13 changes: 13 additions & 0 deletions examples/with-telemetry/src/components/Container.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
const { tag = 'div' } = Astro.props;
const Tag = tag;
---

<style>
.container {
width: 1248px; /** TODO: responsive */
margin-left: auto;
margin-right: auto;
}
</style>
<Tag class="container"><slot /></Tag>
Loading
Loading