Skip to content

Commit

Permalink
feat: Initial Sequel instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jrgns committed Sep 24, 2023
1 parent 352812e commit 7eecb44
Show file tree
Hide file tree
Showing 15 changed files with 402 additions and 0 deletions.
1 change: 1 addition & 0 deletions instrumentation/sequel/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
inherit_from: ../../.rubocop.yml
9 changes: 9 additions & 0 deletions instrumentation/sequel/.yardopts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--no-private
--title=OpenTelemetry Sequel Instrumentation
--markup=markdown
--main=README.md
./lib/opentelemetry/instrumentation/**/*.rb
./lib/opentelemetry/instrumentation.rb
-
README.md
CHANGELOG.md
5 changes: 5 additions & 0 deletions instrumentation/sequel/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Release History: opentelemetry-instrumentation-sequel

### v0.1.0 / 2023-09-30

* Initial release
13 changes: 13 additions & 0 deletions instrumentation/sequel/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

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

source 'https://rubygems.org'

gemspec

group :test do
gem 'opentelemetry-instrumentation-base', path: '../base'
end
67 changes: 67 additions & 0 deletions instrumentation/sequel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Sequel Instrumentation

The Sequel instrumentation is a community-maintained instrumentation for the [Sequel][sequel-home] Database Toolkit.

## How do I get started?

Install the gem using:

```
gem install opentelemetry-instrumentation-sequel
```

Or, if you use [bundler][bundler-home], include `opentelemetry-instrumentation-sequel` to your `Gemfile`.

## Usage

To install the instrumentation, add the gem to your Gemfile:

```ruby
gem 'opentelemetry-instrumentation-sequel'
```

Then call `use` with the name of the instrumentation:

```ruby
require 'rubygems'
require 'bundler/setup'

Bundler.require

OpenTelemetry::SDK.configure do |c|
c.use 'OpenTelemetry::Instrumentation::Sequel'
end
```

Alternatively, you can also call `use_all` to install all the available instrumentation.

```ruby
OpenTelemetry::SDK.configure do |c|
c.use_all
end
```

Lastly enable the extension on your DB connection:

```ruby
DB = Sequel.connect('sqlite::memory:')
DB.extension :opentelemetry
```

## How can I get involved?

The `opentelemetry-instrumentation-sequel` gem source is [on github][repo-github], along with related gems including `opentelemetry-api` and `opentelemetry-sdk`.

The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us in [GitHub Discussions][discussions-url] or attending our weekly meeting. See the [meeting calendar][community-meetings] for dates and times. For more information on this and other language SIGs, see the OpenTelemetry [community page][ruby-sig].

## License

The `opentelemetry-instrumentation-sequel` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information.

