opentelemetry-exporter-jaeger 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2019 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ require 'uri'
8
+
9
+ module OpenTelemetry
10
+ module Exporter
11
+ module Jaeger
12
+ # An OpenTelemetry trace exporter that sends spans over HTTP as Thrift Binary encoded Jaeger spans.
13
+ class CollectorExporter
14
+ SUCCESS = OpenTelemetry::SDK::Trace::Export::SUCCESS
15
+ FAILURE = OpenTelemetry::SDK::Trace::Export::FAILURE
16
+ private_constant(:SUCCESS, :FAILURE)
17
+
18
+ def initialize(endpoint: ENV.fetch('OTEL_EXPORTER_JAEGER_ENDPOINT', 'http://localhost:14268/api/traces'),
19
+ username: ENV['OTEL_EXPORTER_JAEGER_USER'],
20
+ password: ENV['OTEL_EXPORTER_JAEGER_PASSWORD'])
21
+ raise ArgumentError, "invalid url for Jaeger::CollectorExporter #{endpoint}" if invalid_url?(endpoint)
22
+ raise ArgumentError, 'username and password should either both be nil or both be set' if username.nil? != password.nil?
23
+
24
+ @transport = ::Thrift::HTTPClientTransport.new(endpoint)
25
+ unless username.nil? || password.nil?
26
+ authorization = Base64.strict_encode64("#{username}:#{password}")
27
+ auth_header = { 'Authorization': "Basic #{authorization}" }
28
+ @transport.add_headers(auth_header)
29
+ end
30
+ @serializer = ::Thrift::Serializer.new
31
+ @shutdown = false
32
+ @tracer = OpenTelemetry.tracer_provider.tracer
33
+ end
34
+
35
+ # Called to export sampled {OpenTelemetry::SDK::Trace::SpanData} structs.
36
+ #
37
+ # @param [Enumerable<OpenTelemetry::SDK::Trace::SpanData>] span_data the
38
+ # list of recorded {OpenTelemetry::SDK::Trace::SpanData} structs to be
39
+ # exported.
40
+ # @return [Integer] the result of the export.
41
+ def export(span_data)
42
+ return FAILURE if @shutdown
43
+
44
+ encoded_batches(span_data).each do |batch|
45
+ @transport.write(@serializer.serialize(batch))
46
+ end
47
+
48
+ untraced do
49
+ @transport.flush
50
+ end
51
+ SUCCESS
52
+ rescue StandardError => e
53
+ OpenTelemetry.logger.error("unexpected error in Jaeger::CollectorExporter#export - #{e}")
54
+ FAILURE
55
+ end
56
+
57
+ # Called when {OpenTelemetry::SDK::Trace::Tracer#shutdown} is called, if
58
+ # this exporter is registered to a {OpenTelemetry::SDK::Trace::Tracer}
59
+ # object.
60
+ def shutdown
61
+ @shutdown = true
62
+ end
63
+
64
+ private
65
+
66
+ def invalid_url?(url)
67
+ return true if url.nil? || url.strip.empty?
68
+
69
+ URI(url)
70
+ false
71
+ rescue URI::InvalidURIError
72
+ true
73
+ end
74
+
75
+ def untraced
76
+ @tracer.with_span(OpenTelemetry::Trace::Span.new) { yield }
77
+ end
78
+
79
+ def encoded_batches(span_data)
80
+ span_data.group_by(&:resource).map do |resource, spans|
81
+ process = Encoder.encoded_process(resource)
82
+ spans.map! { |span| Encoder.encoded_span(span) }
83
+ Thrift::Batch.new('process' => process, 'spans' => spans)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 OpenTelemetry Authors
4
+ #
5
+ # SPDX-License-Identifier: Apache-2.0
6
+
7
+ module OpenTelemetry
8
+ module Exporter
9
+ module Jaeger
10
+ # @api private
11
+ module Encoder # rubocop:disable Metrics/ModuleLength
12
+ extend self
13
+
14
+ EMPTY_ARRAY = [].freeze
15
+ LONG = Thrift::Tag::FIELDS[Thrift::Tag::VLONG][:name]
16
+ DOUBLE = Thrift::Tag::FIELDS[Thrift::Tag::VDOUBLE][:name]
17
+ STRING = Thrift::Tag::FIELDS[Thrift::Tag::VSTR][:name]
18
+ BOOL = Thrift::Tag::FIELDS[Thrift::Tag::VBOOL][:name]
19
+ KEY = Thrift::Tag::FIELDS[Thrift::Tag::KEY][:name]
20
+ TYPE = Thrift::Tag::FIELDS[Thrift::Tag::VTYPE][:name]
21
+ TYPE_MAP = {
22
+ LONG => Thrift::TagType::LONG,
23
+ DOUBLE => Thrift::TagType::DOUBLE,
24
+ STRING => Thrift::TagType::STRING,
25
+ BOOL => Thrift::TagType::BOOL
26
+ }.freeze
27
+ KIND_MAP = {
28
+ OpenTelemetry::Trace::SpanKind::SERVER => 'server',
29
+ OpenTelemetry::Trace::SpanKind::CLIENT => 'client',
30
+ OpenTelemetry::Trace::SpanKind::PRODUCER => 'producer',
31
+ OpenTelemetry::Trace::SpanKind::CONSUMER => 'consumer'
32
+ }.freeze
33
+ private_constant(:EMPTY_ARRAY, :LONG, :DOUBLE, :STRING, :BOOL, :KEY, :TYPE, :TYPE_MAP, :KIND_MAP)
34
+
35
+ def encoded_process(resource)
36
+ service_name = 'unknown'
37
+ tags = resource&.attribute_enumerator&.select do |key, value|
38
+ service_name = value if key == 'service.name'
39
+ key != 'service.name'
40
+ end
41
+ tags = encoded_tags(tags)
42
+ Thrift::Process.new('serviceName' => service_name, 'tags' => tags)
43
+ end
44
+
45
+ def encoded_tags(attributes)
46
+ attributes&.map do |key, value|
47
+ encoded_tag(key, value)
48
+ end || EMPTY_ARRAY
49
+ end
50
+
51
+ def encoded_tag(key, value)
52
+ value_key = case value
53
+ when Integer then LONG
54
+ when Float then DOUBLE
55
+ when String, Array then STRING
56
+ when false, true then BOOL
57
+ end
58
+ value = value.to_json if value.is_a?(Array)
59
+ Thrift::Tag.new(
60
+ KEY => key,
61
+ TYPE => TYPE_MAP[value_key],
62
+ value_key => value
63
+ )
64
+ end
65
+
66
+ def encoded_span(span_data) # rubocop:disable Metrics/AbcSize
67
+ start_time = (span_data.start_timestamp.to_f * 1_000_000).to_i
68
+ duration = (span_data.end_timestamp.to_f * 1_000_000).to_i - start_time
69
+
70
+ Thrift::Span.new(
71
+ 'traceIdLow' => int64(span_data.trace_id[8, 8]),
72
+ 'traceIdHigh' => int64(span_data.trace_id[0, 8]),
73
+ 'spanId' => int64(span_data.span_id),
74
+ 'parentSpanId' => int64(span_data.parent_span_id),
75
+ 'operationName' => span_data.name,
76
+ 'references' => encoded_references(span_data.links),
77
+ 'flags' => span_data.trace_flags.sampled? ? 1 : 0,
78
+ 'startTime' => start_time,
79
+ 'duration' => duration,
80
+ 'tags' => encoded_tags(span_data.attributes) +
81
+ encoded_status(span_data.status) +
82
+ encoded_kind(span_data.kind) +
83
+ encoded_instrumentation_library(span_data.instrumentation_library),
84
+ 'logs' => encoded_logs(span_data.events)
85
+ )
86
+ end
87
+
88
+ def encoded_kind(kind)
89
+ value = KIND_MAP[kind]
90
+ return EMPTY_ARRAY unless value
91
+
92
+ Array(
93
+ Thrift::Tag.new(
94
+ KEY => 'span.kind',
95
+ TYPE => Thrift::TagType::STRING,
96
+ STRING => value
97
+ )
98
+ )
99
+ end
100
+
101
+ def encoded_logs(events)
102
+ events&.map do |event|
103
+ Thrift::Log.new(
104
+ 'timestamp' => (event.timestamp.to_f * 1_000_000).to_i,
105
+ 'fields' => encoded_tags(event.attributes) + encoded_tags('name' => event.name)
106
+ )
107
+ end
108
+ end
109
+
110
+ def encoded_references(links)
111
+ links&.map do |link|
112
+ Thrift::SpanRef.new(
113
+ 'refType' => Thrift::SpanRefType::CHILD_OF,
114
+ 'traceIdLow' => int64(link.context.trace_id[16, 16]),
115
+ 'traceIdHigh' => int64(link.context.trace_id[0, 16]),
116
+ 'spanId' => int64(link.context.span_id)
117
+ )
118
+ end
119
+ end
120
+
121
+ def encoded_status(status)
122
+ # TODO: OpenTracing doesn't specify how to report non-HTTP (i.e. generic) status.
123
+ EMPTY_ARRAY
124
+ end
125
+
126
+ def encoded_instrumentation_library(instrumentation_library)
127
+ return EMPTY_ARRAY unless instrumentation_library
128
+
129
+ tags = []
130
+ tags << encoded_tag('otel.instrumentation_library.name', instrumentation_library.name) if instrumentation_library.name
131
+ tags << encoded_tag('otel.instrumentation_library.version', instrumentation_library.version) if instrumentation_library.version
132
+ tags
133
+ end
134
+
135
+ def int64(byte_string)
136
+ int = byte_string.unpack1('Q>')
137
+ int < (1 << 63) ? int : int - (1 << 64)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,37 @@
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 Exporter
9
+ module Jaeger
10
+ # A Thrift-compatible UDP transport.
11
+ class Transport
12
+ def initialize(host, port)
13
+ @socket = UDPSocket.new
14
+ @socket.connect(host, port)
15
+ @buffer = ::Thrift::MemoryBufferTransport.new
16
+ end
17
+
18
+ def write(string)
19
+ @buffer.write(string)
20
+ end
21
+
22
+ def flush
23
+ @socket.send(@buffer.read(@buffer.available), 0)
24
+ @socket.flush
25
+ rescue Errno::ECONNREFUSED
26
+ OpenTelemetry.logger.warn('Unable to connect to Jaeger Agent')
27
+ rescue StandardError => e
28
+ OpenTelemetry.logger.warn("Unable to send spans: #{e.message}")
29
+ end
30
+
31
+ def open; end
32
+
33
+ def close; end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
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 Exporter
9
+ module Jaeger
10
+ ## Current OpenTelemetry Jaeger exporter version
11
+ VERSION = '0.6.0'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,120 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.12.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'agent_types'
9
+
10
+ module OpenTelemetry
11
+ module Exporter
12
+ module Jaeger
13
+ module Thrift
14
+ module Agent
15
+ class Client
16
+ include ::Thrift::Client
17
+
18
+ def emitZipkinBatch(spans)
19
+ send_emitZipkinBatch(spans)
20
+ end
21
+
22
+ def send_emitZipkinBatch(spans)
23
+ send_oneway_message('emitZipkinBatch', EmitZipkinBatch_args, :spans => spans)
24
+ end
25
+ def emitBatch(batch)
26
+ send_emitBatch(batch)
27
+ end
28
+
29
+ def send_emitBatch(batch)
30
+ send_oneway_message('emitBatch', EmitBatch_args, :batch => batch)
31
+ end
32
+ end
33
+
34
+ class Processor
35
+ include ::Thrift::Processor
36
+
37
+ def process_emitZipkinBatch(seqid, iprot, oprot)
38
+ args = read_args(iprot, EmitZipkinBatch_args)
39
+ @handler.emitZipkinBatch(args.spans)
40
+ return
41
+ end
42
+
43
+ def process_emitBatch(seqid, iprot, oprot)
44
+ args = read_args(iprot, EmitBatch_args)
45
+ @handler.emitBatch(args.batch)
46
+ return
47
+ end
48
+
49
+ end
50
+
51
+ # HELPER FUNCTIONS AND STRUCTURES
52
+
53
+ class EmitZipkinBatch_args
54
+ include ::Thrift::Struct, ::Thrift::Struct_Union
55
+ SPANS = 1
56
+
57
+ FIELDS = {
58
+ SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => ::OpenTelemetry::Exporter::Jaeger::Thrift::Zipkin::Span}}
59
+ }
60
+
61
+ def struct_fields; FIELDS; end
62
+
63
+ def validate
64
+ end
65
+
66
+ ::Thrift::Struct.generate_accessors self
67
+ end
68
+
69
+ class EmitZipkinBatch_result
70
+ include ::Thrift::Struct, ::Thrift::Struct_Union
71
+
72
+ FIELDS = {
73
+
74
+ }
75
+
76
+ def struct_fields; FIELDS; end
77
+
78
+ def validate
79
+ end
80
+
81
+ ::Thrift::Struct.generate_accessors self
82
+ end
83
+
84
+ class EmitBatch_args
85
+ include ::Thrift::Struct, ::Thrift::Struct_Union
86
+ BATCH = 1
87
+
88
+ FIELDS = {
89
+ BATCH => {:type => ::Thrift::Types::STRUCT, :name => 'batch', :class => ::OpenTelemetry::Exporter::Jaeger::Thrift::Batch}
90
+ }
91
+
92
+ def struct_fields; FIELDS; end
93
+
94
+ def validate
95
+ end
96
+
97
+ ::Thrift::Struct.generate_accessors self
98
+ end
99
+
100
+ class EmitBatch_result
101
+ include ::Thrift::Struct, ::Thrift::Struct_Union
102
+
103
+ FIELDS = {
104
+
105
+ }
106
+
107
+ def struct_fields; FIELDS; end
108
+
109
+ def validate
110
+ end
111
+
112
+ ::Thrift::Struct.generate_accessors self
113
+ end
114
+
115
+ end
116
+
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,17 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.12.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'agent_types'
9
+
10
+ module OpenTelemetry
11
+ module Exporter
12
+ module Jaeger
13
+ module Thrift
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.12.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'jaeger_types'
9
+ require 'zipkincore_types'
10
+
11
+
12
+ module OpenTelemetry
13
+ module Exporter
14
+ module Jaeger
15
+ module Thrift
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,86 @@
1
+ #
2
+ # Autogenerated by Thrift Compiler (0.12.0)
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'jaeger_types'
9
+
10
+ module OpenTelemetry
11
+ module Exporter
12
+ module Jaeger
13
+ module Thrift
14
+ module Collector
15
+ class Client
16
+ include ::Thrift::Client
17
+
18
+ def submitBatches(batches)
19
+ send_submitBatches(batches)
20
+ return recv_submitBatches()
21
+ end
22
+
23
+ def send_submitBatches(batches)
24
+ send_message('submitBatches', SubmitBatches_args, :batches => batches)
25
+ end
26
+
27
+ def recv_submitBatches()
28
+ result = receive_message(SubmitBatches_result)
29
+ return result.success unless result.success.nil?
30
+ raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'submitBatches failed: unknown result')
31
+ end
32
+
33
+ end
34
+
35
+ class Processor
36
+ include ::Thrift::Processor
37
+
38
+ def process_submitBatches(seqid, iprot, oprot)
39
+ args = read_args(iprot, SubmitBatches_args)
40
+ result = SubmitBatches_result.new()
41
+ result.success = @handler.submitBatches(args.batches)
42
+ write_result(result, oprot, 'submitBatches', seqid)
43
+ end
44
+
45
+ end
46
+
47
+ # HELPER FUNCTIONS AND STRUCTURES
48
+
49
+ class SubmitBatches_args
50
+ include ::Thrift::Struct, ::Thrift::Struct_Union
51
+ BATCHES = 1
52
+
53
+ FIELDS = {
54
+ BATCHES => {:type => ::Thrift::Types::LIST, :name => 'batches', :element => {:type => ::Thrift::Types::STRUCT, :class => ::OpenTelemetry::Exporter::Jaeger::Thrift::Batch}}
55
+ }
56
+
57
+ def struct_fields; FIELDS; end
58
+
59
+ def validate
60
+ end
61
+
62
+ ::Thrift::Struct.generate_accessors self
63
+ end
64
+
65
+ class SubmitBatches_result
66
+ include ::Thrift::Struct, ::Thrift::Struct_Union
67
+ SUCCESS = 0
68
+
69
+ FIELDS = {
70
+ SUCCESS => {:type => ::Thrift::Types::LIST, :name => 'success', :element => {:type => ::Thrift::Types::STRUCT, :class => ::OpenTelemetry::Exporter::Jaeger::Thrift::BatchSubmitResponse}}
71
+ }
72
+
73
+ def struct_fields; FIELDS; end
74
+
75
+ def validate
76
+ end
77
+
78
+ ::Thrift::Struct.generate_accessors self
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end