Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion helpers/mysql/lib/opentelemetry/helpers/mysql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0module OpenTelemetry
# SPDX-License-Identifier: Apache-2.0

require 'opentelemetry-common'

module OpenTelemetry
Expand Down
5 changes: 5 additions & 0 deletions instrumentation/trilogy/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
inherit_from: ../../.rubocop.yml

Metrics/ModuleLength:
Exclude:
- "lib/opentelemetry/instrumentation/trilogy/patches/stable/client.rb"
- "lib/opentelemetry/instrumentation/trilogy/patches/dup/client.rb"
16 changes: 16 additions & 0 deletions instrumentation/trilogy/.simplecov
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require 'digest'

digest = Digest::MD5.new
digest.update('test')
digest.update(ENV.fetch('BUNDLE_GEMFILE', 'gemfile'))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of the repo-level .simplecov, minus a guard:

digest.update(ENV.fetch('BUNDLE_GEMFILE', 'gemfile')) if ENV['APPRAISAL_INITIALIZED']

Usually appraisals are testing the same test file with different versions. They are overwriting each other's test coverage but it doesn't matter because it still looks like one file is well tested.

But now different appraisals are testing different source files and still overwriting each other's name, so it looks like code is missing tests.

The fix gives each run a unique name so all 6 results are saved and combined. When we are done with all these duplicate files, we can simply delete this file

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just seeing #2272. If that is merged, i'll revert this change, merge that change in, and see if that fixes things


ENV['ENABLE_COVERAGE'] ||= '1'

if ENV['ENABLE_COVERAGE'].to_i.positive?
SimpleCov.command_name(digest.hexdigest)
SimpleCov.start do
add_filter %r{^/test/}
end
end
19 changes: 14 additions & 5 deletions instrumentation/trilogy/Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@
#
# SPDX-License-Identifier: Apache-2.0

appraise 'trilogy-2.9' do
gem 'trilogy', '~> 2.9.0'
end
# To facilitate database semantic convention stability migration, we are using
# appraisal to test the different semantic convention modes along with different
# gem versions. For more information on the semantic convention modes, see:
# https://opentelemetry.io/docs/specs/semconv/non-normative/db-migration/

semconv_stability = %w[old stable dup]

semconv_stability.each do |mode|
appraise "trilogy-2-#{mode}" do
gem 'trilogy', '~> 2.9'
end

