Skip to content

Commit 2d359bc

Browse files
committed
Add support for automated resource detection
This adds support for populating a resource with with Telemetry data as well as Google Cloud Platform environment metata data. For #230
1 parent 2976399 commit 2d359bc

15 files changed

Lines changed: 382 additions & 21 deletions

sdk/lib/opentelemetry/sdk/configurator.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ module OpenTelemetry
88
module SDK
99
# The configurator provides defaults and facilitates configuring the
1010
# SDK for use.
11-
class Configurator
11+
class Configurator # rubocop:disable Metrics/ClassLength
1212
USE_MODE_UNSPECIFIED = 0
1313
USE_MODE_ONE = 1
1414
USE_MODE_ALL = 2
1515

1616
private_constant :USE_MODE_UNSPECIFIED, :USE_MODE_ONE, :USE_MODE_ALL
1717

1818
attr_writer :logger, :http_extractors, :http_injectors, :text_extractors,
19-
:text_injectors
19+
:text_injectors, :resource
2020

2121
def initialize
2222
@adapter_names = []
@@ -27,7 +27,7 @@ def initialize
2727
@text_injectors = nil
2828
@span_processors = []
2929
@use_mode = USE_MODE_UNSPECIFIED
30-
@tracer_provider = Trace::TracerProvider.new
30+
@resource = OpenTelemetry::SDK::Resources::Resource.create
3131
end
3232

3333
def logger
@@ -83,12 +83,16 @@ def configure
8383
OpenTelemetry.correlations = CorrelationContext::Manager.new
8484
configure_propagation
8585
configure_span_processors
86-
OpenTelemetry.tracer_provider = @tracer_provider
86+
OpenTelemetry.tracer_provider = tracer_provider
8787
install_instrumentation
8888
end
8989

9090
private
9191

92+
def tracer_provider
93+
@tracer_provider ||= Trace::TracerProvider.new(@resource)
94+
end
95+
9296
def check_use_mode!(mode)
9397
@use_mode = mode if @use_mode == USE_MODE_UNSPECIFIED
9498
raise 'Use either `use_all` or `use`, but not both' unless @use_mode == mode
@@ -105,7 +109,7 @@ def install_instrumentation
105109

106110
def configure_span_processors
107111
processors = @span_processors.empty? ? [default_span_processor] : @span_processors
108-
processors.each { |p| @tracer_provider.add_span_processor(p) }
112+
processors.each { |p| tracer_provider.add_span_processor(p) }
109113
end
110114

111115
def default_span_processor

sdk/lib/opentelemetry/sdk/resources.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ module Resources
1313
end
1414

