Logfire Tutorial: Observability Platform for Python and AI Applications

# Tutorial Logfire: Platform Observability untuk Aplikasi Python dan AI Logfire adalah platform observability buatan tim Pydantic yang dirancang khusus untuk aplikasi Python. Dengan integrasi native...

By Ruby Abdullah · · tutorial
LogfireObservabilityPythonLLM MonitoringPydantic

Logfire Tutorial: Observability Platform for Python and AI Applications

Logfire is an observability platform built by the Pydantic team, designed specifically for Python applications. With native integrations into the Python ecosystem including Pydantic, FastAPI, OpenAI, LangChain, and many other AI/ML libraries, Logfire provides deep monitoring, tracing, and debugging capabilities for modern applications. This tutorial covers how to use Logfire from installation to advanced usage for monitoring AI applications.

Why Logfire?

In AI and ML application development, observability is a critical need. You need to understand how models respond, how long each request takes to process, and where bottlenecks occur. Logfire offers several advantages:

  • Native Python Integration: Built by the Pydantic team, it integrates directly with the Python ecosystem
  • OpenTelemetry-based: Uses the OpenTelemetry standard, making it compatible with other monitoring tools
  • LLM Observability: Dedicated tracking for calls to OpenAI, Anthropic, and other LLM providers
  • SQL Query Interface: Query observability data using SQL directly from the dashboard
  • Auto-instrumentation: Automatically detects and instruments popular Python libraries

Installation and Setup

Prerequisites

  • Python 3.9 or later
  • pip or uv as package manager
  • Logfire account (free to start)

Package Installation

pip install logfire

Or using uv:

uv add logfire

For installation with additional integrations:

pip install "logfire[fastapi]"

pip install "logfire[openai]"

pip install "logfire[anthropic]"

pip install "logfire[langchain]"

pip install "logfire[httpx]"

Authentication

After installation, authenticate with your Logfire account:

logfire auth

This command will open a browser for you to log in to your Logfire account. After successful login, the token will be stored locally.

Creating a Project

logfire projects new my-ai-app

Or use environment variables for CI/CD:

export LOGFIRETOKEN=your-write-token-here

Basic Usage

Hello World with Logfire

import logfire

logfire.configure()

logfire.info("Application started")

with logfire.span("process-data"):

logfire.info("Processing user data", recordcount=100)

with logfire.span("validation"):

logfire.info("Validation complete", valid=95, invalid=5)

with logfire.span("transformation"):

logfire.info("Transformation complete", outputformat="parquet")

logfire.info("Process completed")

The code above creates structured logs with a span hierarchy. Each span represents an operation that can have child spans, forming a trace tree that is easy to read in the Logfire dashboard.

Logging with Structured Data

import logfire

logfire.configure()

userdata = {

"userid": "usr12345",

"email": "user@example.com",

"plan": "pro"

}

logfire.info("User login successful", *userdata)

logfire.warn(

"Rate limit approaching threshold",

currentusage=450,

maxlimit=500,

remaining=50

)

logfire.error(

"Payment processing failed",

orderid="ord789",

errorcode="INSUFFICIENTFUNDS"

)

Logging Levels

Logfire supports several log levels:

logfire.trace("Detailed trace for deep debugging")

logfire.debug("Debugging information")

logfire.info("General information")

logfire.warn("Warning")

logfire.error("Error")

logfire.fatal("Fatal error, application must stop")

Integration with FastAPI

One of Logfire's main strengths is its integration with FastAPI. With a single line of code, the entire request/response cycle is automatically instrumented.

Basic Setup

from fastapi import FastAPI

import logfire

app = FastAPI()

logfire.configure()

logfire.instrumentfastapi(app)

@app.get("/")

async def root():

return {"message": "Hello World"}

@app.get("/users/{userid}")

async def getuser(userid: str):

with logfire.span("fetch-user", userid=userid):

user = await fetchuserfromdb(userid)

logfire.info("User found", username=user["name"])

return user

@app.post("/predict")

async def predict(data: dict):

with logfire.span("ml-prediction"):

logfire.info("Input received", features=len(data))

result = runprediction(data)

logfire.info("Prediction complete", confidence=result["confidence"])

return result

Monitoring Pydantic Validation

Logfire can automatically monitor Pydantic validation:

from pydantic import BaseModel

import logfire

logfire.configure()

logfire.instrumentpydantic()

class UserRequest(BaseModel):

name: str

email: str

age: int

try:

user = UserRequest(name="Ruby", email="ruby@example.com", age="invalid")

except Exception as e:

logfire.error("Validation failed", error=str(e))

With instrumentpydantic(), every Pydantic model validation is recorded in Logfire, including which fields failed validation and why.

Monitoring LLM Applications

OpenAI Integration

import openai

import logfire

logfire.configure()

logfire.instrumentopenai(openai)

client = openai.OpenAI()

response = client.chat.completions.create(

model="gpt-4o",

messages=[

{"role": "system", "content": "You are a helpful assistant."},

{"role": "user", "content": "Explain what machine learning is in 2 sentences."}

],

temperature=0.7

)

