26. Tracing python-oracledb
26.1. Application Tracing
There are multiple approaches for application tracing and monitoring:
End-to-end database tracing attributes such as
Connection.actionandConnection.moduleare supported in the python-oracledb Thin and Thick modes. Using these attributes is recommended since they aid application monitoring and troubleshooting.You can subclass python-oracledb classes and implement your own driver API call tracing and logging. Also, the standard Python tracing capabilities can be used.
The Java Debug Wire Protocol (JDWP) for debugging PL/SQL can be used. See Debugging PL/SQL with the Java Debug Wire Protocol.
Instrumentation libraries such as OpenTelemetry allow sophisticated monitoring, see Using python-oracledb with OpenTelemetry.
Python-oracledb in Thick mode can dump a trace of SQL statements executed. See Low Level SQL Tracing.
The unique connection identifiers that appear in connection error messages, and in Oracle Database traces and logs, can be used to resolve connectivity errors. See Using Connection Identifiers.
Applications that need to observe python-oracledb operation start, completion, or failure events can use database operation callbacks. See Database Operation Callbacks.
26.1.1. Oracle Database End-to-End Tracing
Oracle Database end-to-end application tracing simplifies diagnosing application code flow and performance problems in multi-tier or multi-user environments.
The connection attributes Connection.client_identifier,
Connection.clientinfo, Connection.dbop,
Connection.module, and Connection.action set metadata for
end-to-end tracing. The values can be queried from data dictionary and dynamic
performance views to monitor applications, or you can use tracing
utilities. Values may appear in logs and audit trails.
Also see Connection Metadata and Application Contexts for information about setting Application Contexts.
The Connection.client_identifier attribute is typically set to the name
(or identifier) of the actual end user initiating a query. This allows the
database to distinguish, and trace, end users for applications that connect
using a common database username. It can also be used by Oracle Virtual
Private Database (VPD) policies to automatically limit
data access. Oracle Database’s DBMS_MONITOR package can take advantage of the
client identifer to enable statistics and tracing at an individual level.
The Connection.module and Connection.action attributes can be
set to user-chosen, descriptive values identifying your code architecture.
After attributes are set, the values are sent to the database when the next round-trip to the database occurs, for example when the next SQL statement is executed.
The attribute values will remain set in connections released back to a connection pool. When the application re-acquires a connection from the pool, it should initialize the values to a desired state before using that connection.
The example below shows setting the action, module, and client identifier attributes on a connection object, and then querying a view to see the recorded values. The example both sets and queries the values, but typically monitoring is done externally to the application.
# Set the tracing metadata
connection.client_identifier = "pythonuser"
connection.action = "Query Session tracing parameters"
connection.module = "End-to-end Demo"
for row in cursor.execute("""
SELECT username, client_identifier, module, action
FROM V$SESSION
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""):
print(row)
The output will be like:
('SYSTEM', 'pythonuser', 'End-to-end Demo', 'Query Session tracing parameters')
The values can also be manually set by calling DBMS_APPLICATION_INFO procedures or DBMS_SESSION.SET_IDENTIFIER. These incur round-trips to the database which reduces application scalability:
BEGIN
DBMS_SESSION.SET_IDENTIFIER('pythonuser');
DBMS_APPLICATION_INFO.set_module('End-to-End Demo');
DBMS_APPLICATION_INFO.set_action(action_name => 'Query Session tracing parameters');
END;
The Connection.dbop attribute can be used for Real-Time SQL Monitoring,
see Monitoring Database Operations. The value will
be shown in the DBOP_NAME column of the V$SQL_MONITOR
view:
connection.dbop = "my op"
for row in cursor.execute("""
SELECT dbop_name
FROM V$SQL_MONITOR
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""):
print(row)
26.1.2. Debugging PL/SQL with the Java Debug Wire Protocol
The Java Debug Wire Protocol (JDWP) for debugging PL/SQL can be used with python-oracledb.
Python-oracledb applications that call PL/SQL can step through that PL/SQL code using JDWP in a debugger. This allows Python and PL/SQL code to be debugged in the same debugger environment. You can enable PL/SQL debugging in python-oracledb as follows:
In python-oracledb Thin or Thick modes, set the
ORA_DEBUG_JDWPenvironment variable to host=hostname;port=portnum indicating where the PL/SQL debugger is running. Then run the application.In python-oracledb Thin mode, you can alternatively set the connection parameter
debug_jdwpduring connection. This variable defaults to the value of theORA_DEBUG_JDWPenvironment variable.
See the documentation on DBMS_DEBUG_JDWP, the video PL/SQL debugging with Visual Studio and Visual Studio Code, and the blog post Debugging PL/SQL with Visual Studio Code (and more).
26.1.3. Low Level SQL Tracing
The Thick mode of python-oracledb is implemented using the
ODPI-C wrapper on top of the Oracle Client
libraries. The ODPI-C tracing capability can be used to log executed
python-oracledb statements to the standard error stream. Before executing
Python, set the environment variable DPI_DEBUG_LEVEL to 16 in your terminal
window.
On Linux, you might use:
export DPI_DEBUG_LEVEL=16
On Windows, this could be done with:
set DPI_DEBUG_LEVEL=16
After setting the variable, run the Python Script, for example on Linux:
python end-to-endtracing.py 2> log.txt
For an application that does a single query, the log file might contain a tracing line consisting of the prefix ‘ODPI’, a thread identifier, a timestamp, and the SQL statement executed:
ODPI [23389068] 2025-06-25 12:07:55.405: ODPI-C 5.5.1
ODPI [23389068] 2025-06-25 12:07:55.405: debugging messages initialized at level 16
ODPI [23389068] 2025-06-25 12:08:01.363: SQL select name from jobs
See ODPI-C Debugging for
documentation on DPI_DEBUG_LEVEL.
26.1.4. Using Connection Identifiers
A unique connection identifier (CONNECTION_ID) is generated for each
connection to the Oracle Database. The connection identifier is shown in some
Oracle Network error messages and logs, which helps in better tracing and
diagnosing of connection failures. For example:
DPY-6005: cannot connect to database (CONNECTION_ID=m0PfUY6hYSmWPcgrHZCQIQ==)
Depending on the Oracle Database version in use, the information that is shown in logs varies.
You can define a prefix value which is added to the beginning of the
CONNECTION_ID value. This prefix aids in identifying the connections from a
specific application.
See Troubleshooting Oracle Net Services for more information on connection identifiers.
Python-oracledb Thin mode
In python-oracledb Thin mode, you can specify a prefix using the
connection_id_prefix parameter when creating standalone connections or pooled connections,
or alternatively set a prefix when calling oracledb.ConnectParams() or
oracledb.PoolParams(). For example:
connection = oracledb.connect(user="hr", password=userpwd,
dsn="localhost/orclpdb",
connection_id_prefix="MYAPP")
If this connection to the database fails, MYAPP is added as a prefix to the
CONNECTION_ID value shown in the error message, for example:
DPY-6005: cannot connect to database (CONNECTION_ID=MYAPPm0PfUY6hYSmWPcgrHZCQIQ==).
Python-oracledb Thick mode
In python-oracledb Thick mode, you can specify the connection identifier prefix in the connection string or connect descriptor. For example:
mydb = (DESCRIPTION =
(ADDRESS_LIST= (ADDRESS=...) (ADDRESS=...))
(CONNECT_DATA=
(SERVICE_NAME=sales.us.example.com)
(CONNECTION_ID_PREFIX=MYAPP)
)
)
26.1.5. Tracing Bind Values
Several methods for tracing bind variable values can be used. When tracing bind variable values, be careful not to leak information and create a security problem.
In Oracle Database, the view V$SQL_BIND_CAPTURE can capture bind information. Tracing with Oracle Database’s DBMS_MONITOR package may also be useful.
You can additionally subclass python-oracledb classes and log any bind values.
OpenTelemetry can also be used, see Using python-oracledb with OpenTelemetry.
26.1.6. Database Views for Tracing python-oracledb
This section shows some of the Oracle Database views useful for tracing and monitoring python-oracledb. Other views and columns not described here also contain useful information, such as the Database Resident Connection Pooling (DRCP) views discussed in Monitoring DRCP, and the views discussed in Oracle Database End-to-End Tracing and Tracing Bind Values.
26.1.6.1. V$SESSION
The following table shows sample values for some V$SESSION columns. You may see other values
if you have changed the defaults using the Defaults object
before connecting, set the equivalent connection or pool creation parameters,
or set the attribute Connection.module as shown in
Oracle Database End-to-End Tracing.
Column |
Sample Thin mode value |
Sample Thick mode value |
|---|---|---|
MACHINE |
“myusername-mac” |
“myusername-mac” |
MODULE |
The value of Python’s |
Similar to python@myusername-mac (TNS V1-V3) |
OSUSER |
“myusername” |
“myusername” |
PROGRAM |
The value of Python’s |
Similar to python@myusername-mac (TNS V1-V3) |
TERMINAL |
“unknown” |
Similar to ttys001 |
26.1.6.2. V$SESSION_CONNECT_INFO
The following table shows sample values for some V$SESSION_CONNECT_INFO columns. You may see other
values if you have changed the defaults using the Defaults object before connecting, set the equivalent connection or pool creation
parameters, or set the driver_name parameter in
oracledb.init_oracle_client().
Column |
Sample Thin mode value |
Sample Thick mode value |
|---|---|---|
CLIENT_DRIVER |
“python-oracledb thn : 3.2.0” |
“python-oracledb thk : 3.2.0” |
CLIENT_OCI_LIBRARY |
“Unknown” |
The Oracle Client or Instant Client type, such as “Full Instant Client” |
CLIENT_VERSION |
“3.2.0.0.0” (the python-oracledb version number with an extra .0.0) |
The Oracle Client library version number |
OSUSER |
“myusername” |
“myusername” |
26.2. Database Operation Callbacks
Python-oracledb provides callbacks that allow applications and integrations to observe database activity without changing how connection, cursor, or pool objects are used. The two callback types supported by python-oracledb are operation callbacks and round-trip callbacks. Operation callbacks observe python-oracledb API operations, while round-trip callbacks observe lower-level protocol exchanges.
Warning
Callbacks must not perform database operations using the connection whose operation or round-trip is currently being processed.
26.2.1. Operation Callbacks
Operation callbacks observe public python-oracledb operations such as statement execution, fetching, commits, rollbacks, LOB access, AQ operations, and connection management calls. They are useful for tracing, metrics collection, profiling, diagnostic logging, and other integrations that need to record operation timing or failures without wrapping python-oracledb objects. This callback type is supported in both python-oracledb Thin and Thick modes.
Before each supported operation starts, python-oracledb invokes the operation callback. An operation callback should have the following signature:
operation_callback(name: str, arguments: dict) -> Callable | None
The name parameter is the operation callback name, such as “execute”,
“fetchone”, “commit”, or “rollback”. See the
Operation Callback Names table for the list of supported operation
callback names. The arguments parameter is a mapping containing the
arguments passed to the operation. The available arguments depend on the
operation. For example, an “execute” operation may include the SQL statement
and bind parameters. The argument mapping can contain sensitive values such as
passwords. The callback should access only the specific argument values needed,
and should not log, record, or export the complete mapping.
The operation callback can optionally return a completion function. If a
completion function is returned, python-oracledb invokes it after the operation
succeeds or fails. The completion function receives the operation result when
the operation succeeds, or the exception object when the operation fails. If
the callback returns None, no completion function is invoked. Returning any
other non-callable value raises the DPY-2070 error before database
communication starts. The optional completion function should have this
signature:
completion(result_or_exception) -> None
Operation callbacks and their completion functions are synchronous when used
with both synchronous Connection and asynchronous
AsyncConnection operations. They must be regular callables and must
not be defined with async def.
In the following example, before_operation() prints the operation name
before it starts. Since it returns after_operation(), the completion
function is invoked after the operation completes:
def before_operation(name, arguments):
print("starting", name)
def after_operation(result_or_exception):
print("completed", result_or_exception)
return after_operation
An exception raised by an operation callback before an operation starts
prevents that operation from starting. If a completion function raises an
exception after a successful operation, that exception is propagated. If both
the operation and its completion function fail, the completion function
exception is propagated. The original operation exception is available from
the completion exception’s __context__ attribute.
You can specify an operation callback with the operation_callback parameter
when creating a standalone connection or pooled connection with
oracledb.connect(), oracledb.create_pool(),
oracledb.connect_async(), or oracledb.create_pool_async(). For
example:
connection = oracledb.connect(
user=user,
password=password,
dsn=dsn,
operation_callback=before_operation,
)
An operation callback can also be set or changed on an existing connection
with the Connection.operation_callback or
AsyncConnection.operation_callback attributes. This change takes
effect on the next operation. For example:
connection.operation_callback = before_operation
To disable an operation callback on an existing connection, set
Connection.operation_callback or
AsyncConnection.operation_callback to None. For example:
connection.operation_callback = None
Also, you can specify an operation callback with
ConnectParams.operation_callback or with the operation_callback in
oracledb.PoolParams(). An operation callback set using
oracledb.PoolParams() becomes the default for connections acquired from
that pool. The pool defaults are restored on every acquisition, so callback
changes made to an acquired connection do not affect the next time that
connection is acquired from the pool.
The following table lists the operation names that can be passed to an operation callback and the python-oracledb methods that use each name:
Operation name |
python-oracledb Methods |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some python-oracledb methods are not included in the table. Pool management methods do not invoke operation callbacks because the pool performs database communication in its background worker. Convenience methods that call another callback-enabled method do not invoke a separate callback.
Running a pipeline is treated as a single operation, even though it can perform one or more round-trips. SODA, database startup, and database shutdown operations are not currently covered by operation callbacks.
26.2.2. Round-trip Callbacks
Round-trip callbacks observe the lower-level protocol round-trips made by python-oracledb Thin mode connections to Oracle Database. An operation can perform zero, one, or multiple round-trips, so round-trip callbacks provide a finer level of detail than operation callbacks. They are useful for tracing, metrics collection, profiling, and diagnostic logging that needs visibility into individual database protocol exchanges. For example, they can be used to measure individual round-trip timings, identify operations that perform multiple round-trips, and help diagnose latency caused by network or database response time.
Round-trip callbacks are only supported in python-oracledb Thin mode. A round-trip callback should have the following signature:
round_trip_callback(name: str) -> Callable | None
The name parameter is the protocol operation name, such as “execute” or
“fetch”. The round-trip callback can optionally return a completion function.
If the callback returns None, no completion function is invoked. Returning
any other non-callable value raises the DPY-2070 error before database
communication starts. The optional completion function should have this
signature:
completion(exception_or_none) -> None
Round-trip callbacks and their completion functions are synchronous when used
with both synchronous Connection and asynchronous
AsyncConnection operations. They must be regular callables and must
not be defined with async def.
The round-trip completion function receives None on success or the raised exception on failure. A callback pair is invoked for each named protocol exchange processed by the connection.
In the following example, before_round_trip() prints the protocol operation
name before the round-trip starts. Since it returns after_round_trip(), the
completion function is invoked after the round-trip completes:
def before_round_trip(name):
print("starting", name)
def after_round_trip(exception):
print("completed", exception)
return after_round_trip
An exception raised by a round-trip callback before a round-trip starts
prevents that round-trip from starting. If a completion function raises an
exception after a successful round-trip, that exception is propagated. If both
the operation and its completion function fail, the completion function
exception is propagated. The original operation exception is available from
the completion exception’s __context__ attribute.
You can specify a round-trip callback with the round_trip_callback
parameter when creating a standalone connection or pooled connection with
oracledb.connect(), oracledb.create_pool(),
oracledb.connect_async(), or oracledb.create_pool_async(). For
example:
connection = oracledb.connect(
user=user,
password=password,
dsn=dsn,
round_trip_callback=before_round_trip,
)
A round-trip callback can also be set or changed on an existing connection
with the Connection.round_trip_callback or
AsyncConnection.round_trip_callback attributes. This change takes
effect on the next round-trip. For example:
connection.round_trip_callback = before_round_trip
To disable a round-trip callback on an existing connection, set
Connection.round_trip_callback or
AsyncConnection.round_trip_callback to None. For example:
connection.round_trip_callback = None
Also, you can specify a round-trip callback with
ConnectParams.round_trip_callback or with the round_trip_callback
parameter in oracledb.PoolParams(). When a round-trip callback is
configured in ConnectParams, it can also observe python-oracledb Thin
mode round-trips made while a standalone connection is being established. A
round-trip callback set using oracledb.PoolParams() becomes the default
for connections acquired from that pool. The pool defaults are restored on
every acquisition, so callback changes made to an acquired connection do not
affect the next time that connection is acquired from the pool.
26.3. Using python-oracledb with OpenTelemetry
The OpenTelemetry observability framework is useful for monitoring applications and identifying bottlenecks.
OpenTelemetry’s backend trace exporters can provide
graphic and intuitive representation of OpenTelemetry trace information.
Recording and reporting tools include Zipkin, Jaeger, Grafana, and Prometheus.
These make database query relationships and timings easier to analyze. Simple
tracing can also be directed to the console by making use of the exporter
ConsoleSpanExporter from the opentelemetry-sdk package.
Python-oracledb provides a pre-supplied OpenTelemetry plugin, opentelemetry, for creating spans from the operation and python-oracledb Thin mode round-trip callbacks, and records operation-duration metrics from operation callbacks.
Warning
Do not enable the opentelemetry plugin with another Oracle Database
OpenTelemetry instrumentation in the same process because duplicate
database spans will be created.
To use the opentelemetry plugin, you must:
Install the OpenTelemetry module, see Installing OpenTelemetry Modules for python-oracledb.
Import the opentelemetry plugin in your application using:
from oracledb.plugins import opentelemetry
Enable the
opentelemetryplugin using the functionoracledb.plugins.opentelemetry.instrument():opentelemetry.instrument()
After the plugin is enabled, python-oracledb creates OpenTelemetry spans for
newly created connections and pools. Note that only installing the optional
dependency and importing the plugin without calling
oracledb.plugins.opentelemetry.instrument() does not enable
instrumentation.
You can specify the following optional parameters in the
oracledb.plugins.opentelemetry.instrument() function to customize span
creation:
require_parent_spanto create spans only when there is an active parent span.enable_database_trace_propagationto request database-side tracing for supported Thin mode round trips and propagate OpenTelemetry trace context so database spans can be linked to application spans. This requires Oracle AI Database 26ai (version 23.26.2 or later) to advertise OpenTelemetry tracing support. Oracle Database OpenTelemetry must also be configured on the server.db_statement_dumpto record SQL statement text in spans for operations that do not include bind parameters. SQL text for parameterized statements may be recorded even when this option is False.tracer_providerto specify a custom OpenTelemetry trace provider for creating and exporting spans.meter_providerto specify a custom OpenTelemetry meter provider that is used to create and export metrics.request_hookandresponse_hookto specify callables that can inspect or customize spans when database operations start and finish.enhanced_database_reportingto record additional database details.
See oracledb.plugins.opentelemetry.instrument() for more information
on these parameters.
Warning
SQL text can be recorded in spans for parameterized statements even when
db_statement_dump is set to False. Setting db_statement_dump to
True additionally records SQL text for statements without bind
parameters. SQL text may contain sensitive information, so ensure it is
safe for the telemetry destination.
The enhanced_database_reporting option records additional database
details, including bind values. Bind values may contain sensitive
application data and should only be enabled when they are safe for the
telemetry destination.
An example of setting some of the opentelemetry.instrument() parameters
is shown below:
opentelemetry.instrument(
require_parent_span=True,
enable_database_trace_propagation=True,
db_statement_dump=True,
)
This example creates spans only when there is an active parent span, requests database-side tracing and propagates trace context for supported Thin mode round trips, and records SQL statement text in spans.
To disable OpenTelemetry instrumentation, and prevent newly created connections and pools from being instrumented, call:
opentelemetry.uninstrument()
Calling oracledb.plugins.opentelemetry.instrument() while
instrumentation is already enabled raises a RuntimeError. To change the
instrumentation settings, call
oracledb.plugins.opentelemetry.uninstrument() before calling
oracledb.plugins.opentelemetry.instrument() again with the new values
specified.
The plugin can also instrument an already open connection with
oracledb.plugins.opentelemetry.instrument_connection(). This is useful
when a connection was created before
oracledb.plugins.opentelemetry.instrument() was called, or when you
want to instrument only selected connections instead of all
newly created connections and pools. The function adds OpenTelemetry callbacks
to the connection in place while preserving any existing operation and
round-trip callbacks. To remove instrumentation from a connection and restore
its original callbacks, call
oracledb.plugins.opentelemetry.uninstrument_connection(). For example:
connection = oracledb.connect(user="hr", password=userpwd,
dsn="localhost/orclpdb")
opentelemetry.instrument_connection(connection)
with connection.cursor() as cursor:
cursor.execute("select 'Hello World!' from dual")
print(cursor.fetchone())
opentelemetry.uninstrument_connection(connection)
Calling oracledb.plugins.opentelemetry.instrument_connection() again
for the same connection raises a RuntimeError. Calling
oracledb.plugins.opentelemetry.uninstrument_connection() for a
connection that is not instrumented has no effect. You must call
oracledb.plugins.opentelemetry.uninstrument_connection() before
instrumenting the connection again.
The opentelemetry plugin uses the OpenTelemetry API to create spans, but
the API does not record or export them. To record and export spans, also install
opentelemetry-sdk and a suitable exporter. For installation instructions,
see
Installing OpenTelemetry Modules for python-oracledb.
After installing these packages, configure an OpenTelemetry tracer provider and
exporter before enabling the opentelemetry plugin. For example, the
following uses ConsoleSpanExporter and ConsoleMetricExporter to
write spans and metrics to the console:
from opentelemetry import metrics, trace
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import (
ConsoleMetricExporter,
PeriodicExportingMetricReader,
)
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleSpanProcessor,
)
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
trace.set_tracer_provider(provider)
meter_provider = MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(ConsoleMetricExporter())
]
)
metrics.set_meter_provider(meter_provider)
from oracledb.plugins import opentelemetry
opentelemetry.instrument()
The opentelemetry plugin can trace python-oracledb operations that invoke
the driver’s operation callbacks. Operations that are not supported by
operation callbacks, such as SODA, database startup, and database shutdown,
are not currently traced by the plugin. See
operation callbacks for more information.
26.3.1. OpenTelemetry Metrics
The opentelemetry plugin records the stable OpenTelemetry database client
operation-duration metric,
db.client.operation.duration, in seconds for each operation covered by the
operation callback. The metric uses the meter provider specified in the
meter_provider parameter of
oracledb.plugins.opentelemetry.instrument() or
oracledb.plugins.opentelemetry.instrument_connection(). If this parameter
is not set, then the global OpenTelemetry MeterProvider is used.
Metric attributes are limited to db.system.name, db.operation.name,
db.operation.batch.size, db.namespace, server.address,
server.port, and error.type for failed operations. SQL text, bind
values, hook payloads, result data, connection strings, and credentials are
never recorded as metric attributes. Pool metrics are not currently emitted.
26.3.2. Example of Using python-oracledb with OpenTelemetry
This example configures console exporters for spans and metrics, enables python-oracledb’s OpenTelemetry plugin, and executes a parameterized query inside an application span.
import oracledb
from opentelemetry import metrics, trace
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import (
ConsoleMetricExporter,
PeriodicExportingMetricReader,
)
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
ConsoleSpanExporter,
SimpleSpanProcessor,
)
from oracledb.plugins import opentelemetry
tracer_provider = TracerProvider()
tracer_provider.add_span_processor(
SimpleSpanProcessor(ConsoleSpanExporter())
)
trace.set_tracer_provider(tracer_provider)
meter_provider = MeterProvider(
metric_readers=[
PeriodicExportingMetricReader(ConsoleMetricExporter())
]
)
metrics.set_meter_provider(meter_provider)
opentelemetry.instrument()
connection = oracledb.connect(
user="hr",
password=userpwd,
dsn="localhost/orclpdb",
)
tracer = trace.get_tracer("example")
with tracer.start_as_current_span("application-query"):
with connection.cursor() as cursor:
cursor.execute(
"select city from locations where location_id = :1",
[2200],
)
print(cursor.fetchone())
connection.close()
The following console output is abbreviated and shows the application span and database operation span. It does not show the python-oracledb Thin mode round-trip child spans that the plugin may also create.
('Sydney',)
{
"name": "SELECT localhost",
"context": {
"trace_id": "0x...",
"span_id": "0x..."
},
"kind": "SpanKind.CLIENT",
"parent_id": "0x<Application span ID>",
"status": {
"status_code": "UNSET"
},
"attributes": {
"db.system.name": "oracle.db",
"db.operation.name": "SELECT",
"db.query.text":
"select city from locations where location_id = :1",
"server.address": "localhost"
}
}
{
"name": "application-query",
"context": {
"trace_id": "0x...",
"span_id": "0x<Application span ID>"
},
"kind": "SpanKind.INTERNAL",
"parent_id": null,
"status": {
"status_code": "UNSET"
},
"attributes": {}
}
The query result is displayed before the OpenTelemetry output. The output then
shows a database span created by the python-oracledb opentelemetry plugin
and the surrounding application span.
Operation-duration metrics are exported separately from spans when a
MeterProvider and metric reader are configured. A representative
operation-duration metric for the example would look like this:
{
"name": "db.client.operation.duration",
"description": "Duration of database client operations",
"unit": "s",
"data": {
"data_points": [
{
"attributes": {
"db.system.name": "oracle.db",
"db.operation.name": "SELECT",
"server.address": "localhost"
},
"count": 1,
"sum": 0.003,
"min": 0.003,
"max": 0.003
}
]
}
}
The plugin also records the db.client.operation.duration metric for the
SELECT operation. The metric records the operation duration in seconds and
includes database metadata such as db.system.name, db.operation.name,
and server.address.
Metric values such as count, sum, min, and max depend on the
operations recorded during the export interval. SQL text and bind values are
not included in metric attributes.
26.3.3. OpenTelemetry and extended python-oracledb functionality
Python-oracledb can pass application trace context to Oracle Database to correlate application spans with database work. OpenTelemetry APIs can also be used to trace driver calls explicitly.
Propagating OpenTelemetry Trace Context
An instrumentation library can pass the current W3C traceparent value and
optional tracestate value to
Connection.set_opentelemetry_trace_context(). Python-oracledb formats
and sends them as the ora$opentelem$tracectx attribute in the
CLIENTCONTEXT application context namespace on the next round-trip. Pass
None as traceparent to disable tracing and clear the OpenTelemetry trace
context. Other values in the CLIENTCONTEXT namespace are unchanged.
Python-oracledb does not depend on the OpenTelemetry SDK or create the trace
values.
Enabling Oracle Database OpenTelemetry
Oracle Database OpenTelemetry was introduced in Oracle AI Database 26ai (version 23.26.2). It must be configured and enabled in the database server to collect database server-side spans. For information on configuring Oracle Database OpenTelemetry, see Observability with OpenTelemetry and DBMS_OBSERVABILITY.
When connected to Oracle AI Database 26ai (version 23.26.2) or later,
python-oracledb Thin mode can use
Connection.set_opentelemetry_trace_context() or
AsyncConnection.set_opentelemetry_trace_context() to request database
server-side tracing and correlate it with application spans. These methods are
not supported in python-oracledb Thick mode. If you use Oracle AI Database 26ai
versions earlier than 23.26.2, the DPY-3044 error is raised. Before calling
these methods, you can use Connection.supports_opentelemetry_tracing or
AsyncConnection.supports_opentelemetry_tracing to check whether the
database supports OpenTelemetry tracing.
For example:
connection.set_opentelemetry_trace_context(traceparent, tracestate)
Pass None as traceparent to disable tracing and clear its context:
connection.set_opentelemetry_trace_context(None)
Changes take effect on the next database round-trip.
Ensure that Oracle Database OpenTelemetry is configured and enabled on the database server before using this method. Database server-side spans are exported by Oracle Database to the database’s configured OpenTelemetry endpoint or collector; they are not returned to python-oracledb.
To view application and Oracle Database spans together, configure both the Python application and Oracle Database to export spans to the same collector or observability backend.
Database server-side OpenTelemetry spans can provide details about work inside Oracle Database for a traced operation, such as SQL execution, timing, waits, errors, and other database-side execution information. The exact span attributes and events depend on the Oracle Database release and database OpenTelemetry configuration.
Instrumenting Extended python-oracledb Features
For python-oracledb features that are not part of the Python DB API standard,
you can add explicit instrumentation. For example, to monitor a call to
Connection.fetch_df_all(), add a tracer like:
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("myDFQuery"):
sql = "select city from locations where country_id = :1"
odf = connection.fetch_df_all(sql, ['UK'])
print(odf.num_rows())
The new OpenTelemetry span will be like:
{
"name": "myDFQuery",
"context": {
"trace_id": "0x8512a9fac568c07fc16cd872f68d0346",
"span_id": "0x03f424111825540f",
"trace_state": "[]"
},
"kind": "SpanKind.INTERNAL",
"parent_id": null,
"start_time": "2025-10-06T01:20:34.200129Z",
"end_time": "2025-10-06T01:20:39.212618Z",
"status": {
"status_code": "UNSET"
},
"attributes": {},
"events": [],
"links": [],
"resource": {
"attributes": {
"service.name": "orclepdb",
"db.name": ""
},
"schema_url": ""
}
}
26.4. Finding the python-oracledb Mode
The boolean attributes Connection.thin and ConnectionPool.thin
can be used to find whether python-oracledb is in Thin or Thick mode.
For example, to show the current python-oracledb mode:
print(connection.thin)
The method oracledb.is_thin_mode() can also be used to find the
mode. Immediately after python-oracledb is imported,
oracledb.is_thin_mode() will return True indicating that
python-oracledb defaults to Thin mode. However if a call to
oracledb.init_oracle_client() is made and it returns successfully, then
oracledb.is_thin_mode() will return False, indicating that Thick mode
is enabled. Once the first standalone connection or connection pool is
created, or a successful call to init_oracle_client() is
made, or oracledb.enable_thin_mode() is called, then python-oracledb’s
mode is fixed and the value returned by oracledb.is_thin_mode() will
never change for the lifetime of the process.
For example:
print(oracledb.is_thin_mode())
oracledb.init_oracle_client()
print(oracledb.is_thin_mode())
If the call to init_oracle_client(), succeeds, the code above
prints:
True
False
Mode and version information can also be seen in the Oracle Database data dictionary table V$SESSION_CONNECT_INFO:
with connection.cursor() as cursor:
sql = """SELECT UNIQUE client_driver
FROM V$SESSION_CONNECT_INFO
WHERE sid = SYS_CONTEXT('USERENV', 'SID')"""
for r, in cursor.execute(sql):
print(r)
In python-oracledb Thin mode, the output will be like:
python-oracledb thn : 4.0.0
In python-oracledb Thick mode, the output will be like:
python-oracledb thk : 4.0.0
Note that you may see different values if you have set
oracledb.defaults.driver_name or the
driver_name parameter in oracledb.init_oracle_client().
The python-oracledb version can also be shown with
oracledb.__version__:
print(oracledb.__version__)
26.5. Low Level Python-oracledb Driver Tracing
Low level tracing is mostly useful to maintainers of python-oracledb.
For python-oracledb Thin mode, packets can be traced by setting the environment variable PYO_DEBUG_PACKETS in your terminal window before running your application.
For example, on Linux, you might use:
export PYO_DEBUG_PACKETS=1
On Windows you might set the variable like:
set PYO_DEBUG_PACKETS=1
Alternatively, the variable can be set in the application:
import os os.environ["PYO_DEBUG_PACKETS"] = "1" import oracledb
The output goes to stdout. The information logged is roughly similar to an Oracle Net trace of level 16, see Oracle Net Services TRACE_LEVEL_CLIENT.
Python-oracledb Thick mode can be traced using:
DPI_DEBUG_LEVEL as documented in ODPI-C Debugging.
Oracle Call Interface (OCI) tracing as directed by Oracle Support.
Oracle Net services tracing as documented in Oracle Net Services Tracing Parameters.