[sequel-home]: https://sequel.jeremyevans.net/
[bundler-home]: https://bundler.io
[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby
[license-github]: https://github.com/open-telemetry/opentelemetry-ruby-contrib/blob/main/LICENSE
[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig
[community-meetings]: https://github.com/open-telemetry/community#community-meetings
[discussions-url]: https://github.com/open-telemetry/opentelemetry-ruby/discussions
28 changes: 28 additions & 0 deletions instrumentation/sequel/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

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

require 'bundler/gem_tasks'
require 'rake/testtask'
require 'yard'
require 'rubocop/rake_task'

RuboCop::RakeTask.new

Rake::TestTask.new :test do |t|
t.libs << 'test'
t.libs << 'lib'
t.test_files = FileList['test/**/*_test.rb']
end

YARD::Rake::YardocTask.new do |t|
t.stats_options = ['--list-undoc']
end

if RUBY_ENGINE == 'truffleruby'
task default: %i[test]
else
task default: %i[test rubocop yard]
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

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

require_relative 'opentelemetry/sequel'
19 changes: 19 additions & 0 deletions instrumentation/sequel/lib/opentelemetry/instrumentation/sequel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

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

require 'opentelemetry'
require 'opentelemetry-instrumentation-base'

module OpenTelemetry
module Instrumentation
# Contains the OpenTelemetry instrumentation for the Sequel gem
module Sequel
end
end
end

require_relative 'sequel/instrumentation'
require_relative 'sequel/version'
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

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

require 'sequel/extensions/opentelemetry'

module OpenTelemetry
module Instrumentation
module Sequel
# The Instrumentation class contains logic to detect and install the Sequel
# instrumentation
class Instrumentation < OpenTelemetry::Instrumentation::Base
install do |_|
::Sequel.extension(:opentelemetry)
end

present do
defined?(::Sequel)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

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

module OpenTelemetry
module Instrumentation
module Sequel
VERSION = '0.1.0'
end
end
end
22 changes: 22 additions & 0 deletions instrumentation/sequel/lib/opentelemetry/sequel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

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

# OpenTelemetry is an open source observability framework, providing a
# general-purpose API, SDK, and related tools required for the instrumentation
# of cloud-native software, frameworks, and libraries.
#
# The OpenTelemetry module provides global accessors for telemetry objects.
# See the documentation for the `opentelemetry-api` gem for details.
module OpenTelemetry
# "Instrumentation" are specified by
# https://github.com/open-telemetry/opentelemetry-specification/blob/784635d01d8690c8f5fcd1f55bdbc8a13cf2f4f2/specification/glossary.md#instrumentation-library
#
# Instrumentation should be able to handle the case when the library is not installed on a user's system.
module Instrumentation
end
end

require_relative 'instrumentation/sequel'
46 changes: 46 additions & 0 deletions instrumentation/sequel/lib/sequel/extensions/opentelemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

require 'sequel'

# Sequel: The Database Toolkit for Ruby
module Sequel
# The Sequel::Opentelemetry extension
#
# @example Simple usage
#
# require 'sequel-opentelemetry'
# DB.extension :opentelemetry
#
# Related module: Sequel::Opentelemetry
module Opentelemetry
def tracer
@tracer ||= ::OpenTelemetry.tracer_provider.tracer('Sequel', ::Sequel::VERSION)
end

def trace_execute(super_method, sequel_method, sql, options, &block)
response = nil
attributes = {
'component' => 'Sequel',
::OpenTelemetry::SemanticConventions::Trace::DB_NAME => opts[:database],
::OpenTelemetry::SemanticConventions::Trace::DB_SYSTEM => database_type.to_s,
::OpenTelemetry::SemanticConventions::Trace::DB_STATEMENT => sql,
::OpenTelemetry::SemanticConventions::Trace::DB_OPERATION => sequel_method
}
attributes['sequel.host'] = opts[:host] if opts[:host]

tracer.in_span("sequel.#{sequel_method}", attributes: attributes) do
response = super_method.call(sql, options, &block)
end
response
end

def execute(sql, options = ::Sequel::OPTS, &block)
trace_execute(proc { super(sql, options, &block) }, 'execute', sql, options, &block)
end

def execute_dui(sql, options = ::Sequel::OPTS, &block)
trace_execute(proc { super(sql, options, &block) }, 'execute_dui', sql, options, &block)
end
end
Database.register_extension(:opentelemetry, Opentelemetry)
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

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

lib = File.expand_path('lib', __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'opentelemetry/instrumentation/sequel/version'

Gem::Specification.new do |spec|
spec.name = 'opentelemetry-instrumentation-sequel'
spec.version = OpenTelemetry::Instrumentation::Sequel::VERSION
spec.authors = ['OpenTelemetry Authors']
spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io']

spec.summary = 'Sequel instrumentation for the OpenTelemetry framework'
spec.description = 'Sequel instrumentation for the OpenTelemetry framework'
spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib'
spec.license = 'Apache-2.0'

spec.files = Dir.glob('lib/**/*.rb') +
Dir.glob('*.md') +
['LICENSE', '.yardopts']
spec.require_paths = ['lib']
spec.required_ruby_version = '>= 3.0'

spec.add_dependency 'opentelemetry-api', '~> 1.0'
spec.add_dependency 'opentelemetry-common', '~> 0.20.0'
spec.add_dependency 'opentelemetry-instrumentation-base', '~> 0.22.1'

spec.add_development_dependency 'appraisal', '~> 2.5'
spec.add_development_dependency 'bundler', '~> 2.4'
spec.add_development_dependency 'minitest', '~> 5.0'
spec.add_development_dependency 'opentelemetry-sdk', '~> 1.1'
spec.add_development_dependency 'opentelemetry-test-helpers', '~> 0.3'
spec.add_development_dependency 'rubocop', '~> 1.56.1'
spec.add_development_dependency 'sequel', '~> 5.29.0'
spec.add_development_dependency 'simplecov', '~> 0.17.1'
spec.add_development_dependency 'sqlite3', '~> 1.6.4'
spec.add_development_dependency 'yard', '~> 0.9'

if spec.respond_to?(:metadata)
spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md"
spec.metadata['source_code_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation/sequel'
spec.metadata['bug_tracker_uri'] = 'https://github.com/open-telemetry/opentelemetry-ruby-contrib/issues'
spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}"
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# frozen_string_literal: true

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

require 'test_helper'

describe OpenTelemetry::Instrumentation::Sequel do
let(:instrumentation) { OpenTelemetry::Instrumentation::Sequel::Instrumentation.instance }
let(:exporter) { EXPORTER }
let(:database) { Sequel.connect('sqlite::memory:') }
let(:model) { Class.new(Sequel::Model(:documents)) }

# class CustomError < StandardError; end

before do
instrumentation.install
database.extension :opentelemetry
database.create_table!(:documents) do
primary_key :id
String :title
end
model # Instantiate the model so that it doesn't interfere with further calls

exporter.reset
end

describe 'tracing' do
it 'before query' do
_(exporter.finished_spans.size).must_equal 0
end

it 'traces dataset executes' do
database['SELECT 1'].all

_(exporter.finished_spans.size).must_equal 1
end

it 'traces direct executes' do
database.run('SELECT 1')

_(exporter.finished_spans.size).must_equal 1
end

it 'traces models' do
model.first

_(exporter.finished_spans.size).must_equal 1
end

it 'records attributes' do
model.first

_(exporter.finished_spans.first.attributes).must_equal(
'component' => 'Sequel',
'db.name' => '',
'db.system' => 'sqlite',
'db.statement' => 'SELECT * FROM `documents` LIMIT 1',
'db.operation' => 'execute'
)
end

it 'traces DUI calls' do
database.create_table(:test_table) { primary_key :id }

_(exporter.finished_spans.first.attributes).must_equal(
'component' => 'Sequel',
'db.name' => '',
'db.system' => 'sqlite',
'db.statement' => 'CREATE TABLE `test_table` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT)',
'db.operation' => 'execute_dui'
)
end
end
end
Loading

0 comments on commit 7eecb44

Please sign in to comment.