1515
require 'opentelemetry/sdk/resources/resource'
16+
require 'opentelemetry/sdk/resources/constants'
17+
require 'opentelemetry/sdk/resources/auto_detector'
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright 2019 OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'opentelemetry/sdk/resources/detectors/google_cloud_platform'
8+
require 'opentelemetry/sdk/resources/detectors/telemetry'
9+
10+
module OpenTelemetry
11+
module SDK
12+
module Resources
13+
# AutoDetector contains detect class method for running all detectors
14+
module AutoDetector
15+
extend self
16+
17+
DETECTORS = [
18+
OpenTelemetry::SDK::Resources::Detectors::GoogleCloudPlatform,
19+
OpenTelemetry::SDK::Resources::Detectors::Telemetry
20+
]
21+
22+
def detect
23+
resources = DETECTORS.map(&:detect)
24+
resources.reduce(OpenTelemetry::SDK::Resources::Resource.create) do |empty_resource, detected_resource|
25+
empty_resource.merge(detected_resource)
26+
end
27+
end
28+
end
29+
end
30+
end
31+
end
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright 2019 OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
module OpenTelemetry
8+
module SDK
9+
module Resources
10+
# Attributes describing a service instance.
11+
SERVICE_RESOURCE = {
12+
# Logical name of the service.
13+
name: 'service.name',
14+
15+
# A namespace for `service.name`.
16+
namespace: 'service.namespace',
17+
18+
# The string ID of the service instance.
19+
instance_id: 'service.instance.id',
20+
21+
# The version string of the service API or implementation.
22+
version: 'service.version'
23+
}.freeze
24+
25+
# Attributes describing the telemetry library.
26+
TELEMETRY_SDK_RESOURCE = {
27+
# The name of the telemetry library.
28+
name: 'telemetry.sdk.name',
29+
30+
# The language of telemetry library and of the code instrumented with it.
31+
language: 'telemetry.sdk.language',
32+
33+
# The version string of the telemetry library
34+
version: 'telemetry.sdk.version'
35+
}.freeze
36+
37+
# Attributes defining a compute unit (e.g. Container, Process, Lambda
38+
# Function).
39+
CONTAINER_RESOURCE = {
40+
# The container name.
41+
name: 'container.name',
42+
43+
# The name of the image the container was built on.
44+
image_name: 'container.image.name',
45+
46+
# The container image tag.
47+
image_tag: 'container.image.tag'
48+
}.freeze
49+
50+
FAAS_RESOURCE = {
51+
# The name of the function being executed.
52+
name: 'faas.name',
53+
54+
# The unique name of the function being executed.
55+
id: 'faas.id',
56+
57+
# The version string of the function being executed.
58+
version: 'faas.version',
59+
60+
# The execution environment ID as a string.
61+
instance: 'faas.instance'
62+
}.freeze
63+
64+
# Attributes defining a deployment service (e.g. Kubernetes).
65+
K8S_RESOURCE = {
66+
# The name of the cluster that the pod is running in.
67+
cluster_name: 'k8s.cluster.name',
68+
69+
# The name of the namespace that the pod is running in.
70+
namespace_name: 'k8s.namespace.name',
71+
72+
# The name of the pod.
73+
pod_name: 'k8s.pod.name',
74+
75+
# The name of the deployment.
76+
deployment_name: 'k8s.deployment.name'
77+
}.freeze
78+
79+
# Attributes defining a computing instance (e.g. host).
80+
HOST_RESOURCE = {
81+
# Hostname of the host. It contains what the hostname command returns on the
82+
# host machine.
83+
hostname: 'host.hostname',
84+
85+
# Unique host id. For Cloud this must be the instance_id assigned by the
86+
# cloud provider
87+
id: 'host.id',
88+
89+
# Name of the host. It may contain what hostname returns on Unix systems,
90+
# the fully qualified, or a name specified by the user.
91+
name: 'host.name',
92+
93+
# Type of host. For Cloud this must be the machine type.
94+
type: 'host.type',
95+
96+
# Name of the VM image or OS install the host was instantiated from.
97+
image_name: 'host.image.name',
98+
99+
# VM image id. For Cloud, this value is from the provider.
100+
image_id: 'host.image.id',
101+
102+
# The version string of the VM image.
103+
image_version: 'host.image.version'
104+
}.freeze
105+
106+
# Attributes defining a running environment (e.g. Cloud, Data Center).
107+
CLOUD_RESOURCE = {
108+
# Name of the cloud provider. Example values are aws, azure, gcp.
109+
provider: 'cloud.provider',
110+
111+
# The cloud account id used to identify different entities.
112+
account_id: 'cloud.account.id',
113+
114+
# A specific geographical location where different entities can run.
115+
region: 'cloud.region',
116+
117+
# Zones are a sub set of the region connected through low-latency links.
118+
zone: 'cloud.zone'
119+
}.freeze
120+
end
121+
end
122+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright 2019 OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'google-cloud-env'
8+
9+
module OpenTelemetry
10+
module SDK
11+
module Resources
12+
module Detectors
13+
# GoogleCloudPlatform contains detect class method for determining gcp environment resource labels
14+
module GoogleCloudPlatform
15+
extend self
16+
17+
def detect
18+
gcp_env = Google::Cloud::Env.new
19+
resource_labels = {}
20+
21+
if gcp_env.compute_engine?
22+
resource_labels[CLOUD_RESOURCE[:provider]] = 'gcp'
23+
resource_labels[CLOUD_RESOURCE[:account_id]] = gcp_env.project_id || ''
24+
resource_labels[CLOUD_RESOURCE[:region]] = gcp_env.instance_attribute('cluster-location') || ''
25+
resource_labels[CLOUD_RESOURCE[:zone]] = gcp_env.instance_zone || ''
26+
27+
resource_labels[HOST_RESOURCE[:hostname]] = hostname
28+
resource_labels[HOST_RESOURCE[:id]] = gcp_env.lookup_metadata('instance', 'id') || ''
29+
resource_labels[HOST_RESOURCE[:name]] = gcp_env.lookup_metadata('instance', 'hostname') || ''
30+
end
31+
32+
if gcp_env.kubernetes_engine?
33+
resource_labels[K8S_RESOURCE[:cluster_name]] = gcp_env.instance_attribute('cluster-name') || ''
34+
resource_labels[K8S_RESOURCE[:namespace_name]] = gcp_env.kubernetes_engine_namespace_id || ''
35+
resource_labels[K8S_RESOURCE[:pod_name]] = hostname
36+
37+
resource_labels[CONTAINER_RESOURCE[:name]] = ENV['CONTAINER_NAME'] || ''
38+
end
39+
40+
Resource.create(resource_labels)
41+
end
42+
43+
private
44+
45+
def hostname
46+
ENV['HOSTNAME'] || `hostname`&.strip
47+
rescue StandardError => e
48+
''
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright 2019 OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
module OpenTelemetry
8+
module SDK
9+
module Resources
10+
module Detectors
11+
# Telemetry contains detect class method for determining instrumentation resource labels
12+
module Telemetry
13+
extend self
14+
15+
def detect
16+
resource_labels = {}
17+
resource_labels[TELEMETRY_SDK_RESOURCE[:name]] = 'OpenTelemetry'
18+
resource_labels[TELEMETRY_SDK_RESOURCE[:language]] = 'ruby'
19+
resource_labels[TELEMETRY_SDK_RESOURCE[:version]] = "semver:#{OpenTelemetry::SDK::VERSION}"
20+
Resource.create(resource_labels)
21+
end
22+
end
23+
end
24+
end
25+
end
26+
end

