OpenTelemetry Instrumentation for Tortoise ORM
This library provides automatic OpenTelemetry tracing for applications using the Tortoise ORM. It captures database operations (e.g., SELECT, INSERT, UPDATE, DELETE) as spans, enriching them with relevant attributes like query text, table names, and latency. The current version is 0.62b0 and it's part of the `opentelemetry-python-contrib` project, which has a rapid, often weekly, release cadence for beta components.
Warnings
- gotcha This instrumentation (version 0.62b0) is in beta. While generally stable, API surfaces or captured attributes may change in future minor or major releases without strict backward compatibility guarantees.
- breaking The `opentelemetry-instrumentation-tortoiseorm` package has specific version compatibility requirements for `tortoise-orm`. Current versions expect `tortoise-orm>=0.17.0,<0.21.0`. Using an unsupported version might lead to uninstrumented queries or runtime errors.
- gotcha Forgetting to call `TortoiseORMInstrumentor().instrument()` will result in no Tortoise ORM operations being traced. This instrumentation is not automatically enabled by simply importing the package.
- gotcha The instrumentation itself only generates spans; to actually view or export these traces, you must configure an OpenTelemetry `TracerProvider` with appropriate `SpanProcessor` and `SpanExporter` (e.g., `OTLPSpanExporter`, `ConsoleSpanExporter`).
Install
-
pip install opentelemetry-instrumentation-tortoiseorm opentelemetry-sdk -
pip install tortoise-orm
Imports
- TortoiseORMInstrumentor
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
Quickstart
import os
import asyncio
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.instrumentation.tortoiseorm import TortoiseORMInstrumentor
# 1. Setup OpenTelemetry TracerProvider and Exporter
resource = Resource.create({"service.name": "tortoise-orm-app"})
provider = TracerProvider(resource=resource)
# Using ConsoleSpanExporter for demonstration; replace with OTLPSpanExporter for real apps
processor = SimpleSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# 2. Instrument Tortoise ORM
TortoiseORMInstrumentor().instrument()
# 3. Tortoise ORM application code
from tortoise import models, fields, Tortoise
class User(models.Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
class Meta:
table = "users"
def __str__(self):
return self.name
async def main():
# Connect to the database and generate schema
await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
await Tortoise.generate_schemas()
# Perform some ORM operations within a custom span to see the full flow
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("application-flow"):
with tracer.start_as_current_span("create_user"):
user = await User.create(name="Alice")
print(f"Created user: {user.name}")
with tracer.start_as_current_span("fetch_user"):
fetched_user = await User.get(id=user.id)
print(f"Fetched user: {fetched_user.name}")
with tracer.start_as_current_span("update_user"):
fetched_user.name = "Alicia"
await fetched_user.save()
print(f"Updated user to: {fetched_user.name}")
with tracer.start_as_current_span("delete_user"):
await fetched_user.delete()
print(f"Deleted user: {fetched_user.name}")
# Close connection
await Tortoise.close_connections()
if __name__ == "__main__":
asyncio.run(main())