OpenTelemetry
Modern computing has seen more distributed systems scaled and more services added, such as with microservices and serverless applications. With this growth, service ownership across the system is allocated to different individuals, or even organizations. It is increasingly difficult to observe how these services depend on each and affect other services without a unified observability framework. This becomes clear after a deployment or during an outage when issue identification requires speed and accuracy.
OpenTelemetry is such a unified observability framework. It is a popular open-source observability framework for instrumenting, generating, collecting, and exporting telemetry data. It provides a common specification and protocol so that multiple services can furnish a unified version of traces, metrics, and logs. OpenTelemetry relies on components that participate in the observability model to be instrumented, which means the code emits traces, metrics, and logs.
Numerous managed ODP.NET and ODP.NET Core APIs have been instrumented to support OpenTelemetry observability and standards. Developers and operators can then customize the ODP.NET metrics collected.
ODP.NET OpenTelemetry Traces
Traces record the request paths taken either by an application or end user as they propagate through a multi-service architecture. Traces in OpenTelemetry are defined implicitly by their spans. A trace can be thought of as a directed acyclic graph of spans where the edges between spans are defined as parent/child relationships.
A span, or activity, represents an operation within a transaction. Each span encapsulates the following state:
-
Display name
-
Start timestamp and duration
-
Attributes – list of key-value pairs
-
Events – a tuple (timestamp, name, attributes) and can number from zero or more
-
The parent span identifier
-
Links to causally related spans through SpanContext, which can number from zero or more
ODP.NET spans become child spans if the application creates a (parent) span before calling ODP.NET instrumented APIs.
ODP.NET OpenTelemetry publishes the following activity tags, or attributes.
Table 3-41 ODP.NET OpenTelemetry activity tags
Attribute Name | Description |
---|---|
|
Database name. |
|
Database being accessed by the command. The attribute value is the same as the |
|
Database user |
|
Database statement being executed |
|
Status description of the trace |
|
Status code of the trace |
|
Exception stack trace |
|
Exception type |
|
Exception message |
|
Activity display name or the name of the operation being instrumented. |
|
Server port number |
|
Oracle database SQL statement identifier value, equivalent to |
|
Name of the database host |
ODP.NET OpenTelemetry Trace Sample Exporter Visualization:
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: fdffbc4cbd46442a
Activity.TraceFlags: Recorded
Activity.ParentSpanId: 2d0e9a180b2cc6f1
Activity.ActivitySourceName: Oracle.ManagedDataAccess.Core
Activity.DisplayName: Connect myhostname:1521:dbview
Activity.Kind: Client
Activity.StartTime: 2024-04-08T07:00:13.8434743Z
Activity.Duration: 00:00:00.3927843
Activity.Tags:
db.system: oracle
db.user: bloguser
db.name: dbview
server.address: myhostname
server.port: 1521
StatusCode : Ok
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: 2d0e9a180b2cc6f1
Activity.TraceFlags: Recorded
Activity.ParentSpanId: a5d9f8d14f7c7363
Activity.ActivitySourceName: Oracle.ManagedDataAccess.Core
Activity.DisplayName: Open myhostname:1521:dbview
Activity.Kind: Client
Activity.StartTime: 2024-04-08T07:00:13.7765477Z
Activity.Duration: 00:00:00.5161881
Activity.Tags:
db.system: oracle
db.user: bloguser
db.name: dbview
server.address: myhostname
server.port: 1521
StatusCode : Ok
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: 60c7fd751cfda4b7
Activity.TraceFlags: Recorded
Activity.ParentSpanId: 44c746737d1eb69c
Activity.ActivitySourceName: Oracle.ManagedDataAccess.Core
Activity.DisplayName: SendExecuteRequest myhostname:1521:dbview
Activity.Kind: Client
Activity.StartTime: 2024-04-08T07:00:14.3763571Z
Activity.Duration: 00:00:00.0207091
Activity.Tags:
db.system: oracle
server.address: myhostname
server.port: 1521
db.name: dbview
db.user: bloguser
db.statement: select count(*) from blogs
db.odp.sql_id: 28b6f34d0ft8x
StatusCode : Ok
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: 44c746737d1eb69c
Activity.TraceFlags: Recorded
Activity.ParentSpanId: a5d9f8d14f7c7363
Activity.ActivitySourceName: Oracle.ManagedDataAccess.Core
Activity.DisplayName: ExecuteNonQuery myhostname:1521:dbview
Activity.Kind: Client
Activity.StartTime: 2024-04-08T07:00:14.3369387Z
Activity.Duration: 00:00:00.1049166
Activity.Tags:
db.system: oracle
server.address: myhostname
server.port: 1521
db.name: dbview
db.user: bloguser
db.statement: select count(*) from blogs
db.odp.sql_id: 28b6f34d0ft8x
StatusCode : Ok
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: e503c191cb27b09c
Activity.TraceFlags: Recorded
Activity.ParentSpanId: a5d9f8d14f7c7363
Activity.ActivitySourceName: Oracle.ManagedDataAccess.Core
Activity.DisplayName: Close myhostname:1521:dbview
Activity.Kind: Client
Activity.StartTime: 2024-04-08T07:00:14.4862863Z
Activity.Duration: 00:00:00.0122342
Activity.Tags:
db.system: oracle
server.address: myhostname
server.port: 1521
db.name: dbview
db.user: bloguser
StatusCode : Ok
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
Activity.TraceId: ea0203e89f932940e0ffd0c6e3ee0913
Activity.SpanId: a5d9f8d14f7c7363
Activity.TraceFlags: Recorded
Activity.ActivitySourceName: TEST
Activity.DisplayName: SampleTestActivity
Activity.Kind: Internal
Activity.StartTime: 2024-04-08T07:00:13.6475302Z
Activity.Duration: 00:00:00.9192665
Resource associated with Activity:
service.name: DemoApp
service.version: 1.0.0
service.instance.id: d16fd8e5-8c49-4489-900b-55af928044c6
ODP.NET OpenTelemetry supports both dynamic and manual instrumentation. The next section describes how to set up each instrumentation method.
Dynamic and Manual Instrumentation
ODP.NET OpenTelemetry requires an exporter, such Jaeger or Zipkin, to visualize and analyze traces. These exporters are available as NuGet packages and will also include the OpenTelemetry SDK as a NuGet dependency.
ODP.NET OpenTelemetry manual instrumentation has the following requirements:
-
Oracle.ManagedDataAccess.OpenTelemetry
package available from NuGet Gallery. -
Exporter NuGet package, such as Console or Zipkin, which is used for instrumentation visualization.
-
OpenTelemetry SDK, which will be installed automatically with an exporter NuGet package.
Once the ODP.Net OpenTelemetry
nuget package is installed, ODP.NET OpenTelemetry is enabled by invoking the TracerProviderBuilder
AddOracleDataProviderInstrumentation
extension method upon application startup. It accepts various options for configuring OpenTelemetry instrumentation.
Code sample: Enabling ODP.NET OpenTelemetry instrumentation with default options
using OpenTelemetry.Trace; Sdk.CreateTracerProviderBuilder() .AddOracleDataProviderInstrumentation() // ODP.NET extension method .AddConsoleExporter() .Build();
Code sample: Enabling ODP.NET OpenTelemetry instrumentation with all options enabled
using OpenTelemetry.Trace; Sdk.CreateTracerProviderBuilder() .AddOracleDataProviderInstrumentation(o => { o.EnableConnectionLevelAttributes = true; o.RecordException = true; o.InstrumentOracleDataReaderRead = true; o.SetDbStatementForText = true; }) .AddConsoleExporter() .Build();
After enabling ODP.NET OpenTelemetry, whenever a provider instrumented API is called, OpenTelemetry traces will be generated and sent to the configured exporter.
By default, instrumentation for potentially sensitive data is disabled, such as for SQL statements.
A second manual instrumentation method allows enabling ODP.NET OpenTelemetry without adding the ODP.NET OpenTelemetry NuGet package and without calling the AddOracleDataProviderInstrumentation()
extension method. It requires adding the TracerProviderBuilder
call AddSource(“Oracle.ManagedDataAccess.Core”)
for ODP.NET Core or AddSource(“Oracle.ManagedDataAccess”)
for managed ODP.NET.
ODP.NET OpenTelemetry instrumentation will then be enabled using default options. The defaults must be used as there is no way to modify option values. Changing these values require using ODP.NET OpenTelemetry NuGet package directly.
OpenTelemetry.Api
NuGet package is installed automatically if the application adds any OpenTelemetry exporter NuGet packages, such as OpenTelemetry.Exporter.Console
or OpenTelemetry.Exporter.Zipkin
. OpenTelemetry.Api
is necessary to call the AddSource()
method.
Code sample: Enabling ODP.NET OpenTelemetry manual instrumentation via AddSource
Sdk.CreateTracerProviderBuilder()
.AddSource("Oracle.ManagedDataAccess.Core")
.AddConsoleExporter()
.Build();
Through this instrumentation method, it is possible to enable and disable ODP.NET OpenTelemetry tracing programmatically at runtime dynamically. To enable, set OracleConfiguration
OpenTelemetryTracing
to true
(default). To disable, set it to false
.
OpenTelemetryTracing
is available as a managed ODP.NET .NET configuration file setting as well. As a .NET config setting, it is read upon app startup only. Changes to this setting's value after startup are ignored.
Automatic Instrumentation
OpenTelemetry automatic instrumentation enables ODP.NET tracing without requiring code changes. Existing .NET apps can turn on tracing without having to recompile.
To use it, add the OpenTelemetry.AutoInstrumentation
NuGet package to your project. Older package versions require setting environment variables to enable ODP.NET tracing. For example, the following environment variable settings turn on ODP.NET Core tracing using the console exporter.
OTEL_DOTNET_AUTO_TRACES_ADDITIONAL_SOURCES=Oracle.ManagedDataAccess.Core
OTEL_DOTNET_AUTO_TRACES_CONSOLE_EXPORTER_ENABLED=true
ODP.NET automatic instrumentation is on by default, without requiring environment variables, when using a newer OpenTelemetry.AutoInstrumentation
NuGet package.
Instrumented ODP.NET APIs
The list of managed ODP.NET and ODP.NET Core APIs that are instrumented to support OpenTelemetry is as follows. Every ODP.NET database round trip originating from one of these APIs is instrumented as well.
-
OracleCommand
-
ExecuteNonQuery()
-
ExecuteNonQueryAsync(CancellationToken cancellationToken)
-
ExecuteNonQueryAsync()
-
All
ExecuteReader
overloads -
All
ExecuteReaderAsync
overloads -
ExecuteScalar()
-
ExecuteScalarAsync(CancellationToken cancellationToken)
-
ExecuteStream()
-
ExecuteToStream(Stream)
-
ExecuteXmlReader()
-
All
ExecuteXmlReaderAsync
overloads
-
-
OracleConnection
-
Close()
-
Open()
-
OpenAsync()
-
-
OracleDataAdapter
-
All
Fill
overloads
-
-
OracleDataReader
-
Read()
-
ReadAsync()
-
See Also:
Oracle Data Provider for .NET OpenTelemetry and Metrics Classes
OpenTelemetry documentation