sdk/lib/opentelemetry/sdk/trace/tracer.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ module SDK
99
module Trace
1010
# {Tracer} is the SDK implementation of {OpenTelemetry::Trace::Tracer}.
1111
class Tracer < OpenTelemetry::Trace::Tracer
12-
attr_reader :name
13-
attr_reader :version
12+
attr_reader :resource
1413

1514
# @api private
1615
#
1716
# Returns a new {Tracer} instance.
1817
#
19-
# @param [String] name Instrumentation package name
20-
# @param [String] version Instrumentation package version
18+
# @param [Resource] resource Containing name and version arguments supplied to the TracerProvider
2119
#
2220
# @return [Tracer]
2321
def initialize(name, version)

sdk/lib/opentelemetry/sdk/trace/tracer_provider.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ class TracerProvider < OpenTelemetry::Trace::TracerProvider
2020
# Returns a new {TracerProvider} instance.
2121
#
2222
# @return [TracerProvider]
23-
def initialize
23+
def initialize(resource = OpenTelemetry::SDK::Resources::Resource.create)
2424
@mutex = Mutex.new
2525
@registry = {}
2626
@active_span_processor = NoopSpanProcessor.instance
2727
@active_trace_config = Config::TraceConfig::DEFAULT
2828
@registered_span_processors = []
2929
@stopped = false
30+
@resource = resource
3031
end
3132

3233
# Returns a {Tracer} instance.
@@ -38,7 +39,8 @@ def initialize
3839
def tracer(name = nil, version = nil)
3940
name ||= ''
4041
version ||= ''
41-
@mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(name, version) }
42+
resource = @resource.merge(OpenTelemetry::SDK::Resources::Resource.create('name' => name, 'version' => version))
43+
@mutex.synchronize { @registry[Key.new(name, version)] ||= Tracer.new(resource) }
4244
end
4345

4446
# Attempts to stop all the activity for this {Tracer}. Calls

sdk/opentelemetry-sdk.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
2626
spec.required_ruby_version = '>= 2.5.0'
2727

2828
spec.add_dependency 'opentelemetry-api', '~> 0.4.0'
29+
spec.add_dependency 'google-cloud-env'
2930

3031
spec.add_development_dependency 'bundler', '>= 1.17'
3132
spec.add_development_dependency 'faraday', '~> 0.13'
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
3+
# Copyright 2019 OpenTelemetry Authors
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
require 'test_helper'
8+
9+
describe OpenTelemetry::SDK::Resources::AutoDetector do
10+
let(:auto_detector) { OpenTelemetry::SDK::Resources::AutoDetector }
11+
let(:detected_resource) { auto_detector.detect }
12+
let(:detected_resource_labels) { detected_resource.label_enumerator.to_h }
13+
let(:expected_resource_labels) do
14+
{
15+
'telemetry.sdk.name' => 'OpenTelemetry',
16+
'telemetry.sdk.language' => 'ruby',
17+
'telemetry.sdk.version' => 'semver:0.4.0'
18+
}
19+
end
20+
21+
describe '.detect' do
22+
it 'returns detected resources' do
23+
_(detected_resource).must_be_instance_of(OpenTelemetry::SDK::Resources::Resource)
24+
_(detected_resource_labels).must_equal(expected_resource_labels)
25+
end
26+
end
27+
end

0 commit comments

Comments
 (0)