All code available at
list[tuple[datetime, Level, str]][
(datetime(...), 'info', 'GET /accounts/invoice/ -> 200'),
(datetime(...), 'info', 'GET /add-on/complete/ -> 200'),
(datetime(...), 'error', 'Stripe payment failed, insuff...'),
(datetime(...), 'warning', 'POST /payments/42/complete/ -> 400'),
(datetime(...), 'warning', 'POST /payments/37/complete/ -> 400'),
(datetime(...), 'debug', 'Slow query detected SELECT...'),
]This is what code looked like...
until about 1970
class Log(TypedDict):
start: datetime
end: datetime | None
message: str
attributes: dict[str, Any] | None
children: list[Log] | None
list[Log]What we proposing here isn't new, but it's still not available to most developers
[
Log(
start=datetime(...),
end=datetime(...),
message='POST /payments/42/complete/ -> 400',
children=[
Log(
start=datetime(...),
message='Stripe payment failed, insuff...',
attributes={'reason': 'insufficent funds', ...}
),
]
)
]
@app.post('/payments/{user_id:int}/complete/')
def payment_complete(user_id: int):
amount, currency, payment_method = get_payment_details(user_id)
with logfire.span(f'stripe payment {amount=} {currency=} {user_id=}') as span:
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
payment_method=payment_method,
confirm=True,
return_url="https://example.com/return",
)
except stripe.CardError as e:
span.set_level('warning')
span.set_attribute('error', e.error)
else:
span.set_attribute('payment_intent', intent)
if intent is None:
store_payment_failure(user_id)
return JSONResponse(content={'detail': 'Card error'}, status_code=400)
else:
store_payment_success(user_id, intent)No, but these are:
from time import sleep
import logfire
logfire.configure()
logfire.info('Hello {name}', name='world')
activity = 'work'
with logfire.span('doing some slow {activity}...', activity=activity):
logfire.info('{fruit=}', fruit='banana')
sleep(0.123)
with logfire.span('more nesting'):
status = 'ominous'
logfire.warn('this is {status}', status=status)
sleep(0.456)
logfire.info('done')
from time import sleep
import logfire
logfire.configure()
name = 'world'
logfire.info(f'Hello {name}')
activity = 'work'
with logfire.span(f'doing some slow {activity}...'):
fruit = 'banana'
logfire.info(f'{fruit=}')
sleep(0.123)
with logfire.span('more nesting'):
status = 'ominous'
logfire.warn(f'this is {status}')
sleep(0.456)
logfire.info('done')logfire.install_auto_tracing(modules=['dependants', 'bs4.*'], min_duration=0.03)from datetime import datetime
from pydantic import BaseModel
import logfire
logfire.configure()
class Delivery(BaseModel):
timestamp: datetime
dims: tuple[int, int]
input_json = [
'{"timestamp": "2020-01-02T03:04:05Z", "dims": ["10", "20"]}',
'{"timestamp": "2020-01-02T04:04:05Z", "dims": ["15", "25"]}',
'{"timestamp": "2020-01-02T05:04:05Z", "dims": ["20", "30"]}',
]
deliveries = [
Delivery.model_validate_json(json) for json in input_json
]
logfire.info(f'{len(deliveries)} deliveries', deliveries=deliveries)
logfire.instrument_asyncpg()SQLAlchemy
Psycopg
Asyncpg
PyMongo
Sqlite3
Redis
ElasticSearch
MySQL
Kafka
and more...
logfire.instrument_fastapi(app)django
flask
starlette
ASGI
AWS Lambda
falcon
tornado
and more...
from opentelemetry.instrumentation.requests import RequestsInstrumentor
RequestsInstrumentor().instrument()
@app.post('/payments/{user_id:int}/complete/')
def hello(user_id: int):
amount, currency, payment_method = get_payment_details(user_id)
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
payment_method=payment_method,
confirm=True,
return_url="https://example.com/return",
)
except stripe.CardError:
store_payment_failure(user_id)
return JSONResponse(content={'detail': 'Card error'},
status_code=400)
else:
store_payment_success(user_id, intent)
httpx
requests
aiohttp
func main() {
cleanup := initTracerAuto()
defer cleanup(context.Background())
r := gin.Default()
r.Use(otelgin.Middleware("otel-otlp-go-service"))
r.GET("/user/:user", func(c *gin.Context) {
user := c.Param("user")
c.JSON(200, gin.H{
"user": user,
})
})
r.Run(":8080")
}import React from 'react';
import { tracer } from './tracing';
import { context, trace } from '@opentelemetry/api';
function App() {
const makeRequest = () => {
const rootSpan = tracer.startSpan('Click the button');
context.with(trace.setSpan(context.active(), rootSpan), () => {
fetch('http://localhost:8000/hello/demo')
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
// End the root span when all actions are complete
rootSpan.end();
});
});
};from datetime import datetime
from pydantic import BaseModel
import logfire
logfire.configure(pydantic_plugin=logfire.PydanticPlugin(record='all'))
class Delivery(BaseModel):
timestamp: datetime
dims: tuple[int, int]
input_json = [
'{"timestamp": "2020-01-02T03:04:05Z", "dims": ["10", "20"]}',
'{"timestamp": "2020-01-02T04:04:05Z", "dims": ["15", "25"]}',
'{"timestamp": "2020-01-02T05:04:05Z", "dims": ["20", "30"]}',
]
deliveries = [
Delivery.model_validate_json(json) for json in input_json
]
Delivery.model_validate_json(
'{"timestamp": "2020-01-02T03:04:05Z", "dims": ["10"]}'
)
import webbrowser
import openai
import logfire
logfire.configure()
client = openai.Client()
logfire.instrument_openai(client)
with logfire.span('Picture of a cat in the style of a famous painter'):
response = client.chat.completions.create(
model='gpt-4',
messages=[
{'role': 'system', 'content': 'Response entirely in plain text, with just a name'},
{'role': 'user', 'content': 'Who was the influential painter in the 20th century?'},
],
)
chat_response = response.choices[0].message.content
print(chat_response)
response = client.images.generate(
prompt=f'Create an image of a cat in the style of {chat_response}',
model='dall-e-3',
)
url = response.data[0].url
print(url)
webbrowser.open(url)
And on slack after that