Observations from other libraries
By default the SDK exports only its own observations and LLM-related spans. Other OpenTelemetry spans are dropped, and their children lose their place in the tree.
Because Langfuse is built on OpenTelemetry, any library that records OpenTelemetry spans can appear in a trace: a database driver, an HTTP client, a framework. Lesson 15's hook sees any of them that are exported. This lesson is about which ones are.
from opentelemetry import trace
ORDERS = {"A17": "shipped on 3 March"}
database = trace.get_tracer("shop-db")
@observe(name="lookup-order", as_type="tool")
def lookup_order(order_id):
with database.start_as_current_span("SELECT orders"):
with langfuse.start_as_current_observation(name="parse-row"):
return ORDERS.get(order_id, "not found")trace.get_tracer is plain OpenTelemetry, the way a database library would record its queries. The query span sits inside the Langfuse tool, and a Langfuse observation sits inside the query.
lookup_order("A17")
langfuse.flush()
local_langfuse.tree()python third_party.pyThe query is missing, and parse-row appears as a root of its own. Its parent was the query span, which was filtered out, so the server has an observation whose parent never arrived. The default filter keeps observations created by the Langfuse SDK, spans with gen_ai.* attributes, and spans from a list of known LLM libraries; shop-db is none of these.
Keeping a library's spans
from langfuse import Langfuse, observe
from langfuse.span_filter import is_default_export_span
import local_langfuse
def keep(span):
return is_default_export_span(span) or span.instrumentation_scope.name == "shop-db"
url = local_langfuse.start()
langfuse = Langfuse(public_key="pk-lf-local", secret_key="sk-lf-local", base_url=url, should_export_span=keep)python third_party.pyshould_export_span replaces the default filter, so keep calls the default, is_default_export_span, and adds one scope. The tree is whole again. To find the name of a scope that is being dropped, turn on debug logging with debug=True or LANGFUSE_DEBUG=True and read the log.
Everything between your code and the server
This is what the five lessons of this part add up to: two gates that can drop a trace, a queue, and one hook that can change what is left.
Pick one to watch it run, step by step.
Export only what you need to debug the LLM part of the application. Every extra scope is more data sent, and a database span's attributes can include the query text, with whatever values were in it.
- Pass
should_export_span=lambda span: Trueand count what arrives. - Use
langfuse.span_filter.is_langfuse_spanas the filter and compare the tree. - Print
span["scope"]for each observation the server received.
You understood something today that you didn't yesterday.