opentracing-instrumentation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.BUNDLER_VERSION +1 -0
- data/.drone.jsonnet +35 -0
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +80 -0
- data/.rubocop.yml +36 -0
- data/.ruby-version +1 -0
- data/GEM_VERSION +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +101 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +11 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/lib/opentracing/instrumentation.rb +23 -0
- data/lib/opentracing/instrumentation/common.rb +13 -0
- data/lib/opentracing/instrumentation/common/error_writer.rb +56 -0
- data/lib/opentracing/instrumentation/faraday.rb +10 -0
- data/lib/opentracing/instrumentation/faraday/response_logger.rb +76 -0
- data/lib/opentracing/instrumentation/faraday/trace_middleware.rb +202 -0
- data/lib/opentracing/instrumentation/mongo.rb +12 -0
- data/lib/opentracing/instrumentation/mongo/direct_sanitazer.rb +16 -0
- data/lib/opentracing/instrumentation/mongo/query_sanitazer.rb +84 -0
- data/lib/opentracing/instrumentation/mongo/trace_subscriber.rb +107 -0
- data/lib/opentracing/instrumentation/object_wrapper.rb +59 -0
- data/lib/opentracing/instrumentation/rack.rb +11 -0
- data/lib/opentracing/instrumentation/rack/http_tagger.rb +69 -0
- data/lib/opentracing/instrumentation/rack/trace_middleware.rb +94 -0
- data/lib/opentracing/instrumentation/redis.rb +18 -0
- data/lib/opentracing/instrumentation/redis/config.rb +40 -0
- data/lib/opentracing/instrumentation/redis/span_builder.rb +85 -0
- data/lib/opentracing/instrumentation/redis/tracing_driver_wrapper.rb +117 -0
- data/lib/opentracing/instrumentation/sidekiq.rb +17 -0
- data/lib/opentracing/instrumentation/sidekiq/client_middleware.rb +66 -0
- data/lib/opentracing/instrumentation/sidekiq/job_tagger.rb +61 -0
- data/lib/opentracing/instrumentation/sidekiq/server_middleware.rb +70 -0
- data/lib/opentracing/instrumentation/sinatra.rb +11 -0
- data/lib/opentracing/instrumentation/sinatra/trace_middleware.rb +64 -0
- data/lib/opentracing/instrumentation/thrift.rb +15 -0
- data/lib/opentracing/instrumentation/thrift/config.rb +24 -0
- data/lib/opentracing/instrumentation/thrift/traced_protocol.rb +145 -0
- data/lib/opentracing/instrumentation/thrift/traced_protocol_factory.rb +48 -0
- data/lib/opentracing/instrumentation/version.rb +7 -0
- data/opentracing-instrumentation.gemspec +40 -0
- metadata +255 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module OpenTracing
|
6
|
+
module Instrumentation
|
7
|
+
module Common
|
8
|
+
# ErrorWriter can be used to write error tag and log to span
|
9
|
+
class ErrorWriter
|
10
|
+
LOG_ERROR_EVENT = 'error'
|
11
|
+
ERROR_TAG = 'error'
|
12
|
+
|
13
|
+
attr_reader :set_error_tag
|
14
|
+
attr_reader :log_error_event
|
15
|
+
|
16
|
+
# @param set_error_tag [TrueClass,ErrorClass] enable set error tag
|
17
|
+
# @param log_error_event [TrueClass, ErrorClass] enable log exception
|
18
|
+
def initialize(
|
19
|
+
set_error_tag: true,
|
20
|
+
log_error_event: true
|
21
|
+
)
|
22
|
+
@set_error_tag = set_error_tag
|
23
|
+
@log_error_event = log_error_event
|
24
|
+
end
|
25
|
+
|
26
|
+
# Write error tag and log error event
|
27
|
+
# @param span [OpenTracing::Span] target for tag and log
|
28
|
+
# @param exception [Exception] logged to tag
|
29
|
+
def write_error(span, exception, event: LOG_ERROR_EVENT)
|
30
|
+
tag_error(span)
|
31
|
+
|
32
|
+
log_error(span, exception, event)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def tag_error(span)
|
38
|
+
return unless set_error_tag
|
39
|
+
|
40
|
+
span.set_tag('error', true)
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_error(span, exception, event)
|
44
|
+
return unless log_error_event
|
45
|
+
|
46
|
+
span.log_kv(
|
47
|
+
event: event,
|
48
|
+
'error.kind': exception.class.to_s,
|
49
|
+
message: exception.to_s,
|
50
|
+
stack: JSON.dump(exception.backtrace),
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenTracing
|
4
|
+
module Instrumentation
|
5
|
+
module Faraday
|
6
|
+
# ResponseLogger add request and response log_kv to faraday_request span
|
7
|
+
class ResponseLogger
|
8
|
+
DEFAULT_REQUEST_LOG_HEADERS = %w[].freeze
|
9
|
+
DEFAULT_REQUEST_TAG_HEADERS = {
|
10
|
+
accept: 'Accept',
|
11
|
+
connection: 'Connection',
|
12
|
+
content_type: 'Content-Type',
|
13
|
+
user_agent: 'User-Agent',
|
14
|
+
}.freeze
|
15
|
+
DEFAULT_RESPONSE_LOG_HEADERS = %w[
|
16
|
+
Content-Length
|
17
|
+
].freeze
|
18
|
+
DEFAULT_RESPONSE_TAG_HEADERS = {
|
19
|
+
connection: 'Connection',
|
20
|
+
content_type: 'Content-Type',
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
attr_reader :request_tag_headers
|
24
|
+
attr_reader :request_log_headers
|
25
|
+
attr_reader :response_tag_headers
|
26
|
+
attr_reader :response_log_headers
|
27
|
+
|
28
|
+
def initialize(
|
29
|
+
request_log_headers: DEFAULT_REQUEST_LOG_HEADERS,
|
30
|
+
request_tag_headers: DEFAULT_REQUEST_TAG_HEADERS,
|
31
|
+
response_tag_headers: DEFAULT_RESPONSE_TAG_HEADERS,
|
32
|
+
response_log_headers: DEFAULT_RESPONSE_LOG_HEADERS
|
33
|
+
)
|
34
|
+
@request_tag_headers = request_tag_headers
|
35
|
+
@request_log_headers = request_log_headers
|
36
|
+
@response_tag_headers = response_tag_headers
|
37
|
+
@response_log_headers = response_log_headers
|
38
|
+
end
|
39
|
+
|
40
|
+
def log_request(span, request_headers)
|
41
|
+
log_headers(span, request_headers, request_log_headers, 'request')
|
42
|
+
tag_headers(span, request_headers, request_tag_headers, 'request')
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_response(span, response_headers)
|
46
|
+
log_headers(span, response_headers, response_log_headers, 'response')
|
47
|
+
tag_headers(span, response_headers, response_tag_headers, 'response')
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def tag_headers(span, headers, tag_headers, event)
|
53
|
+
tag_headers.each do |key, header_name|
|
54
|
+
header_value = headers[header_name]
|
55
|
+
next unless header_value
|
56
|
+
|
57
|
+
span.set_tag("http.#{event}.#{key}", header_value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_headers(span, headers, log_headers, event)
|
62
|
+
header_hash =
|
63
|
+
log_headers
|
64
|
+
.map { |key| [key, headers[key]] }
|
65
|
+
.reject { |(_key, value)| value.nil? }
|
66
|
+
.to_h
|
67
|
+
|
68
|
+
span.log_kv(
|
69
|
+
event: event,
|
70
|
+
headers: JSON.dump(header_hash),
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opentracing/instrumentation/faraday/response_logger'
|
4
|
+
|
5
|
+
module OpenTracing
|
6
|
+
module Instrumentation
|
7
|
+
module Faraday
|
8
|
+
# TraceMiddleware inject tracing header into request and trace request
|
9
|
+
#
|
10
|
+
# Usage with default config:
|
11
|
+
# Faraday.new(url) do |connection|
|
12
|
+
# connection.use \
|
13
|
+
# OpenTracing::Instrumentation::Faraday::TraceMiddleware
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Usage with config block:
|
17
|
+
# Faraday.new(url) do |connection|
|
18
|
+
# connection.use \
|
19
|
+
# OpenTracing::Instrumentation::Faraday::TraceMiddleware do |c|
|
20
|
+
# # c is instance of Config
|
21
|
+
# c.tracer = tracer
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
class TraceMiddleware
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
# Config for TraceMiddleware
|
28
|
+
class Config
|
29
|
+
DEFAULT_COMMAND_NAME = 'faraday_request'
|
30
|
+
DEFAULT_COMPONENT = 'faraday'
|
31
|
+
DEFAULT_EXPECTED_ERRORS = [StandardError].freeze
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@tracer = OpenTracing.global_tracer
|
35
|
+
@operation_name = DEFAULT_COMMAND_NAME
|
36
|
+
@component = DEFAULT_COMPONENT
|
37
|
+
@expected_errors = DEFAULT_EXPECTED_ERRORS
|
38
|
+
@service_name = nil
|
39
|
+
@inject = true
|
40
|
+
@response_logger = ResponseLogger.new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Instance of tracer. Should implement OpenTracing::Tracer API.
|
44
|
+
#
|
45
|
+
# @return [OpenTracing::Tracer]
|
46
|
+
attr_accessor :tracer
|
47
|
+
|
48
|
+
# Operation name of tracing span.
|
49
|
+
#
|
50
|
+
# @return [String]
|
51
|
+
attr_accessor :operation_name
|
52
|
+
|
53
|
+
# List of handled errors.
|
54
|
+
#
|
55
|
+
# @return [Array<Class>]
|
56
|
+
attr_accessor :expected_errors
|
57
|
+
|
58
|
+
# Value for component tag
|
59
|
+
#
|
60
|
+
# @return [String]
|
61
|
+
attr_accessor :component
|
62
|
+
|
63
|
+
# Value for service_name tag.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
attr_accessor :service_name
|
67
|
+
|
68
|
+
# Instance of response logger
|
69
|
+
#
|
70
|
+
# @return [ResponseLogger]
|
71
|
+
attr_accessor :response_logger
|
72
|
+
|
73
|
+
# Inject trace headers to response
|
74
|
+
#
|
75
|
+
# @return [Boolean]
|
76
|
+
attr_accessor :inject
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param config [Config]
|
80
|
+
#
|
81
|
+
# @yieldparam config [Config]
|
82
|
+
def initialize(
|
83
|
+
app,
|
84
|
+
config = Config.new
|
85
|
+
)
|
86
|
+
@app = app
|
87
|
+
@config = config
|
88
|
+
yield(config) if block_given?
|
89
|
+
end
|
90
|
+
|
91
|
+
# Wrap Faraday request to trace it with OpenTracing
|
92
|
+
# @param env [Faraday::Env]
|
93
|
+
# @return [Faraday::Response]
|
94
|
+
def call(env)
|
95
|
+
trace_request(env) do |span|
|
96
|
+
inject_tracing(span, env) if inject
|
97
|
+
response_logger&.log_request(span, env.request_headers)
|
98
|
+
@app.call(env).on_complete do |response|
|
99
|
+
set_response_tags(span, response)
|
100
|
+
response_logger&.log_response(span, response.response_headers)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def_delegators :@config,
|
108
|
+
:tracer,
|
109
|
+
:operation_name,
|
110
|
+
:component,
|
111
|
+
:expected_errors,
|
112
|
+
:inject,
|
113
|
+
:response_logger,
|
114
|
+
:service_name
|
115
|
+
|
116
|
+
def trace_request(env)
|
117
|
+
scope = build_scope(env)
|
118
|
+
span = scope.span
|
119
|
+
|
120
|
+
yield(span)
|
121
|
+
rescue *expected_errors => e
|
122
|
+
set_exception_tags(span, e)
|
123
|
+
raise
|
124
|
+
ensure
|
125
|
+
scope.close
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_scope(env)
|
129
|
+
tracer.start_active_span(
|
130
|
+
operation_name,
|
131
|
+
tags: request_tags(env),
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
def inject_tracing(span, env)
|
136
|
+
tracer.inject(
|
137
|
+
span.context,
|
138
|
+
OpenTracing::FORMAT_RACK,
|
139
|
+
env[:request_headers],
|
140
|
+
)
|
141
|
+
end
|
142
|
+
|
143
|
+
def request_tags(env)
|
144
|
+
base_tags
|
145
|
+
.merge(http_tags(env))
|
146
|
+
.merge(faraday_tags(env))
|
147
|
+
end
|
148
|
+
|
149
|
+
def base_tags
|
150
|
+
{
|
151
|
+
'span.kind' => 'client',
|
152
|
+
'component' => component,
|
153
|
+
'peer.service_name' => service_name,
|
154
|
+
}.compact
|
155
|
+
end
|
156
|
+
|
157
|
+
def http_tags(env)
|
158
|
+
{
|
159
|
+
'http.method' => env[:method],
|
160
|
+
'http.url' => env[:url].to_s,
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
def faraday_tags(env)
|
165
|
+
{
|
166
|
+
'faraday.adapter' => @app.class.to_s,
|
167
|
+
'faraday.parallel' => env.parallel?,
|
168
|
+
'faraday.parse_body' => env.parse_body?,
|
169
|
+
'faraday.ssl_verify' => env.ssl.verify?,
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
def set_response_tags(span, response)
|
174
|
+
span.set_tag('http.status_code', response.status)
|
175
|
+
|
176
|
+
return if response.success?
|
177
|
+
|
178
|
+
set_http_error_tags(span, response)
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_http_error_tags(span, response)
|
182
|
+
span.set_tag('error', true)
|
183
|
+
span.log_kv(
|
184
|
+
event: 'error',
|
185
|
+
message: response.body.to_s,
|
186
|
+
'error.kind': 'http',
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_exception_tags(span, error)
|
191
|
+
span.set_tag('error', true)
|
192
|
+
span.log_kv(
|
193
|
+
event: 'error',
|
194
|
+
message: error.message,
|
195
|
+
'error.kind': error.class.to_s,
|
196
|
+
'stack': error.backtrace,
|
197
|
+
)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenTracing
|
4
|
+
module Instrumentation
|
5
|
+
# Mongo driver instrumentation. Support mongo gem version 2.x.
|
6
|
+
module Mongo
|
7
|
+
autoload :DirectSanitazer, 'opentracing/instrumentation/mongo/direct_sanitazer'
|
8
|
+
autoload :TraceSubscriber, 'opentracing/instrumentation/mongo/trace_subscriber'
|
9
|
+
autoload :QuerySanitazer, 'opentracing/instrumentation/mongo/query_sanitazer'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenTracing
|
4
|
+
module Instrumentation
|
5
|
+
module Mongo
|
6
|
+
# DirectSanitazer requests.
|
7
|
+
class DirectSanitazer
|
8
|
+
def sanitaze(command, command_name)
|
9
|
+
command = command.dup
|
10
|
+
command.delete(command_name)
|
11
|
+
command
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bson'
|
4
|
+
require 'date'
|
5
|
+
require 'opentracing/instrumentation/mongo/direct_sanitazer'
|
6
|
+
|
7
|
+
module OpenTracing
|
8
|
+
module Instrumentation
|
9
|
+
module Mongo
|
10
|
+
# QuerySanitazer clean private data from requests.
|
11
|
+
class QuerySanitazer < DirectSanitazer
|
12
|
+
DEFAULT_SAFE_CLASSES = [
|
13
|
+
TrueClass,
|
14
|
+
FalseClass,
|
15
|
+
Numeric,
|
16
|
+
Date,
|
17
|
+
Time,
|
18
|
+
].freeze
|
19
|
+
DEFAULT_EXCLUDE_KEYS = %w[lsid].freeze
|
20
|
+
|
21
|
+
def initialize(
|
22
|
+
safe_classes: DEFAULT_SAFE_CLASSES,
|
23
|
+
exclude_keys: DEFAULT_EXCLUDE_KEYS
|
24
|
+
)
|
25
|
+
@safe_classes = safe_classes
|
26
|
+
@exclude_keys = exclude_keys
|
27
|
+
end
|
28
|
+
|
29
|
+
def sanitaze(command, command_name)
|
30
|
+
command_without_command_name = super(command, command_name)
|
31
|
+
exclude_keys.each do |key|
|
32
|
+
command_without_command_name.delete(key)
|
33
|
+
end
|
34
|
+
sanitaze_value(command_without_command_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :safe_classes
|
40
|
+
attr_reader :exclude_keys
|
41
|
+
|
42
|
+
OBJECT_ID_PLACEHOLDER = '$oid'
|
43
|
+
STRING_PLACEHOLDER = '$string'
|
44
|
+
PLACEHOLDER = '?'
|
45
|
+
|
46
|
+
def sanitaze_value(value)
|
47
|
+
case value
|
48
|
+
when Hash
|
49
|
+
sanitaze_hash(value)
|
50
|
+
when Enumerable
|
51
|
+
sanitaze_array(value)
|
52
|
+
else
|
53
|
+
sanitaze_simple(value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def sanitaze_simple(value)
|
58
|
+
case value
|
59
|
+
when BSON::ObjectId
|
60
|
+
OBJECT_ID_PLACEHOLDER
|
61
|
+
when String
|
62
|
+
STRING_PLACEHOLDER
|
63
|
+
when *safe_classes
|
64
|
+
value
|
65
|
+
else
|
66
|
+
PLACEHOLDER
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def sanitaze_hash(hash)
|
71
|
+
hash.transform_values do |value|
|
72
|
+
sanitaze_value(value)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def sanitaze_array(array)
|
77
|
+
array.map do |value|
|
78
|
+
sanitaze_value(value)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|