Skip to content

Commit

Permalink
Add support for automated resource detection
Browse files Browse the repository at this point in the history
This adds support for populating a resource with with Telemetry data
as well as Google Cloud Platform environment metata data.

For #230
  • Loading branch information
robertlaurin committed May 14, 2020
1 parent aa351fc commit dba4e58
Show file tree
Hide file tree
Showing 16 changed files with 383 additions and 24 deletions.
12 changes: 8 additions & 4 deletions sdk/lib/opentelemetry/sdk/configurator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Configurator
private_constant :USE_MODE_UNSPECIFIED, :USE_MODE_ONE, :USE_MODE_ALL

attr_writer :logger, :http_extractors, :http_injectors, :text_extractors,
:text_injectors
:text_injectors, :resource

def initialize
@adapter_names = []
Expand All @@ -27,7 +27,7 @@ def initialize
@text_injectors = nil
@span_processors = []
@use_mode = USE_MODE_UNSPECIFIED
@tracer_provider = Trace::TracerProvider.new
@resource = OpenTelemetry::SDK::Resources::Resource.create
end

def logger
Expand Down Expand Up @@ -83,12 +83,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
Expand All @@ -105,7 +109,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
Expand Down
2 changes: 2 additions & 0 deletions sdk/lib/opentelemetry/sdk/resources.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ module Resources
end

require 'opentelemetry/sdk/resources/resource'
require 'opentelemetry/sdk/resources/constants'
require 'opentelemetry/sdk/resources/auto_detector'
33 changes: 33 additions & 0 deletions sdk/lib/opentelemetry/sdk/resources/auto_detector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

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

require 'opentelemetry/sdk/resources/detectors/google_cloud_platform'
require 'opentelemetry/sdk/resources/detectors/telemetry'

module OpenTelemetry
module SDK
module Resources
module AutoDetector
extend self

DETECTORS = [
OpenTelemetry::SDK::Resources::Detectors::GoogleCloudPlatform,
OpenTelemetry::SDK::Resources::Detectors::Telemetry
]

def detect
resources = DETECTORS.map do |detector|
detector.detect
end

resources.reduce(OpenTelemetry::SDK::Resources::Resource.create) do |empty_resource, detected_resource|
empty_resource.merge(detected_resource)
end
end
end
end
end
end
122 changes: 122 additions & 0 deletions sdk/lib/opentelemetry/sdk/resources/constants.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

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

module OpenTelemetry
module SDK
module Resources
# 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 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

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

require 'google-cloud-env'

module OpenTelemetry
module SDK
module Resources
module Detectors
module GoogleCloudPlatform
extend self

def detect
gcp_env = Google::Cloud::Env.new
return Resource.create unless gcp_env.compute_engine?

resource_labels = {}
resource_labels[CLOUD_RESOURCE[:provider]] = 'gcp'
resource_labels[CLOUD_RESOURCE[:account_id]] = gcp_env.project_id || ''
resource_labels[CLOUD_RESOURCE[:region]] = gcp_env.instance_attribute('cluster-location') || ''
resource_labels[CLOUD_RESOURCE[:zone]] = gcp_env.instance_zone || ''

resource_labels[HOST_RESOURCE[:hostname]] = hostname
resource_labels[HOST_RESOURCE[:id]] = gcp_env.lookup_metadata('instance', 'id') || ''
resource_labels[HOST_RESOURCE[:name]] = gcp_env.lookup_metadata('instance', 'hostname') || ''

if gcp_env.kubernetes_engine?
resource_labels[K8S_RESOURCE[:cluster_name]] = gcp_env.instance_attribute('cluster-name') || ''
resource_labels[K8S_RESOURCE[:namespace_name]] = gcp_env.kubernetes_engine_namespace_id || ''
resource_labels[K8S_RESOURCE[:pod_name]] = hostname

resource_labels[CONTAINER_RESOURCE[:name]] = ENV['CONTAINER_NAME'] || ''
end

Resource.create(resource_labels)
end

private

def hostname
ENV['HOSTNAME'] || `hostname`&.strip
rescue
''
end
end
end
end
end
end
25 changes: 25 additions & 0 deletions sdk/lib/opentelemetry/sdk/resources/detectors/telemetry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

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

module OpenTelemetry
module SDK
module Resources
module Detectors
module Telemetry
extend self

def detect
resource_labels = {}
resource_labels[TELEMETRY_SDK_RESOURCE[:name]] = 'OpenTelemetry'
resource_labels[TELEMETRY_SDK_RESOURCE[:language]] = 'ruby'
resource_labels[TELEMETRY_SDK_RESOURCE[:version]] = "semver:#{OpenTelemetry::SDK::VERSION}"
Resource.create(resource_labels)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions sdk/lib/opentelemetry/sdk/resources/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ def merge(other)
end
end
end

12 changes: 4 additions & 8 deletions sdk/lib/opentelemetry/sdk/trace/tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@ module SDK
module Trace
# {Tracer} is the SDK implementation of {OpenTelemetry::Trace::Tracer}.
class Tracer < OpenTelemetry::Trace::Tracer
attr_reader :name
attr_reader :version
attr_reader :resource

# @api private
#
# Returns a new {Tracer} instance.
#
# @param [String] name Instrumentation package name
# @param [String] version Instrumentation package version
# @param [Resource] resource Containing name and version arguments supplied to the TracerProvider
#
# @return [Tracer]
def initialize(name, version)
@name = name
@version = version
@resource = Resources::Resource.create('name' => name, 'version' => version)
def initialize(resource)
@resource = resource
end

def start_root_span(name, attributes: nil, links: nil, start_timestamp: nil, kind: nil)
Expand Down
6 changes: 4 additions & 2 deletions sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ class TracerProvider < OpenTelemetry::Trace::TracerProvider
# 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.
Expand All @@ -38,7 +39,8 @@ def initialize
def tracer(name = nil, version = nil)
name ||= ''
version ||= ''
@mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(name, version) }
resource = @resource.merge(OpenTelemetry::SDK::Resources::Resource.create({ 'name' => name, 'version' => version }))
@mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(resource) }
end

# Attempts to stop all the activity for this {Tracer}. Calls
Expand Down
1 change: 1 addition & 0 deletions sdk/opentelemetry-sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
spec.required_ruby_version = '>= 2.5.0'

spec.add_dependency 'opentelemetry-api', '~> 0.4.0'
spec.add_dependency 'google-cloud-env'

spec.add_development_dependency 'bundler', '>= 1.17'
spec.add_development_dependency 'faraday', '~> 0.13'
Expand Down
27 changes: 27 additions & 0 deletions sdk/test/opentelemetry/sdk/resources/auto_detector_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

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

require 'test_helper'

describe OpenTelemetry::SDK::Resources::AutoDetector do
let(:auto_detector) { OpenTelemetry::SDK::Resources::AutoDetector }
let(:detected_resource) { auto_detector.detect }
let(:detected_resource_labels) { detected_resource.label_enumerator.to_h }
let(:expected_resource_labels) do
{
'telemetry.sdk.name' => 'OpenTelemetry',
'telemetry.sdk.language' => 'ruby',
'telemetry.sdk.version' => 'semver:0.4.0'
}
end

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
Loading

0 comments on commit dba4e58

Please sign in to comment.