diff --git a/adapters/all/lib/opentelemetry/adapters/all/version.rb b/adapters/all/lib/opentelemetry/adapters/all/version.rb index 20be964360..d6a50d5c33 100644 --- a/adapters/all/lib/opentelemetry/adapters/all/version.rb +++ b/adapters/all/lib/opentelemetry/adapters/all/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Adapters module All - VERSION = '0.4.0' + VERSION = '0.4.1' end end end diff --git a/adapters/all/opentelemetry-adapters-all.gemspec b/adapters/all/opentelemetry-adapters-all.gemspec index 67b8261320..507a101faa 100644 --- a/adapters/all/opentelemetry-adapters-all.gemspec +++ b/adapters/all/opentelemetry-adapters-all.gemspec @@ -35,7 +35,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'opentelemetry-adapters-redis', '~> 0.4.0' spec.add_dependency 'opentelemetry-adapters-restclient', '~> 0.4.0' spec.add_dependency 'opentelemetry-adapters-sidekiq', '~> 0.4.0' - spec.add_dependency 'opentelemetry-adapters-sinatra', '~> 0.4.0' + spec.add_dependency 'opentelemetry-adapters-sinatra', '~> 0.4.1' spec.add_development_dependency 'bundler', '>= 1.17' spec.add_development_dependency 'minitest', '~> 5.0' diff --git a/adapters/ethon/test/opentelemetry/adapters/ethon/adapter_test.rb b/adapters/ethon/test/opentelemetry/adapters/ethon/adapter_test.rb index 170d24f0ff..6227c01dc0 100644 --- a/adapters/ethon/test/opentelemetry/adapters/ethon/adapter_test.rb +++ b/adapters/ethon/test/opentelemetry/adapters/ethon/adapter_test.rb @@ -99,7 +99,7 @@ def stub_response(options) _(easy.instance_eval { @otel_span }).must_be_nil _( easy.instance_eval { @otel_original_headers['traceparent'] } - ).must_equal "00-#{span.trace_id}-#{span.span_id}-01" + ).must_equal "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" end end @@ -112,7 +112,7 @@ def stub_response(options) _(easy.instance_eval { @otel_span }).must_be_nil _( easy.instance_eval { @otel_original_headers['traceparent'] } - ).must_equal "00-#{span.trace_id}-#{span.span_id}-01" + ).must_equal "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" end end @@ -131,7 +131,7 @@ def stub_response(options) _(easy.instance_eval { @otel_span }).must_be_nil _( easy.instance_eval { @otel_original_headers['traceparent'] } - ).must_equal "00-#{span.trace_id}-#{span.span_id}-01" + ).must_equal "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" end end end diff --git a/adapters/excon/test/opentelemetry/adapters/excon/adapter_test.rb b/adapters/excon/test/opentelemetry/adapters/excon/adapter_test.rb index 458811b89a..a579b5b231 100644 --- a/adapters/excon/test/opentelemetry/adapters/excon/adapter_test.rb +++ b/adapters/excon/test/opentelemetry/adapters/excon/adapter_test.rb @@ -58,7 +58,7 @@ assert_requested( :get, 'http://example.com/success', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end @@ -75,7 +75,7 @@ assert_requested( :get, 'http://example.com/failure', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end @@ -99,7 +99,7 @@ assert_requested( :get, 'http://example.com/timeout', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end end diff --git a/adapters/faraday/test/opentelemetry/adapters/faraday/middlewares/tracer_middleware_test.rb b/adapters/faraday/test/opentelemetry/adapters/faraday/middlewares/tracer_middleware_test.rb index c4f4579dad..5b40400e12 100644 --- a/adapters/faraday/test/opentelemetry/adapters/faraday/middlewares/tracer_middleware_test.rb +++ b/adapters/faraday/test/opentelemetry/adapters/faraday/middlewares/tracer_middleware_test.rb @@ -54,7 +54,7 @@ _(span.attributes['http.status_code']).must_equal 200 _(span.attributes['http.url']).must_equal 'http://example.com/success' _(response.env.request_headers['Traceparent']).must_equal( - "00-#{span.trace_id}-#{span.span_id}-01" + "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" ) end @@ -66,7 +66,7 @@ _(span.attributes['http.status_code']).must_equal 404 _(span.attributes['http.url']).must_equal 'http://example.com/not_found' _(response.env.request_headers['Traceparent']).must_equal( - "00-#{span.trace_id}-#{span.span_id}-01" + "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" ) end @@ -78,7 +78,7 @@ _(span.attributes['http.status_code']).must_equal 500 _(span.attributes['http.url']).must_equal 'http://example.com/failure' _(response.env.request_headers['Traceparent']).must_equal( - "00-#{span.trace_id}-#{span.span_id}-01" + "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" ) end end diff --git a/adapters/net_http/test/opentelemetry/adapters/net/http/adapter_test.rb b/adapters/net_http/test/opentelemetry/adapters/net/http/adapter_test.rb index 9196079469..96dcf525d2 100644 --- a/adapters/net_http/test/opentelemetry/adapters/net/http/adapter_test.rb +++ b/adapters/net_http/test/opentelemetry/adapters/net/http/adapter_test.rb @@ -59,7 +59,7 @@ assert_requested( :get, 'http://example.com/success', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end @@ -77,7 +77,7 @@ assert_requested( :post, 'http://example.com/failure', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end @@ -103,7 +103,7 @@ assert_requested( :get, 'https://example.com/timeout', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end end diff --git a/adapters/rack/test/opentelemetry/adapters/rack/middlewares/tracer_middleware_test.rb b/adapters/rack/test/opentelemetry/adapters/rack/middlewares/tracer_middleware_test.rb index dfe33892e6..3e3eaf749a 100644 --- a/adapters/rack/test/opentelemetry/adapters/rack/middlewares/tracer_middleware_test.rb +++ b/adapters/rack/test/opentelemetry/adapters/rack/middlewares/tracer_middleware_test.rb @@ -68,7 +68,7 @@ end it 'has no parent' do - _(first_span.parent_span_id).must_equal '0000000000000000' + _(first_span.parent_span_id).must_equal OpenTelemetry::Trace::INVALID_SPAN_ID end describe 'config[:allowed_request_headers]' do @@ -122,7 +122,7 @@ end it 'does not parent the request_span' do - _(request_span.parent_span_id).must_equal '0000000000000000' + _(request_span.parent_span_id).must_equal OpenTelemetry::Trace::INVALID_SPAN_ID end end diff --git a/adapters/restclient/test/opentelemetry/adapters/restclient/adapter_test.rb b/adapters/restclient/test/opentelemetry/adapters/restclient/adapter_test.rb index c7fdeb193d..177386479f 100644 --- a/adapters/restclient/test/opentelemetry/adapters/restclient/adapter_test.rb +++ b/adapters/restclient/test/opentelemetry/adapters/restclient/adapter_test.rb @@ -55,7 +55,7 @@ assert_requested( :get, 'http://example.com/success', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end @@ -72,7 +72,7 @@ assert_requested( :get, 'http://example.com/failure', - headers: { 'Traceparent' => "00-#{span.trace_id}-#{span.span_id}-01" } + headers: { 'Traceparent' => "00-#{span.trace_id.unpack1('H*')}-#{span.span_id.unpack1('H*')}-01" } ) end end diff --git a/adapters/sidekiq/test/opentelemetry/adapters/sidekiq/adapter_test.rb b/adapters/sidekiq/test/opentelemetry/adapters/sidekiq/adapter_test.rb index fd1fba268b..a6344f2824 100644 --- a/adapters/sidekiq/test/opentelemetry/adapters/sidekiq/adapter_test.rb +++ b/adapters/sidekiq/test/opentelemetry/adapters/sidekiq/adapter_test.rb @@ -26,7 +26,7 @@ def perform let(:adapter) { OpenTelemetry::Adapters::Sidekiq::Adapter.instance } let(:exporter) { EXPORTER } let(:spans) { exporter.finished_spans } - let(:root_span) { spans.find { |s| s.parent_span_id == '0000000000000000' } } + let(:root_span) { spans.find { |s| s.parent_span_id == OpenTelemetry::Trace::INVALID_SPAN_ID } } before { exporter.reset } @@ -47,7 +47,7 @@ def perform _(root_span.name).must_equal 'SimpleJob' _(root_span.kind).must_equal :producer - _(root_span.parent_span_id).must_equal '0000000000000000' + _(root_span.parent_span_id).must_equal OpenTelemetry::Trace::INVALID_SPAN_ID _(root_span.attributes['messaging.message_id']).must_equal job_id _(root_span.attributes['messaging.destination']).must_equal 'default' _(root_span.events.size).must_equal(1) @@ -72,7 +72,7 @@ def perform _(exporter.finished_spans.size).must_equal 4 - _(root_span.parent_span_id).must_equal '0000000000000000' + _(root_span.parent_span_id).must_equal OpenTelemetry::Trace::INVALID_SPAN_ID _(root_span.name).must_equal 'SimpleEnqueueingJob' _(root_span.kind).must_equal :producer diff --git a/adapters/sinatra/lib/opentelemetry/adapters/sinatra/version.rb b/adapters/sinatra/lib/opentelemetry/adapters/sinatra/version.rb index 88d373fb4e..b7339234d3 100644 --- a/adapters/sinatra/lib/opentelemetry/adapters/sinatra/version.rb +++ b/adapters/sinatra/lib/opentelemetry/adapters/sinatra/version.rb @@ -7,7 +7,7 @@ module OpenTelemetry module Adapters module Sinatra - VERSION = '0.4.0' + VERSION = '0.4.1' end end end diff --git a/api/lib/opentelemetry/trace.rb b/api/lib/opentelemetry/trace.rb index 6e5bb5ef4b..925dc90cf3 100644 --- a/api/lib/opentelemetry/trace.rb +++ b/api/lib/opentelemetry/trace.rb @@ -9,32 +9,30 @@ module OpenTelemetry # single logical operation, consolidated across various components of an # application. module Trace - # An invalid trace identifier, a 16-byte array with all zero bytes, encoded - # as a hexadecimal string. - INVALID_TRACE_ID = ('0' * 32).freeze + # An invalid trace identifier, a 16-byte string with all zero bytes. + INVALID_TRACE_ID = ("\0" * 16).b - # An invalid span identifier, an 8-byte array with all zero bytes, encoded - # as a hexadecimal string. - INVALID_SPAN_ID = ('0' * 16).freeze + # An invalid span identifier, an 8-byte string with all zero bytes. + INVALID_SPAN_ID = ("\0" * 8).b - # Generates a valid trace identifier, a 16-byte array with at least one - # non-zero byte, encoded as a hexadecimal string. + # Generates a valid trace identifier, a 16-byte string with at least one + # non-zero byte. # - # @return [String] a hexadecimal string encoding of a valid trace ID. + # @return [String] a valid trace ID. def self.generate_trace_id loop do - id = Random::DEFAULT.bytes(16).unpack1('H*') + id = Random::DEFAULT.bytes(16) return id unless id == INVALID_TRACE_ID end end - # Generates a valid span identifier, an 8-byte array with at least one - # non-zero byte, encoded as a hexadecimal string. + # Generates a valid span identifier, an 8-byte string with at least one + # non-zero byte. # - # @return [String] a hexadecimal string encoding of a valid span ID. + # @return [String] a valid span ID. def self.generate_span_id loop do - id = Random::DEFAULT.bytes(8).unpack1('H*') + id = Random::DEFAULT.bytes(8) return id unless id == INVALID_SPAN_ID end end diff --git a/api/lib/opentelemetry/trace/propagation/trace_context/trace_parent.rb b/api/lib/opentelemetry/trace/propagation/trace_context/trace_parent.rb index 4d13c18346..3baac074b5 100644 --- a/api/lib/opentelemetry/trace/propagation/trace_context/trace_parent.rb +++ b/api/lib/opentelemetry/trace/propagation/trace_context/trace_parent.rb @@ -25,6 +25,10 @@ class TraceParent REGEXP = /^(?[A-Fa-f0-9]{2})-(?[A-Fa-f0-9]{32})-(?[A-Fa-f0-9]{16})-(?[A-Fa-f0-9]{2})(?-.*)?$/.freeze private_constant :REGEXP + INVALID_TRACE_ID = OpenTelemetry::Trace::INVALID_TRACE_ID.unpack1('H*') + INVALID_SPAN_ID = OpenTelemetry::Trace::INVALID_SPAN_ID.unpack1('H*') + private_constant :INVALID_TRACE_ID, :INVALID_SPAN_ID + class << self # Creates a new {TraceParent} from a supplied {Trace::SpanContext} # @param [SpanContext] ctx The context @@ -71,17 +75,17 @@ def parse_version(string) end def parse_trace_id(string) - raise InvalidTraceIDError, string if string == OpenTelemetry::Trace::INVALID_TRACE_ID + raise InvalidTraceIDError, string if string == INVALID_TRACE_ID string.downcase! - string + Array(string).pack('H*') end def parse_span_id(string) - raise InvalidSpanIDError, string if string == OpenTelemetry::Trace::INVALID_SPAN_ID + raise InvalidSpanIDError, string if string == INVALID_SPAN_ID string.downcase! - string + Array(string).pack('H*') end def parse_flags(string) @@ -102,7 +106,7 @@ def sampled? # converts this object into a string according to the w3c spec # @return [String] the serialized trace_parent def to_s - "00-#{trace_id}-#{span_id}-#{flag_string}" + "00-#{trace_id.unpack1('H*')}-#{span_id.unpack1('H*')}-#{flag_string}" end private diff --git a/api/test/opentelemetry/trace/propagation/trace_context/text_extractor_test.rb b/api/test/opentelemetry/trace/propagation/trace_context/text_extractor_test.rb index 1bfe10c2e3..a8c5f677db 100644 --- a/api/test/opentelemetry/trace/propagation/trace_context/text_extractor_test.rb +++ b/api/test/opentelemetry/trace/propagation/trace_context/text_extractor_test.rb @@ -48,8 +48,8 @@ ctx = extractor.extract(carrier, context) { |c, k| c[k] } span_context = ctx[span_context_key] _(span_context).must_be :remote? - _(span_context.trace_id).must_equal('000000000000000000000000000000aa') - _(span_context.span_id).must_equal('00000000000000ea') + _(span_context.trace_id).must_equal(("\0" * 15 + "\xaa").b) + _(span_context.span_id).must_equal(("\0" * 7 + "\xea").b) _(span_context.trace_flags).must_be :sampled? _(span_context.tracestate).must_equal('vendorname=opaquevalue') end @@ -58,8 +58,8 @@ ctx = extractor.extract(carrier, context) span_context = ctx[span_context_key] _(span_context).must_be :remote? - _(span_context.trace_id).must_equal('000000000000000000000000000000aa') - _(span_context.span_id).must_equal('00000000000000ea') + _(span_context.trace_id).must_equal(("\0" * 15 + "\xaa").b) + _(span_context.span_id).must_equal(("\0" * 7 + "\xea").b) _(span_context.trace_flags).must_be :sampled? _(span_context.tracestate).must_equal('vendorname=opaquevalue') end diff --git a/api/test/opentelemetry/trace/propagation/trace_context/text_injector_test.rb b/api/test/opentelemetry/trace/propagation/trace_context/text_injector_test.rb index c6bed5a5f7..be03dfbfc9 100644 --- a/api/test/opentelemetry/trace/propagation/trace_context/text_injector_test.rb +++ b/api/test/opentelemetry/trace/propagation/trace_context/text_injector_test.rb @@ -32,18 +32,18 @@ end let(:tracestate_header) { 'vendorname=opaquevalue' } let(:context) do - span_context = SpanContext.new(trace_id: 'f' * 32, span_id: '1' * 16) + span_context = SpanContext.new(trace_id: ("\xff" * 16).b, span_id: ("\x11" * 8).b) span = Span.new(span_context: span_context) Context.empty.set_value(current_span_key, span) end let(:context_with_tracestate) do - span_context = SpanContext.new(trace_id: 'f' * 32, span_id: '1' * 16, + span_context = SpanContext.new(trace_id: ("\xff" * 16).b, span_id: ("\x11" * 8).b, tracestate: tracestate_header) span = Span.new(span_context: span_context) Context.empty.set_value(current_span_key, span) end let(:context_without_current_span) do - span_context = SpanContext.new(trace_id: 'f' * 32, span_id: '1' * 16, + span_context = SpanContext.new(trace_id: ("\xff" * 16).b, span_id: ("\x11" * 8).b, tracestate: tracestate_header) Context.empty.set_value(extracted_span_context_key, span_context) end diff --git a/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb b/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb index 9b62e6884d..760e9c95d9 100644 --- a/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb +++ b/api/test/opentelemetry/trace/propagation/trace_context/trace_parent_test.rb @@ -15,7 +15,7 @@ describe '.to_s' do it 'formats it correctly' do - expected = "00-#{good.trace_id}-#{good.span_id}-01" + expected = "00-#{good.trace_id.unpack1('H*')}-#{good.span_id.unpack1('H*')}-01" _(good.to_s).must_equal(expected) end diff --git a/exporters/jaeger/lib/opentelemetry/exporters/jaeger/exporter/span_encoder.rb b/exporters/jaeger/lib/opentelemetry/exporters/jaeger/exporter/span_encoder.rb index bb66cad1ea..0155042bca 100644 --- a/exporters/jaeger/lib/opentelemetry/exporters/jaeger/exporter/span_encoder.rb +++ b/exporters/jaeger/lib/opentelemetry/exporters/jaeger/exporter/span_encoder.rb @@ -15,8 +15,8 @@ def encoded_span(span_data) # rubocop:disable Metrics/AbcSize duration = (span_data.end_timestamp.to_f * 1_000_000).to_i - start_time Thrift::Span.new( - 'traceIdLow' => int64(span_data.trace_id[16, 16]), - 'traceIdHigh' => int64(span_data.trace_id[0, 16]), + 'traceIdLow' => int64(span_data.trace_id[8, 8]), + 'traceIdHigh' => int64(span_data.trace_id[0, 8]), 'spanId' => int64(span_data.span_id), 'parentSpanId' => int64(span_data.parent_span_id), 'operationName' => span_data.name, @@ -106,8 +106,8 @@ def encoded_tag(key, value) ) end - def int64(hex_string) - int = hex_string.to_i(16) + def int64(byte_string) + int = byte_string.unpack1('Q>') int < (1 << 63) ? int : int - (1 << 64) end end diff --git a/resource-detectors/.rubocop.yml b/resource-detectors/.rubocop.yml new file mode 100644 index 0000000000..7de9ab67fa --- /dev/null +++ b/resource-detectors/.rubocop.yml @@ -0,0 +1,26 @@ +AllCops: + TargetRubyVersion: '2.5.0' + +Bundler/OrderedGems: + Exclude: + - gemfiles/**/* +Lint/UnusedMethodArgument: + Enabled: false +Metrics/AbcSize: + Max: 18 +Metrics/LineLength: + Enabled: false +Metrics/MethodLength: + Max: 20 +Metrics/ParameterLists: + Enabled: false +Style/FrozenStringLiteralComment: + Exclude: + - gemfiles/**/* +Style/ModuleFunction: + Enabled: false +Style/StringLiterals: + Exclude: + - gemfiles/**/* +Metrics/BlockLength: + Enabled: false diff --git a/resource-detectors/Gemfile b/resource-detectors/Gemfile new file mode 100644 index 0000000000..4ef4addd61 --- /dev/null +++ b/resource-detectors/Gemfile @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +source 'https://rubygems.org' + +gemspec + +group :development, :test do + gem 'byebug' + gem 'pry' +end diff --git a/resource-detectors/LICENSE b/resource-detectors/LICENSE new file mode 100644 index 0000000000..b7fbe3acd1 --- /dev/null +++ b/resource-detectors/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 OpenTelemetry Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/resource-detectors/README.md b/resource-detectors/README.md new file mode 100644 index 0000000000..7d12fcb865 --- /dev/null +++ b/resource-detectors/README.md @@ -0,0 +1,59 @@ +# Opentelemetry::Resource::Detectors + +The `opentelemetry-resource-detectors` gem provides resource detectors for OpenTelemetry. + +## What is OpenTelemetry? + +[OpenTelemetry][opentelemetry-home] 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. + +OpenTelemetry provides a single set of APIs, libraries, agents, and collector services to capture distributed traces and metrics from your application. You can analyze them using Prometheus, Jaeger, and other observability tools. + +## How does this gem fit in? + +The `opentelemetry-resource-detectors` gem provides a means of retrieving a [resource](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/sdk.md) for supported environments following the [resource semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/README.md). + +## How do I get started? + +Install the gem using: + +``` +gem install opentelemetry-sdk +gem install opentelemetry-resource-detectors +``` + +Or, if you use [bundler][bundler-home], include `opentelemetry-sdk` and `opentelemetry-resource-detectors` in your `Gemfile`. + +```rb +require 'opentelemetry/sdk' +require 'opentelemetry/resource/detectors' + +# For a specific platform +OpenTelemetry::SDK.configure do |c| + c.resource = OpenTelemetry::Resource::Detectors::GoogleCloudPlatform.detect +end + +# Or if you would like for it to run all detectors available +OpenTelemetry::SDK.configure do |c| + c.resource = OpenTelemetry::Resource::Detectors::AutoDetector.detect +end +``` + +## How can I get involved? + +The `opentelemetry-resource-detectors` gem source is [on github][repo-github], along with related gems. + +The OpenTelemetry Ruby gems are maintained by the OpenTelemetry-Ruby special interest group (SIG). You can get involved by joining us on our [gitter channel][ruby-gitter] 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-resource-detectors` gem is distributed under the Apache 2.0 license. See [LICENSE][license-github] for more information. + + +[opentelemetry-home]: https://opentelemetry.io +[bundler-home]: https://bundler.io +[repo-github]: https://github.com/open-telemetry/opentelemetry-ruby +[license-github]: https://github.com/open-telemetry/opentelemetry-ruby/blob/master/LICENSE +[examples-github]: https://github.com/open-telemetry/opentelemetry-ruby/tree/master/examples +[ruby-sig]: https://github.com/open-telemetry/community#ruby-sig +[community-meetings]: https://github.com/open-telemetry/community#community-meetings +[ruby-gitter]: https://gitter.im/open-telemetry/opentelemetry-ruby diff --git a/resource-detectors/Rakefile b/resource-detectors/Rakefile new file mode 100644 index 0000000000..a9de144083 --- /dev/null +++ b/resource-detectors/Rakefile @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Copyright 2020 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 << '../sdk/lib' + t.libs << '../api/lib' + 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 diff --git a/resource-detectors/lib/opentelemetry/resource/detectors.rb b/resource-detectors/lib/opentelemetry/resource/detectors.rb new file mode 100644 index 0000000000..acb08aacd5 --- /dev/null +++ b/resource-detectors/lib/opentelemetry/resource/detectors.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'opentelemetry/sdk' +require 'opentelemetry/resource/detectors/version' +require 'opentelemetry/resource/detectors/google_cloud_platform' +require 'opentelemetry/resource/detectors/auto_detector' + +module OpenTelemetry + module Resource + # Detectors contains the resource detectors as well as the AutoDetector + # that can run all the detectors and return an accumlated resource + module Detectors + end + end +end diff --git a/resource-detectors/lib/opentelemetry/resource/detectors/auto_detector.rb b/resource-detectors/lib/opentelemetry/resource/detectors/auto_detector.rb new file mode 100644 index 0000000000..98cb23e69a --- /dev/null +++ b/resource-detectors/lib/opentelemetry/resource/detectors/auto_detector.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Resource + module Detectors + # AutoDetector contains detect class method for running all detectors + module AutoDetector + extend self + + DETECTORS = [ + OpenTelemetry::Resource::Detectors::GoogleCloudPlatform + ].freeze + + def detect + DETECTORS.map(&:detect).reduce(:merge) + end + end + end + end +end diff --git a/resource-detectors/lib/opentelemetry/resource/detectors/google_cloud_platform.rb b/resource-detectors/lib/opentelemetry/resource/detectors/google_cloud_platform.rb new file mode 100644 index 0000000000..612e90fc88 --- /dev/null +++ b/resource-detectors/lib/opentelemetry/resource/detectors/google_cloud_platform.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'google-cloud-env' + +module OpenTelemetry + module Resource + module Detectors + # GoogleCloudPlatform contains detect class method for determining gcp environment resource labels + module GoogleCloudPlatform + extend self + + def detect # rubocop:disable Metrics/AbcSize + gcp_env = Google::Cloud::Env.new + resource_labels = {} + resource_constants = OpenTelemetry::SDK::Resources::Constants + + if gcp_env.compute_engine? + resource_labels[resource_constants::CLOUD_RESOURCE[:provider]] = 'gcp' + resource_labels[resource_constants::CLOUD_RESOURCE[:account_id]] = gcp_env.project_id + resource_labels[resource_constants::CLOUD_RESOURCE[:region]] = gcp_env.instance_attribute('cluster-location') + resource_labels[resource_constants::CLOUD_RESOURCE[:zone]] = gcp_env.instance_zone + + resource_labels[resource_constants::HOST_RESOURCE[:hostname]] = hostname + resource_labels[resource_constants::HOST_RESOURCE[:id]] = gcp_env.lookup_metadata('instance', 'id') + resource_labels[resource_constants::HOST_RESOURCE[:name]] = gcp_env.lookup_metadata('instance', 'hostname') + end + + if gcp_env.kubernetes_engine? + resource_labels[resource_constants::K8S_RESOURCE[:cluster_name]] = gcp_env.instance_attribute('cluster-name') + resource_labels[resource_constants::K8S_RESOURCE[:namespace_name]] = gcp_env.kubernetes_engine_namespace_id + resource_labels[resource_constants::K8S_RESOURCE[:pod_name]] = hostname + + resource_labels[resource_constants::CONTAINER_RESOURCE[:name]] = ENV['CONTAINER_NAME'] + end + + resource_labels.delete_if { |_key, value| value.nil? || value.empty? } + OpenTelemetry::SDK::Resources::Resource.create(resource_labels) + end + + private + + def hostname + ENV['HOSTNAME'] || Socket.gethostname + rescue StandardError + '' + end + end + end + end +end diff --git a/resource-detectors/lib/opentelemetry/resource/detectors/version.rb b/resource-detectors/lib/opentelemetry/resource/detectors/version.rb new file mode 100644 index 0000000000..85a01c109b --- /dev/null +++ b/resource-detectors/lib/opentelemetry/resource/detectors/version.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Resource + module Detectors + VERSION = '0.0.0' + end + end +end diff --git a/resource-detectors/opentelemetry-resource-detectors.gemspec b/resource-detectors/opentelemetry-resource-detectors.gemspec new file mode 100644 index 0000000000..d612ebf19d --- /dev/null +++ b/resource-detectors/opentelemetry-resource-detectors.gemspec @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +# Copyright 2020 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/resource/detectors/version' + +Gem::Specification.new do |spec| + spec.name = 'opentelemetry-resource-detectors' + spec.version = OpenTelemetry::Resource::Detectors::VERSION + spec.authors = ['OpenTelemetry Authors'] + spec.email = ['cncf-opentelemetry-contributors@lists.cncf.io'] + + spec.summary = 'Resource detection helpers for OpenTelemetry' + spec.description = 'Resource detection helpers for OpenTelemetry' + spec.homepage = 'https://github.com/open-telemetry/opentelemetry-ruby' + spec.license = 'Apache-2.0' + + spec.files = ::Dir.glob('lib/**/*.rb') + + ::Dir.glob('*.md') + + ['LICENSE', '.yardopts'] + spec.require_paths = ['lib'] + spec.required_ruby_version = '>= 2.5.0' + + spec.add_dependency 'google-cloud-env' + + spec.add_development_dependency 'bundler', '>= 1.17' + spec.add_development_dependency 'minitest', '~> 5.0' + spec.add_development_dependency 'rake', '~> 12.0' + spec.add_development_dependency 'rubocop', '~> 0.73.0' + spec.add_development_dependency 'simplecov', '~> 0.17' + spec.add_development_dependency 'yard', '~> 0.9' + spec.add_development_dependency 'yard-doctest', '~> 0.1.6' +end diff --git a/resource-detectors/test/opentelemetry/detectors/auto_detector_test.rb b/resource-detectors/test/opentelemetry/detectors/auto_detector_test.rb new file mode 100644 index 0000000000..ce26530891 --- /dev/null +++ b/resource-detectors/test/opentelemetry/detectors/auto_detector_test.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::Resource::Detectors::AutoDetector do + let(:auto_detector) { OpenTelemetry::Resource::Detectors::AutoDetector } + let(:detected_resource) { auto_detector.detect } + let(:detected_resource_labels) { detected_resource.label_enumerator.to_h } + let(:expected_resource_labels) { {} } + + describe '.detect' do + it 'returns detected resources' do + _(detected_resource).must_be_instance_of(OpenTelemetry::SDK::Resources::Resource) + _(detected_resource_labels).must_equal(expected_resource_labels) + end + end +end diff --git a/resource-detectors/test/opentelemetry/detectors/google_cloud_platform_test.rb b/resource-detectors/test/opentelemetry/detectors/google_cloud_platform_test.rb new file mode 100644 index 0000000000..38aef91444 --- /dev/null +++ b/resource-detectors/test/opentelemetry/detectors/google_cloud_platform_test.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'test_helper' + +describe OpenTelemetry::Resource::Detectors::GoogleCloudPlatform do + let(:detector) { OpenTelemetry::Resource::Detectors::GoogleCloudPlatform } + + describe '.detect' do + let(:detected_resource) { detector.detect } + let(:detected_resource_labels) { detected_resource.label_enumerator.to_h } + let(:expected_resource_labels) { {} } + + it 'returns an empty resource' do + _(detected_resource).must_be_instance_of(OpenTelemetry::SDK::Resources::Resource) + _(detected_resource_labels).must_equal(expected_resource_labels) + end + + describe 'when in a gcp environment' do + let(:project_id) { 'opentelemetry' } + + before do + gcp_env_mock = MiniTest::Mock.new + gcp_env_mock.expect(:compute_engine?, true) + gcp_env_mock.expect(:project_id, project_id) + gcp_env_mock.expect(:instance_attribute, 'us-central1', %w[cluster-location]) + gcp_env_mock.expect(:instance_zone, 'us-central1-a') + gcp_env_mock.expect(:lookup_metadata, 'opentelemetry-test', %w[instance id]) + gcp_env_mock.expect(:lookup_metadata, 'opentelemetry-test', %w[instance hostname]) + gcp_env_mock.expect(:instance_attribute, 'opentelemetry-cluster', %w[cluster-name]) + gcp_env_mock.expect(:kubernetes_engine?, true) + gcp_env_mock.expect(:kubernetes_engine_namespace_id, 'default') + + Socket.stub(:gethostname, 'opentelemetry-test') do + Google::Cloud::Env.stub(:new, gcp_env_mock) { detected_resource } + end + end + + let(:expected_resource_labels) do + { + 'cloud.provider' => 'gcp', + 'cloud.account.id' => 'opentelemetry', + 'cloud.region' => 'us-central1', + 'cloud.zone' => 'us-central1-a', + 'host.hostname' => 'opentelemetry-test', + 'host.id' => 'opentelemetry-test', + 'host.name' => 'opentelemetry-test', + 'k8s.cluster.name' => 'opentelemetry-cluster', + 'k8s.namespace.name' => 'default', + 'k8s.pod.name' => 'opentelemetry-test' + } + end + + it 'returns a resource with gcp attributes' do + _(detected_resource).must_be_instance_of(OpenTelemetry::SDK::Resources::Resource) + _(detected_resource_labels).must_equal(expected_resource_labels) + end + + describe 'and a nil resource value is detected' do + let(:project_id) { nil } + + it 'returns a resource without that label' do + _(detected_resource_labels.key?('cloud.account.id')).must_equal(false) + end + end + + describe 'and an empty string resource value is detected' do + let(:project_id) { '' } + + it 'returns a resource without that label' do + _(detected_resource_labels.key?('cloud.account.id')).must_equal(false) + end + end + end + end +end diff --git a/resource-detectors/test/test_helper.rb b/resource-detectors/test/test_helper.rb new file mode 100644 index 0000000000..4d73d382e3 --- /dev/null +++ b/resource-detectors/test/test_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +require 'simplecov' +SimpleCov.start + +require 'opentelemetry/resource/detectors' +require 'minitest/autorun' +require 'pry' diff --git a/sdk/lib/opentelemetry/sdk/configurator.rb b/sdk/lib/opentelemetry/sdk/configurator.rb index 1713e8ae44..4dab018b6c 100644 --- a/sdk/lib/opentelemetry/sdk/configurator.rb +++ b/sdk/lib/opentelemetry/sdk/configurator.rb @@ -8,7 +8,7 @@ module OpenTelemetry module SDK # The configurator provides defaults and facilitates configuring the # SDK for use. - class Configurator + class Configurator # rubocop:disable Metrics/ClassLength USE_MODE_UNSPECIFIED = 0 USE_MODE_ONE = 1 USE_MODE_ALL = 2 @@ -27,13 +27,22 @@ def initialize @text_injectors = nil @span_processors = [] @use_mode = USE_MODE_UNSPECIFIED - @tracer_provider = Trace::TracerProvider.new + @resource = Resources::Resource.telemetry_sdk end def logger @logger ||= Logger.new(STDOUT) end + # Accepts a resource object that is merged with the default telemetry sdk + # resource. The use of this method is optional, and is provided as means + # to add additional resource information. + # + # @param [Resource] new_resource The resource to be merged + def resource=(new_resource) + @resource = @resource.merge(new_resource) + end + # Install an instrumentation adapter with specificied optional +config+. # Use can be called multiple times to install multiple instrumentation # adapters. Only +use+ or +use_all+, but not both when installing @@ -83,12 +92,16 @@ def configure OpenTelemetry.correlations = CorrelationContext::Manager.new configure_propagation configure_span_processors - OpenTelemetry.tracer_provider = @tracer_provider + OpenTelemetry.tracer_provider = tracer_provider install_instrumentation end private + def tracer_provider + @tracer_provider ||= Trace::TracerProvider.new(@resource) + end + def check_use_mode!(mode) @use_mode = mode if @use_mode == USE_MODE_UNSPECIFIED raise 'Use either `use_all` or `use`, but not both' unless @use_mode == mode @@ -105,7 +118,7 @@ def install_instrumentation def configure_span_processors processors = @span_processors.empty? ? [default_span_processor] : @span_processors - processors.each { |p| @tracer_provider.add_span_processor(p) } + processors.each { |p| tracer_provider.add_span_processor(p) } end def default_span_processor diff --git a/sdk/lib/opentelemetry/sdk/correlation_context.rb b/sdk/lib/opentelemetry/sdk/correlation_context.rb index 2b0976846e..66508368da 100644 --- a/sdk/lib/opentelemetry/sdk/correlation_context.rb +++ b/sdk/lib/opentelemetry/sdk/correlation_context.rb @@ -9,7 +9,7 @@ module OpenTelemetry module SDK - # Contains operational implementataions of the CorrelationContext::Manager + # Contains operational implementations of the CorrelationContext::Manager module CorrelationContext end end diff --git a/sdk/lib/opentelemetry/sdk/resources.rb b/sdk/lib/opentelemetry/sdk/resources.rb index 7155df90c6..07a98dc479 100644 --- a/sdk/lib/opentelemetry/sdk/resources.rb +++ b/sdk/lib/opentelemetry/sdk/resources.rb @@ -13,3 +13,4 @@ module Resources end require 'opentelemetry/sdk/resources/resource' +require 'opentelemetry/sdk/resources/constants' diff --git a/sdk/lib/opentelemetry/sdk/resources/constants.rb b/sdk/lib/opentelemetry/sdk/resources/constants.rb new file mode 100644 index 0000000000..6f76372925 --- /dev/null +++ b/sdk/lib/opentelemetry/sdk/resources/constants.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +# Copyright 2020 OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module SDK + module Resources + module Constants + # Attributes describing a service instance. + SERVICE_RESOURCE = { + # Logical name of the service. + name: 'service.name', + + # A namespace for `service.name`. + namespace: 'service.namespace', + + # The string ID of the service instance. + instance_id: 'service.instance.id', + + # The version string of the service API or implementation. + version: 'service.version' + }.freeze + + # Attributes describing the telemetry library. + TELEMETRY_SDK_RESOURCE = { + # The name of the telemetry library. + name: 'telemetry.sdk.name', + + # The language of the telemetry library and of the code instrumented with it. + language: 'telemetry.sdk.language', + + # The version string of the telemetry library + version: 'telemetry.sdk.version' + }.freeze + + # Attributes defining a compute unit (e.g. Container, Process, Lambda + # Function). + CONTAINER_RESOURCE = { + # The container name. + name: 'container.name', + + # The name of the image the container was built on. + image_name: 'container.image.name', + + # The container image tag. + image_tag: 'container.image.tag' + }.freeze + + FAAS_RESOURCE = { + # The name of the function being executed. + name: 'faas.name', + + # The unique name of the function being executed. + id: 'faas.id', + + # The version string of the function being executed. + version: 'faas.version', + + # The execution environment ID as a string. + instance: 'faas.instance' + }.freeze + + # Attributes defining a deployment service (e.g. Kubernetes). + K8S_RESOURCE = { + # The name of the cluster that the pod is running in. + cluster_name: 'k8s.cluster.name', + + # The name of the namespace that the pod is running in. + namespace_name: 'k8s.namespace.name', + + # The name of the pod. + pod_name: 'k8s.pod.name', + + # The name of the deployment. + deployment_name: 'k8s.deployment.name' + }.freeze + + # Attributes defining a computing instance (e.g. host). + HOST_RESOURCE = { + # Hostname of the host. It contains what the hostname command returns on the + # host machine. + hostname: 'host.hostname', + + # Unique host id. For Cloud this must be the instance_id assigned by the + # cloud provider + id: 'host.id', + + # Name of the host. It may contain what hostname returns on Unix systems, + # the fully qualified, or a name specified by the user. + name: 'host.name', + + # Type of host. For Cloud this must be the machine type. + type: 'host.type', + + # Name of the VM image or OS install the host was instantiated from. + image_name: 'host.image.name', + + # VM image id. For Cloud, this value is from the provider. + image_id: 'host.image.id', + + # The version string of the VM image. + image_version: 'host.image.version' + }.freeze + + # Attributes defining a running environment (e.g. Cloud, Data Center). + CLOUD_RESOURCE = { + # Name of the cloud provider. Example values are aws, azure, gcp. + provider: 'cloud.provider', + + # The cloud account id used to identify different entities. + account_id: 'cloud.account.id', + + # A specific geographical location where different entities can run. + region: 'cloud.region', + + # Zones are a sub set of the region connected through low-latency links. + zone: 'cloud.zone' + }.freeze + end + end + end +end diff --git a/sdk/lib/opentelemetry/sdk/resources/resource.rb b/sdk/lib/opentelemetry/sdk/resources/resource.rb index ebaf32e376..9981c96c2d 100644 --- a/sdk/lib/opentelemetry/sdk/resources/resource.rb +++ b/sdk/lib/opentelemetry/sdk/resources/resource.rb @@ -29,6 +29,14 @@ def create(labels = {}) new(frozen_labels) end + + def telemetry_sdk + create( + Constants::TELEMETRY_SDK_RESOURCE[:name] => 'opentelemetry', + Constants::TELEMETRY_SDK_RESOURCE[:language] => 'ruby', + Constants::TELEMETRY_SDK_RESOURCE[:version] => "semver:#{OpenTelemetry::SDK::VERSION}" + ) + end end # @api private diff --git a/sdk/lib/opentelemetry/sdk/trace/tracer.rb b/sdk/lib/opentelemetry/sdk/trace/tracer.rb index b12d7f8ac5..d840a4fe3b 100644 --- a/sdk/lib/opentelemetry/sdk/trace/tracer.rb +++ b/sdk/lib/opentelemetry/sdk/trace/tracer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Copyright 2019 OpenTelemetry Authors +# Copyright 2020 OpenTelemetry Authors # # SPDX-License-Identifier: Apache-2.0 @@ -11,6 +11,7 @@ module Trace class Tracer < OpenTelemetry::Trace::Tracer attr_reader :name attr_reader :version + attr_reader :tracer_provider # @api private # @@ -18,13 +19,14 @@ class Tracer < OpenTelemetry::Trace::Tracer # # @param [String] name Instrumentation package name # @param [String] version Instrumentation package version + # @param [TracerProvider] tracer_provider TracerProvider that initialized the tracer # # @return [Tracer] - def initialize(name, version) + def initialize(name, version, tracer_provider) @name = name @version = version - @resource = Resources::Resource.create('name' => name, 'version' => version) @instrumentation_library = InstrumentationLibrary.new(name, version) + @tracer_provider = tracer_provider end def start_root_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil) @@ -41,22 +43,31 @@ def start_span(name, with_parent: nil, with_parent_context: nil, attributes: nil trace_id = parent_span_context&.trace_id trace_id ||= OpenTelemetry::Trace.generate_trace_id span_id = OpenTelemetry::Trace.generate_span_id - sampler = OpenTelemetry.tracer_provider.active_trace_config.sampler + sampler = tracer_provider.active_trace_config.sampler result = sampler.should_sample?(trace_id: trace_id, parent_context: parent_span_context, links: links, name: name, kind: kind, attributes: attributes) - internal_create_span(result, name, kind, trace_id, span_id, parent_span_id, attributes, links, start_timestamp, tracestate) end private def internal_create_span(result, name, kind, trace_id, span_id, parent_span_id, attributes, links, start_timestamp, tracestate) # rubocop:disable Metrics/AbcSize - if result.recording? && !OpenTelemetry.tracer_provider.stopped? + if result.recording? && !tracer_provider.stopped? trace_flags = result.sampled? ? OpenTelemetry::Trace::TraceFlags::SAMPLED : OpenTelemetry::Trace::TraceFlags::DEFAULT context = OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id, trace_flags: trace_flags, tracestate: tracestate) attributes = attributes&.merge(result.attributes) || result.attributes - active_trace_config = OpenTelemetry.tracer_provider.active_trace_config - active_span_processor = OpenTelemetry.tracer_provider.active_span_processor - Span.new(context, name, kind, parent_span_id, active_trace_config, active_span_processor, attributes, links, start_timestamp || Time.now, @resource, @instrumentation_library) + Span.new( + context, + name, + kind, + parent_span_id, + tracer_provider.active_trace_config, + tracer_provider.active_span_processor, + attributes, + links, + start_timestamp || Time.now, + tracer_provider.resource, + @instrumentation_library + ) else OpenTelemetry::Trace::Span.new(span_context: OpenTelemetry::Trace::SpanContext.new(trace_id: trace_id)) end diff --git a/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb b/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb index d7da2f1b9e..8492b15c34 100644 --- a/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb +++ b/sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb @@ -13,20 +13,20 @@ class TracerProvider < OpenTelemetry::Trace::TracerProvider private_constant(:Key) attr_accessor :active_trace_config - attr_reader :active_span_processor - attr_reader :stopped + attr_reader :active_span_processor, :stopped, :resource alias stopped? stopped # Returns a new {TracerProvider} instance. # # @return [TracerProvider] - def initialize + def initialize(resource = OpenTelemetry::SDK::Resources::Resource.create) @mutex = Mutex.new @registry = {} @active_span_processor = NoopSpanProcessor.instance @active_trace_config = Config::TraceConfig::DEFAULT @registered_span_processors = [] @stopped = false + @resource = resource end # Returns a {Tracer} instance. @@ -38,7 +38,7 @@ def initialize def tracer(name = nil, version = nil) name ||= '' version ||= '' - @mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(name, version) } + @mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(name, version, self) } end # Attempts to stop all the activity for this {Tracer}. Calls diff --git a/sdk/test/opentelemetry/sdk/configurator_test.rb b/sdk/test/opentelemetry/sdk/configurator_test.rb index 26bf22b97a..c332720b9c 100644 --- a/sdk/test/opentelemetry/sdk/configurator_test.rb +++ b/sdk/test/opentelemetry/sdk/configurator_test.rb @@ -15,6 +15,24 @@ end end + describe '#resource=' do + let(:configurator_resource) { configurator.instance_variable_get(:@resource) } + let(:configurator_resource_labels) { configurator_resource.label_enumerator.to_h } + let(:expected_resource_labels) do + { + 'telemetry.sdk.name' => 'opentelemetry', + 'telemetry.sdk.language' => 'ruby', + 'telemetry.sdk.version' => "semver:#{OpenTelemetry::SDK::VERSION}", + 'test_key' => 'test_value' + } + end + + it 'merges the resource' do + configurator.resource = OpenTelemetry::SDK::Resources::Resource.create('test_key' => 'test_value') + _(configurator_resource_labels).must_equal(expected_resource_labels) + end + end + describe '#use' do it 'can be called multiple times' do configurator.use('TestAdapter', enabled: true) diff --git a/sdk/test/opentelemetry/sdk/resources/resource_test.rb b/sdk/test/opentelemetry/sdk/resources/resource_test.rb index 4896f02c1c..e1d27be11c 100644 --- a/sdk/test/opentelemetry/sdk/resources/resource_test.rb +++ b/sdk/test/opentelemetry/sdk/resources/resource_test.rb @@ -41,6 +41,15 @@ end end + describe '.telemetry_sdk' do + it 'returns a resource for the telemetry sdk' do + resource_labels = Resource.telemetry_sdk.label_enumerator.to_h + _(resource_labels['telemetry.sdk.name']).must_equal('opentelemetry') + _(resource_labels['telemetry.sdk.language']).must_equal('ruby') + _(resource_labels['telemetry.sdk.version']).must_match(/semver:\b\d{1,3}\.\d{1,3}\.\d{1,3}/) + end + end + describe '#merge' do it 'merges two resources into a third' do res1 = Resource.create('k1' => 'v1', 'k2' => 'v2') diff --git a/sdk/test/opentelemetry/sdk/trace/tracer_test.rb b/sdk/test/opentelemetry/sdk/trace/tracer_test.rb index 5cca8540d3..4ce31fe13a 100644 --- a/sdk/test/opentelemetry/sdk/trace/tracer_test.rb +++ b/sdk/test/opentelemetry/sdk/trace/tracer_test.rb @@ -20,13 +20,19 @@ describe '#name' do it 'reflects the name passed in' do - _(Tracer.new('component', 'semver:1.0').name).must_equal('component') + _(Tracer.new('component', 'semver:1.0', tracer_provider).name).must_equal('component') end end describe '#version' do it 'reflects the version passed in' do - _(Tracer.new('component', 'semver:1.0').version).must_equal('semver:1.0') + _(Tracer.new('component', 'semver:1.0', tracer_provider).version).must_equal('semver:1.0') + end + end + + describe '#tracer_provider' do + it 'reflects the tracer_provider passed in' do + _(Tracer.new('component', 'semver:1.0', tracer_provider).tracer_provider).must_equal(tracer_provider) end end