print(response.choices[0].message.content)

With instrumentopenai(), Logfire automatically records:

  • The model used
  • Token usage (prompt tokens, completion tokens)
  • Latency of each request
  • Content from prompts and responses
  • Errors and retries

Anthropic Integration

import anthropic

import logfire

logfire.configure()

logfire.instrumentanthropic(anthropic)

client = anthropic.Anthropic()

message = client.messages.create(

model="claude-sonnet-4-6",

maxtokens=1024,

messages=[

{"role": "user", "content": "What are the benefits of using observability?"}

]

)

print(message.content[0].text)

LangChain Monitoring

from langchainopenai import ChatOpenAI

from langchaincore.prompts import ChatPromptTemplate

import logfire

logfire.configure()

logfire.instrumentlangchain()

llm = ChatOpenAI(model="gpt-4o", temperature=0)

prompt = ChatPromptTemplate.frommessages([

("system", "You are an AI expert helping developers."),

("user", "{question}")

])

chain = prompt | llm

response = chain.invoke({

"question": "How do you optimize a RAG pipeline?"

})

print(response.content)

With LangChain instrumentation, every step in the chain is visible in Logfire, including prompt template rendering, LLM calls, and output parsing.

Advanced Usage

Custom Span Attributes

import logfire

logfire.configure()

@logfire.instrument("process-document", extractargs=True)

def processdocument(docid: str, doctype: str, pagecount: int):

logfire.info("Starting document processing")

with logfire.span("extract-text") as span:

text = extracttext(docid)

span.setattribute("charcount", len(text))

with logfire.span("analyze-content") as span:

analysis = analyze(text)

span.setattribute("sentiment", analysis["sentiment"])

span.setattribute("topics", analysis["topics"])

return analysis

The @logfire.instrument decorator automatically records function calls along with their arguments. You can also add custom attributes to spans.

Metrics and Monitoring

import logfire

logfire.configure()

requestcounter = logfire.metriccounter("apirequests")

responsetime = logfire.metrichistogram("responsetimems")

async def handlerequest(request):

requestcounter.add(1, {"endpoint": request.path, "method": request.method})

import time

start = time.time()

response = await processrequest(request)

durationms = (time.time() - start) 1000

responsetime.record(durationms, {"endpoint": request.path})

return response

HTTPX Integration

For monitoring outgoing HTTP requests from your application:

import httpx

import logfire

logfire.configure()

logfire.instrumenthttpx(httpx)

async def fetchexternaldata():

async with httpx.AsyncClient() as client:

response = await client.get("https://api.example.com/data")

return response.json()

Database Integration (SQLAlchemy and Psycopg)

from sqlalchemy import createengine

import logfire

logfire.configure()

engine = createengine("postgresql://user:pass@localhost/mydb")

logfire.instrumentsqlalchemy(engine)

import psycopg

import logfire

logfire.configure()

logfire.instrumentpsycopg(psycopg)

conn = psycopg.connect("postgresql://user:pass@localhost/mydb")

with conn.cursor() as cur:

cur.execute("SELECT FROM users WHERE active = %s", (True,))

users = cur.fetchall()

Scrubbing Sensitive Data

Logfire provides automatic scrubbing for sensitive data:

import logfire

logfire.configure(

scrubbing=logfire.ScrubbingOptions(

extrapatterns=["password", "secret", "token", "apikey", "creditcard"]

)

)

logfire.info(

"User registered",

email="user@example.com",

password="supersecret123"

)

Fields containing sensitive keywords will be automatically masked in the dashboard.

Sampling for High-Volume Applications

import logfire

from logfire import SamplingOptions

logfire.configure(

sampling=SamplingOptions(

head=0.1,

tail=logfire.TailSamplingOptions(

level="error",

durationthreshold=5.0

)

)

)

This configuration will:

  • Sample 10% of all traces randomly (head sampling)
  • Always retain traces containing errors or those with duration exceeding 5 seconds (tail sampling)

Monitoring ML/AI Pipelines

End-to-End ML Pipeline Example

import logfire

import numpy as np

from sklearn.ensemble import RandomForestClassifier

from sklearn.modelselection import traintestsplit

from sklearn.metrics import accuracyscore, f1score

logfire.configure()

def trainmodel(X, y, modelname: str = "randomforest"):

with logfire.span("ml-training-pipeline", modelname=modelname):

with logfire.span("data-split"):

Xtrain, Xtest, ytrain, ytest = traintestsplit(

X, y, testsize=0.2, randomstate=42

)

logfire.info(

"Data split complete",

trainsize=len(Xtrain),

testsize=len(Xtest)

)

with logfire.span("model-training"):

model = RandomForestClassifier(nestimators=100, randomstate=42)

model.fit(Xtrain, ytrain)

logfire.info("Training complete", nestimators=100)

with logfire.span("model-evaluation"):

ypred = model.predict(Xtest)

accuracy = accuracyscore(ytest, ypred)

f1 = f1score(ytest, ypred, average="weighted")

logfire.info(

"Model evaluation",

accuracy=round(accuracy, 4),

f1score=round(f1, 4)

)