appraise 'trilogy-latest' do
gem 'trilogy'
appraise "trilogy-latest-#{mode}" do
gem 'trilogy'
end
end
49 changes: 40 additions & 9 deletions instrumentation/trilogy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,49 @@ OpenTelemetry::Instrumentation::Trilogy.with_attributes('pizzatoppings' => 'mush
end
```

## Configuration Options

| Option | Default | Description |
| ------ | ------- | ----------- |
| `db_statement` | `:obfuscate` | Controls how SQL queries appear in spans. `:obfuscate` replaces literal values with `?`, `:include` records the raw SQL, `:omit` excludes the attribute entirely. |
| `obfuscation_limit` | `2000` | Maximum length of the obfuscated SQL statement. Statements exceeding this limit are truncated. |
| `peer_service` | `nil` | Deprecated with no replacement. Sets the `peer.service` attribute on spans (old semantic conventions only). |
| `propagator` | `'none'` | Propagator for injecting trace context into SQL comments. `'none'` disables propagation, `'tracecontext'` uses W3C Trace Context, `'vitess'` uses Vitess-style propagation (requires `opentelemetry-propagator-vitess` gem). |
| `record_exception` | `true` | Records exceptions as span events when an error occurs. |
| `span_name` | `:statement_type` | Controls span naming (old semantic conventions only). `:statement_type` uses the SQL operation (e.g., `SELECT`), `:db_name` uses the database name, `:db_operation_and_name` combines both. |

## Semantic Conventions

This instrumentation generally uses [Database semantic conventions](https://opentelemetry.io/docs/specs/semconv/database/database-spans/).
This instrumentation generally uses [Database semantic conventions](https://opentelemetry.io/docs/specs/semconv/database/database-spans/). See the [Database semantic convention stability](#database-semantic-convention-stability) section for how to switch between stable and old conventions.

| Stable Attribute Name | Old Attribute Name | Type | Notes |
| - | - | - | - |
| `db.namespace` | `db.name` | String | Database name from connection_options |
| `db.query.text` | `db.statement` | String | The database query being executed; set according to the `db_statement` config option |
| `db.response.status_code` | — | String | The Trilogy error code, if available |
| `db.system.name` | `db.system` | String | DBMS product identifier; always `mysql` |
| `error.type` | — | String | The exception class name when the operation fails |
| `server.address` | `net.peer.name` | String | Database host from connection_options |
| `server.port` | — | Integer | Database port from connection_options |
| — | `db.instance.id` | String | Connected host, e.g. result of `SELECT @@hostname` |
| — | `db.user` | String | Database username from connection_options |
| — | `peer.service` | String | Configured via the `peer_service` config option |

## Database semantic convention stability

In the OpenTelemetry ecosystem, database semantic conventions have now reached a stable state. However, the initial Trilogy instrumentation was introduced before this stability was achieved, which resulted in database attributes being based on an older version of the semantic conventions.

To facilitate the migration to stable semantic conventions, you can use the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. This variable allows you to opt-in to the new stable conventions, ensuring compatibility and future-proofing your instrumentation.

When setting the value for `OTEL_SEMCONV_STABILITY_OPT_IN`, you can specify which conventions you wish to adopt:

- `database` - Emits the stable database and networking conventions and ceases emitting the old conventions previously emitted by the instrumentation.
- `database/dup` - Emits both the old and stable database and networking conventions, enabling a phased rollout of the stable semantic conventions.
- Default behavior (in the absence of either value) is to continue emitting the old database and networking conventions the instrumentation previously emitted.

During the transition from old to stable conventions, Trilogy instrumentation code comes in three patch versions: `dup`, `old`, and `stable`. These versions are identical except for the attributes they send. Any changes to Trilogy instrumentation should consider all three patches.

| Attribute Name | Type | Notes |
| - | - | - |
| `db.instance.id` | String | The name of the DB host executing the query e.g. `SELECT @@hostname` |
| `db.name` | String | The name of the database from connection_options |
| `db.statement` | String | SQL statement being executed |
| `db.user` | String | The username from connection_options |
| `db.system` | String | `mysql` |
| `net.peer.name` | String | The name of the remote host from connection_options |
For additional information on migration, please refer to our [documentation](https://opentelemetry.io/docs/specs/semconv/non-normative/db-migration/).

## How can I get involved?

Expand Down
8 changes: 8 additions & 0 deletions instrumentation/trilogy/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ require 'rubocop/rake_task'

RuboCop::RakeTask.new

# Set OTEL_SEMCONV_STABILITY_OPT_IN based on appraisal name
gemfile = ENV.fetch('BUNDLE_GEMFILE', '')
if gemfile.include?('stable')
ENV['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'database'
elsif gemfile.include?('dup')
ENV['OTEL_SEMCONV_STABILITY_OPT_IN'] = 'database/dup'
end

Rake::TestTask.new :test do |t|
t.libs << 'test'
t.libs << 'lib'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,70 @@
module OpenTelemetry
module Instrumentation
module Trilogy
# The Instrumentation class contains logic to detect and install the Trilogy instrumentation
# The {OpenTelemetry::Instrumentation::Trilogy::Instrumentation} class contains logic to detect and install the Trilogy instrumentation
#
# Installation and configuration of this instrumentation is done within the
# {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry/SDK#configure-instance_method OpenTelemetry::SDK#configure}
# block, calling {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use use()}
# or {https://www.rubydoc.info/gems/opentelemetry-sdk/OpenTelemetry%2FSDK%2FConfigurator:use_all use_all()}.
#
# ## Configuration keys and options
#
# ### `:db_statement`
#
# Controls how SQL queries appear in spans.
#
# - `:obfuscate` **(default)** - Replaces literal values with `?` to prevent
# sensitive data from being recorded.
# - `:include` - Records the raw SQL query as-is.
# - `:omit` - Excludes the SQL query attribute entirely.
#
# ### `:obfuscation_limit`
#
# Maximum length of the obfuscated SQL statement. Statements exceeding this limit
# are truncated. Default is `2000`.
#
# ### `:peer_service`
#
# Sets the `peer.service` attribute on spans. Default is `nil`.
# Only applies when using old semantic conventions. Deprecated with no replacement.
#
# ### `:propagator`
#
# Propagator for injecting trace context into SQL comments.
#
# - `'none'` **(default)** - Disables trace context propagation.
# - `'tracecontext'` - Uses W3C Trace Context format via SQL comments.
# - `'vitess'` - Uses Vitess-style propagation. Requires the
# `opentelemetry-propagator-vitess` gem.
#
# ### `:record_exception`
#
# Records exceptions as span events when an error occurs. Default is `true`.
#
# ### `:span_name`
#
# Controls how span names are generated. Only applies when using old semantic
# conventions; ignored for stable semantic conventions.
#
# - `:statement_type` **(default)** - Uses the SQL operation (e.g., `SELECT`).
# - `:db_name` - Uses the database name.
# - `:db_operation_and_name` - Combines the operation and database name.
#
# @example An explicit default configuration
# OpenTelemetry::SDK.configure do |c|
# c.use_all({
# 'OpenTelemetry::Instrumentation::Trilogy' => {
# db_statement: :obfuscate,
# obfuscation_limit: 2000,
# peer_service: nil,
# propagator: 'none',
# record_exception: true,
# span_name: :statement_type,
# },
# })
# end
#
class Instrumentation < OpenTelemetry::Instrumentation::Base
install do |config|
require_dependencies
Expand All @@ -30,16 +93,47 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base
option :propagator, default: 'none', validate: %w[none tracecontext vitess]
option :record_exception, default: true, validate: :boolean

attr_reader :propagator
attr_reader :propagator, :semconv

private

def require_dependencies
require_relative 'patches/client'
@semconv = determine_semconv

case @semconv
when :old
require_relative 'patches/old/client'
when :stable
require_relative 'patches/stable/client'
when :dup
require_relative 'patches/dup/client'
end
end

def patch_client
::Trilogy.prepend(Patches::Client)
case @semconv
when :old
::Trilogy.prepend(Patches::Old::Client)
when :stable
::Trilogy.prepend(Patches::Stable::Client)
when :dup
::Trilogy.prepend(Patches::Dup::Client)
end
end

def determine_semconv
opt_in = ENV.fetch('OTEL_SEMCONV_STABILITY_OPT_IN', nil)
return :old if opt_in.nil?

opt_in_values = opt_in.split(',').map(&:strip)

if opt_in_values.include?('database/dup')
:dup
elsif opt_in_values.include?('database')
:stable
else
:old
end
end

def configure_propagator(config)
Expand Down

This file was deleted.

Loading
Loading