return model, {"accuracy": accuracy, "f1score": f1}

Monitoring RAG Pipelines

import logfire

logfire.configure()

async def ragquery(question: str):

with logfire.span("rag-pipeline", question=question):

with logfire.span("embedding-generation"):

embedding = await generateembedding(question)

logfire.info("Embedding created", dimensions=len(embedding))

with logfire.span("vector-search"):

documents = await searchvectors(embedding, topk=5)

logfire.info(

"Documents found",

count=len(documents),

avgscore=np.mean([d["score"] for d in documents])

)

with logfire.span("context-assembly"):

context = "\n".join([d["text"] for d in documents])

logfire.info("Context assembled", charcount=len(context))

with logfire.span("llm-generation"):

response = await generateresponse(question, context)

logfire.info(

"Response generated",

tokencount=response["usage"]["totaltokens"]

)

return response["content"]

Querying Data with SQL

One of Logfire's unique features is the ability to query observability data using SQL directly from the dashboard:

SELECT

spanname,

AVG(duration) as avgdurationms,

COUNT() as totalcalls,

SUM(CASE WHEN level = 'error' THEN 1 ELSE 0 END) as errorcount

FROM spans

WHERE spanname LIKE 'llm-%'

AND createdat > NOW() - INTERVAL '24 hours'

GROUP BY spanname

ORDER BY avgdurationms DESC;

SELECT

attributes->>'model' as model,

AVG(CAST(attributes->>'prompttokens' AS INT)) as avgprompttokens,

AVG(CAST(attributes->>'completiontokens' AS INT)) as avgcompletiontokens,

AVG(duration) as avglatencyms

FROM spans

WHERE spanname = 'openai.chat.completions'

AND createdat > NOW() - INTERVAL '7 days'

GROUP BY model

ORDER BY avglatencyms DESC;

Best Practices

1. Use Meaningful Spans

with logfire.span("process-order", orderid=orderid):

...

Give spans descriptive names and add attributes useful for filtering.

2. Structured Logging

Always use structured data instead of string formatting:

logfire.info("Order processed", orderid=orderid, total=totalamount)

Avoid:

logfire.info(f"Order {orderid} processed with total {totalamount}")

3. Proper Error Tracking

import logfire

try:

result = processdata(data)

except Exception as e:

logfire.error(

"Processing failed",

errortype=type(e).name,

errormessage=str(e),

inputsize=len(data)

)

raise

4. Environment Separation

import os

import logfire

env = os.getenv("ENVIRONMENT", "development")

logfire.configure(

servicename=f"my-app-{env}",

environment=env

)

5. Monitor Token Usage for LLMs

import logfire

async def monitoredllmcall(prompt: str):

with logfire.span("llm-call") as span:

response = await callllm(prompt)

span.setattribute("prompttokens", response.usage.prompttokens)

span.setattribute("completiontokens", response.usage.completiontokens)

span.setattribute("totaltokens", response.usage.totaltokens)

estimatedcost = calculatecost(response.usage)

span.setattribute("estimatedcostusd", estimatedcost)

return response

6. Use Sampling in Production

For high-volume applications, enable sampling to avoid overloading storage:

logfire.configure(

sampling=SamplingOptions(

head=0.1,

tail=logfire.TailSamplingOptions(level="warn")

)

)

Deployment and Production

Docker Setup

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY . .

ENV LOGFIRETOKEN=your-write-token

ENV LOGFIRESERVICENAME=my-app

ENV LOGFIREENVIRONMENT=production

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Environment Variables

LOGFIRETOKEN=your-write-token

LOGFIRESERVICENAME=my-app

LOGFIREENVIRONMENT=production

LOGFIRESENDTO_LOGFIRE=true

Conclusion

Logfire provides a powerful and easy-to-use observability solution for Python applications, especially for AI/LLM applications. With native integrations into Pydantic, FastAPI, OpenAI, and other popular Python libraries, Logfire reduces the boilerplate code typically required for monitoring. The SQL query feature on the dashboard enables flexible observability data analysis, and the auto-instrumentation capability makes setup extremely fast. For teams building AI applications in the Python ecosystem, Logfire is a worthy choice as a primary observability platform.

Related Articles

Complete Guide to TruLens: LLM Application Evaluation and Observability

Panduan Lengkap TruLens: Evaluasi dan Observabilitas Aplikasi LLM Penulis: Ruby Abdullah - PT RUBYTHALIB DATA KONSULTA D...

Opik Tutorial: LLM Evaluation and Observability Platform

Tutorial Opik: Platform Evaluasi dan Observabilitas LLM Opik adalah platform open-source yang dikembangkan oleh Comet un...

Portkey: One AI Gateway to Manage Every LLM Across Many Providers

Portkey: Satu Gateway AI buat Ngatur Semua LLM dari Banyak Provider Temen-temen, kalau kamu udah pernah bikin aplikasi y...

Complete Instructor Tutorial: Structured Outputs from LLMs with Pydantic

Tutorial Lengkap Instructor: Output Terstruktur dari LLM dengan Pydantic Halo temen-temen, di tutorial ini aku mau ngaja...