gitlab-labkit 0.13.2 → 0.15.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 +4 -4
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +4 -4
- data/.gitlab/CODEOWNERS +1 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -1
- data/gitlab-labkit.gemspec +8 -2
- data/lib/gitlab-labkit.rb +29 -0
- data/lib/labkit/context.rb +12 -1
- data/lib/labkit/excon_publisher.rb +148 -0
- data/lib/labkit/httpclient_publisher.rb +66 -0
- data/lib/labkit/logging/grpc/server_interceptor.rb +3 -1
- data/lib/labkit/logging/sanitizer.rb +8 -0
- data/lib/labkit/middleware/sidekiq/tracing/client.rb +1 -1
- data/lib/labkit/middleware/sidekiq/tracing/server.rb +1 -1
- data/lib/labkit/middleware/sidekiq/tracing/sidekiq_common.rb +14 -1
- data/lib/labkit/net_http_publisher.rb +88 -0
- data/lib/labkit/system.rb +13 -0
- data/lib/labkit/tracing.rb +9 -0
- data/lib/labkit/tracing/abstract_instrumenter.rb +47 -0
- data/lib/labkit/tracing/external_http.rb +26 -0
- data/lib/labkit/tracing/external_http/request_instrumenter.rb +33 -0
- data/lib/labkit/tracing/jaeger_factory.rb +1 -1
- data/lib/labkit/tracing/rails.rb +0 -2
- data/lib/labkit/tracing/rails/action_view.rb +14 -0
- data/lib/labkit/tracing/rails/action_view/render_collection_instrumenter.rb +7 -2
- data/lib/labkit/tracing/rails/action_view/render_partial_instrumenter.rb +7 -2
- data/lib/labkit/tracing/rails/action_view/render_template_instrumenter.rb +7 -2
- data/lib/labkit/tracing/rails/action_view/subscriber.rb +1 -1
- data/lib/labkit/tracing/rails/active_record/sql_instrumenter.rb +3 -2
- data/lib/labkit/tracing/rails/active_record/subscriber.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/cache_delete_instrumenter.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/cache_fetch_hit_instrumenter.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/cache_generate_instrumenter.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/cache_read_instrumenter.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/cache_write_instrumenter.rb +1 -1
- data/lib/labkit/tracing/rails/active_support/subscriber.rb +1 -1
- data/lib/labkit/tracing/tracing_common.rb +20 -0
- data/lib/labkit/tracing/tracing_utils.rb +3 -1
- metadata +99 -8
- data/lib/labkit/tracing/rails/abstract_instrumenter.rb +0 -46
- data/lib/labkit/tracing/rails/rails_common.rb +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 26359f4fec97f64445df7d373d67cd7344e415d4b8db5adedce24d02f03228ec
|
|
4
|
+
data.tar.gz: 11f8e6195ec18d7a3ea81b2b533609ae68f76f50f55ae32fb5b3c4b41434ebaf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2fb683c423ec1e34fc92b5dda0e2973b2259768a6dccf5fca9c6f3d7089d3130bf8e820ca8e751f27c325399b0739a73ae145667702f395e98e09dd58ff18fac
|
|
7
|
+
data.tar.gz: 1dbab7f8469391b9d6105aef90d3cc0af93ac87622a49052780ce843be941f592f4174fa5bff493cf528c8e1aceef1aad0352b992215309935e6f1edbd5bacfd
|
data/.gitignore
CHANGED
data/.gitlab-ci.yml
CHANGED
|
@@ -14,6 +14,10 @@ workflow:
|
|
|
14
14
|
- bundle install
|
|
15
15
|
- bundle exec rake verify build install
|
|
16
16
|
|
|
17
|
+
test:2.7:
|
|
18
|
+
image: ruby:2.7
|
|
19
|
+
<<: *test_definition
|
|
20
|
+
|
|
17
21
|
test:2.6:
|
|
18
22
|
image: ruby:2.6
|
|
19
23
|
<<: *test_definition
|
|
@@ -22,10 +26,6 @@ test:2.5:
|
|
|
22
26
|
image: ruby:2.5
|
|
23
27
|
<<: *test_definition
|
|
24
28
|
|
|
25
|
-
test:2.4:
|
|
26
|
-
image: ruby:2.4
|
|
27
|
-
<<: *test_definition
|
|
28
|
-
|
|
29
29
|
deploy:
|
|
30
30
|
stage: deploy
|
|
31
31
|
script:
|
data/.gitlab/CODEOWNERS
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @andrewn @ayufan @reprazent
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.7.2
|
data/gitlab-labkit.gemspec
CHANGED
|
@@ -19,18 +19,24 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.required_ruby_version = ">= 2.4.0"
|
|
20
20
|
|
|
21
21
|
# Please maintain alphabetical order for dependencies
|
|
22
|
-
spec.add_runtime_dependency "actionpack", ">= 5.0.0", "<
|
|
23
|
-
spec.add_runtime_dependency "activesupport", ">= 5.0.0", "<
|
|
22
|
+
spec.add_runtime_dependency "actionpack", ">= 5.0.0", "< 7.0.0"
|
|
23
|
+
spec.add_runtime_dependency "activesupport", ">= 5.0.0", "< 7.0.0"
|
|
24
24
|
spec.add_runtime_dependency "grpc", "~> 1.19" # Be sure to update the "grpc-tools" dev_depenency too
|
|
25
25
|
spec.add_runtime_dependency "jaeger-client", "~> 1.1"
|
|
26
26
|
spec.add_runtime_dependency "opentracing", "~> 0.4"
|
|
27
|
+
spec.add_runtime_dependency "pg_query", "~> 1.3"
|
|
27
28
|
spec.add_runtime_dependency "redis", ">3.0.0", "<5.0.0"
|
|
28
29
|
|
|
29
30
|
# Please maintain alphabetical order for dev dependencies
|
|
31
|
+
spec.add_development_dependency "excon", "~> 0.78.1"
|
|
32
|
+
spec.add_development_dependency "faraday", "~> 1.2.0"
|
|
30
33
|
spec.add_development_dependency "grpc-tools", "~> 1.19"
|
|
34
|
+
spec.add_development_dependency "httparty", "~> 0.17.3"
|
|
35
|
+
spec.add_development_dependency "httpclient", "~> 2.8.3"
|
|
31
36
|
spec.add_development_dependency "pry", "~> 0.12"
|
|
32
37
|
spec.add_development_dependency "rack", "~> 2.0"
|
|
33
38
|
spec.add_development_dependency "rake", "~> 12.3"
|
|
39
|
+
spec.add_development_dependency "rest-client", "~> 2.1.0"
|
|
34
40
|
spec.add_development_dependency "rspec", "~> 3.8.0"
|
|
35
41
|
spec.add_development_dependency "rspec-parameterized", "~> 0.4"
|
|
36
42
|
spec.add_development_dependency "rubocop", "~> 0.65.0"
|
data/lib/gitlab-labkit.rb
CHANGED
|
@@ -7,11 +7,40 @@ require "active_support/all"
|
|
|
7
7
|
# infrastructural concerns, partcularly related to
|
|
8
8
|
# observability.
|
|
9
9
|
module Labkit
|
|
10
|
+
autoload :System, "labkit/system"
|
|
11
|
+
|
|
10
12
|
autoload :Correlation, "labkit/correlation"
|
|
11
13
|
autoload :Context, "labkit/context"
|
|
12
14
|
autoload :Tracing, "labkit/tracing"
|
|
13
15
|
autoload :Logging, "labkit/logging"
|
|
14
16
|
autoload :Middleware, "labkit/middleware"
|
|
17
|
+
|
|
18
|
+
# Publishers to publish notifications whenever a HTTP reqeust is made.
|
|
19
|
+
# A broadcasted notification's payload in topic "request.external_http" includes:
|
|
20
|
+
# + method (String): "GET"
|
|
21
|
+
# + code (String): "200" # This is the status code read directly from HTTP response
|
|
22
|
+
# + duration (Float - seconds): 0.234
|
|
23
|
+
# + host (String): "gitlab.com"
|
|
24
|
+
# + port (Integer): 80,
|
|
25
|
+
# + path (String): "/gitlab-org/gitlab"
|
|
26
|
+
# + scheme (String): "https"
|
|
27
|
+
# + query (String): "field_a=1&field_b=2"
|
|
28
|
+
# + fragment (String): "issue-number-1"
|
|
29
|
+
# + proxy_host (String - Optional): "proxy.gitlab.com"
|
|
30
|
+
# + proxy_port (Integer - Optional): 80
|
|
31
|
+
# + exception (Array<String> - Optional): ["Net::ReadTimeout", "Net::ReadTimeout with #<TCPSocket:(closed)>"]
|
|
32
|
+
# + exception_object (Error Object - Optional): #<Net::ReadTimeout: Net::ReadTimeout>
|
|
33
|
+
#
|
|
34
|
+
# Usage:
|
|
35
|
+
#
|
|
36
|
+
# ActiveSupport::Notifications.subscribe "request.external_http" do |name, started, finished, unique_id, data|
|
|
37
|
+
# puts "#{name} | #{started} | #{finished} | #{unique_id} | #{data.inspect}"
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
EXTERNAL_HTTP_NOTIFICATION_TOPIC = "request.external_http"
|
|
41
|
+
autoload :NetHttpPublisher, "labkit/net_http_publisher"
|
|
42
|
+
autoload :ExconPublisher, "labkit/excon_publisher"
|
|
43
|
+
autoload :HTTPClientPublisher, "labkit/httpclient_publisher"
|
|
15
44
|
end
|
|
16
45
|
|
|
17
46
|
# rubocop:enable Naming/FileName
|
data/lib/labkit/context.rb
CHANGED
|
@@ -21,8 +21,9 @@ module Labkit
|
|
|
21
21
|
LOG_KEY = "meta"
|
|
22
22
|
CORRELATION_ID_KEY = "correlation_id"
|
|
23
23
|
RAW_KEYS = [CORRELATION_ID_KEY].freeze
|
|
24
|
+
HEADER_PREFIX = "X-Gitlab-"
|
|
24
25
|
KNOWN_KEYS = %w[user project root_namespace subscription_plan caller_id
|
|
25
|
-
related_class feature_category].freeze
|
|
26
|
+
remote_ip related_class feature_category].freeze
|
|
26
27
|
|
|
27
28
|
class << self
|
|
28
29
|
def with_context(attributes = {})
|
|
@@ -67,6 +68,10 @@ module Labkit
|
|
|
67
68
|
@known_log_keys ||= (KNOWN_KEYS.map(&method(:log_key)) + RAW_KEYS).freeze
|
|
68
69
|
end
|
|
69
70
|
|
|
71
|
+
def header_name(name)
|
|
72
|
+
HEADER_PREFIX + log_key(name).titlecase(keep_id_suffix: true).gsub(/\W/, "-")
|
|
73
|
+
end
|
|
74
|
+
|
|
70
75
|
private
|
|
71
76
|
|
|
72
77
|
def contexts
|
|
@@ -95,6 +100,12 @@ module Labkit
|
|
|
95
100
|
data[CORRELATION_ID_KEY]
|
|
96
101
|
end
|
|
97
102
|
|
|
103
|
+
def to_headers
|
|
104
|
+
to_h.except(CORRELATION_ID_KEY).transform_keys do |key|
|
|
105
|
+
self.class.header_name(key)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
98
109
|
protected
|
|
99
110
|
|
|
100
111
|
def assign_attributes(attributes)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
##
|
|
5
|
+
# A middleware for Excon HTTP library to publish a notification
|
|
6
|
+
# whenever a HTTP request is triggered.
|
|
7
|
+
#
|
|
8
|
+
# Excon supports a middleware system that allows request/response
|
|
9
|
+
# interception freely. Whenever a new Excon connection is created, a list of
|
|
10
|
+
# default middlewares is injected. This list of middlewares can be altered
|
|
11
|
+
# thanks to Excon.defaults accessor. ExconPublisher is inserted into this
|
|
12
|
+
# list. It affects all connections created in future. There is a limitation
|
|
13
|
+
# that this approach doesn't work if a user decides to override the default
|
|
14
|
+
# middleware list. It is unlikely though, at least in the dependency tree of
|
|
15
|
+
# GitLab.
|
|
16
|
+
#
|
|
17
|
+
# ExconPublisher instance is created once and shared between all Excon
|
|
18
|
+
# connections later. Each connection may be triggered by different threads in
|
|
19
|
+
# parallel. In such cases, a connection objects creates multiple sockets for
|
|
20
|
+
# each thread. Therfore in the implementation of this middleware, the
|
|
21
|
+
# instrumation payload for each connection is stored inside a thread-isolated
|
|
22
|
+
# storage.
|
|
23
|
+
#
|
|
24
|
+
# For more information:
|
|
25
|
+
# https://github.com/excon/excon/blob/81a0130537f2f8cd00d6daafb05d02d9a90dc9f7/lib/excon/middlewares/base.rb
|
|
26
|
+
# https://github.com/excon/excon/blob/fa3ec51e9bb062a12846a1cfff09534e76c99f4b/lib/excon/constants.rb#L146
|
|
27
|
+
# https://github.com/excon/excon/blob/fa3ec51e9bb062a12846a1cfff09534e76c99f4b/lib/excon/connection.rb#L474
|
|
28
|
+
class ExconPublisher
|
|
29
|
+
@prepend_mutex = Mutex.new
|
|
30
|
+
|
|
31
|
+
def self.labkit_prepend!
|
|
32
|
+
@prepend_mutex.synchronize do
|
|
33
|
+
return if !defined?(Excon) || @prepended
|
|
34
|
+
|
|
35
|
+
defaults = Excon.defaults
|
|
36
|
+
defaults[:middlewares] << ExconPublisher
|
|
37
|
+
|
|
38
|
+
@prepended = true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(stack)
|
|
43
|
+
@stack = stack
|
|
44
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def request_call(datum)
|
|
48
|
+
payload = start_payload(datum)
|
|
49
|
+
store_connection_payload(datum, payload)
|
|
50
|
+
@instrumenter.start(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
|
|
51
|
+
@stack.request_call(datum)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def response_call(datum)
|
|
55
|
+
payload = fetch_connection_payload(datum)
|
|
56
|
+
|
|
57
|
+
return @stack.response_call(datum) if payload.nil?
|
|
58
|
+
|
|
59
|
+
calculate_duration(payload)
|
|
60
|
+
payload[:code] = datum[:response][:status].to_s
|
|
61
|
+
|
|
62
|
+
@instrumenter.finish(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
|
|
63
|
+
@stack.response_call(datum)
|
|
64
|
+
ensure
|
|
65
|
+
remove_connection_payload(datum)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def error_call(datum)
|
|
69
|
+
payload = fetch_connection_payload(datum)
|
|
70
|
+
|
|
71
|
+
return @stack.error_call(datum) if payload.nil?
|
|
72
|
+
|
|
73
|
+
calculate_duration(payload)
|
|
74
|
+
|
|
75
|
+
if datum[:error].is_a?(Exception)
|
|
76
|
+
payload[:exception] = [datum[:error].class.name, datum[:error].message]
|
|
77
|
+
payload[:exception_object] = datum[:error]
|
|
78
|
+
elsif datum[:error].is_a?(String)
|
|
79
|
+
exception = StandardError.new(datum[:error])
|
|
80
|
+
payload[:exception] = [exception.class.name, exception.message]
|
|
81
|
+
payload[:exception_object] = exception
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
@instrumenter.finish(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, payload)
|
|
85
|
+
@stack.error_call(datum)
|
|
86
|
+
ensure
|
|
87
|
+
remove_connection_payload(datum)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def start_payload(datum)
|
|
93
|
+
payload = {
|
|
94
|
+
method: datum[:method].to_s.upcase,
|
|
95
|
+
host: nil_or_string(datum[:host]),
|
|
96
|
+
path: nil_or_string(datum[:path]),
|
|
97
|
+
port: nil_or_int(datum[:port]),
|
|
98
|
+
scheme: nil_or_string(datum[:scheme]),
|
|
99
|
+
query: generate_query_string(datum[:query]),
|
|
100
|
+
start_time: ::Labkit::System.monotonic_time,
|
|
101
|
+
}
|
|
102
|
+
unless datum[:proxy].nil?
|
|
103
|
+
payload[:proxy_host] = datum[:proxy][:host]
|
|
104
|
+
payload[:proxy_port] = datum[:proxy][:port]
|
|
105
|
+
end
|
|
106
|
+
payload
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def calculate_duration(payload)
|
|
110
|
+
start_time = payload.delete(:start_time) || ::Labkit::System.monotonic_time
|
|
111
|
+
payload[:duration] = (::Labkit::System.monotonic_time - start_time).to_f
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def connection_payload
|
|
115
|
+
Thread.current[:__labkit_http_excon_payload] ||= {}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def store_connection_payload(datum, payload)
|
|
119
|
+
connection_payload[datum[:connection]] = payload
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def fetch_connection_payload(datum)
|
|
123
|
+
connection_payload.fetch(datum[:connection], nil)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def remove_connection_payload(datum)
|
|
127
|
+
connection_payload.delete(datum[:connection])
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def nil_or_string(str)
|
|
131
|
+
str&.to_s
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def nil_or_int(int)
|
|
135
|
+
int&.to_i
|
|
136
|
+
rescue
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def generate_query_string(query)
|
|
141
|
+
if query.is_a?(Hash)
|
|
142
|
+
query.to_query
|
|
143
|
+
else
|
|
144
|
+
nil_or_string(query)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
##
|
|
5
|
+
# Prepend to HTTPClient class to publish an ActiveSupport::Notifcation
|
|
6
|
+
# whenever a HTTP request is triggered.
|
|
7
|
+
#
|
|
8
|
+
# Similar to Net::HTTP, this HTTP client redirects all calls to
|
|
9
|
+
# HTTPClient#do_get_block. HTTPClient is prepended with HTTPClientPublisher.
|
|
10
|
+
# Although HTTPClient supports request filter (a kind of middleware), its
|
|
11
|
+
# support is strictly limited. The request and response passed into the
|
|
12
|
+
# filter don't contain connection information. The response doesn't even
|
|
13
|
+
# contain any link to the request object. It's impossible to fit this filter
|
|
14
|
+
# mechanism into our subscribing model.
|
|
15
|
+
#
|
|
16
|
+
# For more information;
|
|
17
|
+
# https://github.com/nahi/httpclient/blob/d3091b095a1b29f65f4531a70a8e581e75be035e/lib/httpclient.rb#L1233
|
|
18
|
+
module HTTPClientPublisher
|
|
19
|
+
@prepend_mutex = Mutex.new
|
|
20
|
+
|
|
21
|
+
def self.labkit_prepend!
|
|
22
|
+
@prepend_mutex.synchronize do
|
|
23
|
+
return if !defined?(HTTPClient) || @prepended
|
|
24
|
+
|
|
25
|
+
HTTPClient.prepend(self)
|
|
26
|
+
@prepended = true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def do_get_block(req, proxy, conn, &block)
|
|
31
|
+
start_time = ::Labkit::System.monotonic_time
|
|
32
|
+
ActiveSupport::Notifications.instrument ::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, create_request_payload(req, proxy) do |payload|
|
|
33
|
+
response =
|
|
34
|
+
begin
|
|
35
|
+
super
|
|
36
|
+
ensure
|
|
37
|
+
payload[:duration] = (::Labkit::System.monotonic_time - start_time).to_f
|
|
38
|
+
end
|
|
39
|
+
payload[:code] = response.status_code.to_s
|
|
40
|
+
response
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def create_request_payload(request, proxy)
|
|
47
|
+
http_header = request.http_header
|
|
48
|
+
payload = {
|
|
49
|
+
method: http_header.request_method,
|
|
50
|
+
host: http_header.request_uri.host,
|
|
51
|
+
path: http_header.request_uri.path,
|
|
52
|
+
port: http_header.request_uri.port,
|
|
53
|
+
scheme: http_header.request_uri.scheme,
|
|
54
|
+
query: http_header.request_uri.query,
|
|
55
|
+
fragment: http_header.request_uri.fragment,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
unless proxy.nil?
|
|
59
|
+
payload[:proxy_host] = proxy.host
|
|
60
|
+
payload[:proxy_port] = proxy.port
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
payload
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -55,7 +55,7 @@ module Labkit
|
|
|
55
55
|
|
|
56
56
|
private
|
|
57
57
|
|
|
58
|
-
def log_request(method,
|
|
58
|
+
def log_request(method, _call)
|
|
59
59
|
start = Time.now
|
|
60
60
|
code = ::GRPC::Core::StatusCodes::OK
|
|
61
61
|
|
|
@@ -77,6 +77,8 @@ module Labkit
|
|
|
77
77
|
time: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%LZ"),
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
message["exception"] = ex.message if ex
|
|
81
|
+
|
|
80
82
|
@log_file.puts(JSON.dump(message))
|
|
81
83
|
end
|
|
82
84
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pg_query"
|
|
4
|
+
|
|
3
5
|
module Labkit
|
|
4
6
|
module Logging
|
|
5
7
|
# Sanitizer provides log message sanitization, removing
|
|
@@ -22,6 +24,12 @@ module Labkit
|
|
|
22
24
|
content
|
|
23
25
|
end
|
|
24
26
|
|
|
27
|
+
def self.sanitize_sql(sql)
|
|
28
|
+
PgQuery.normalize(sql)
|
|
29
|
+
rescue PgQuery::ParseError
|
|
30
|
+
""
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
# Ensures that URLS are sanitized to hide credentials
|
|
26
34
|
def self.mask_url(url)
|
|
27
35
|
url = url.to_s.strip
|
|
@@ -15,7 +15,7 @@ module Labkit
|
|
|
15
15
|
SPAN_KIND = "client"
|
|
16
16
|
|
|
17
17
|
def call(_worker_class, job, _queue, _redis_pool)
|
|
18
|
-
Labkit::Tracing::TracingUtils.with_tracing(operation_name: "sidekiq:#{job
|
|
18
|
+
Labkit::Tracing::TracingUtils.with_tracing(operation_name: "sidekiq:#{job_class(job)}", tags: tags_from_job(job, SPAN_KIND)) do |span|
|
|
19
19
|
# Inject the details directly into the job
|
|
20
20
|
Labkit::Tracing::TracingUtils.tracer.inject(span.context, OpenTracing::FORMAT_TEXT_MAP, job)
|
|
21
21
|
|
|
@@ -17,7 +17,7 @@ module Labkit
|
|
|
17
17
|
def call(_worker, job, _queue)
|
|
18
18
|
context = Labkit::Tracing::TracingUtils.tracer.extract(OpenTracing::FORMAT_TEXT_MAP, job)
|
|
19
19
|
|
|
20
|
-
Labkit::Tracing::TracingUtils.with_tracing(operation_name: "sidekiq:#{job
|
|
20
|
+
Labkit::Tracing::TracingUtils.with_tracing(operation_name: "sidekiq:#{job_class(job)}", child_of: context, tags: tags_from_job(job, SPAN_KIND)) { |_span| yield }
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -6,15 +6,28 @@ module Labkit
|
|
|
6
6
|
module Tracing
|
|
7
7
|
# SidekiqCommon is a mixin for the sidekiq middleware components
|
|
8
8
|
module SidekiqCommon
|
|
9
|
+
def job_class(job)
|
|
10
|
+
# Active Job wrapping can be found at
|
|
11
|
+
# https://github.com/rails/rails/blob/v6.0.3.1/activejob/lib/active_job/queue_adapters/sidekiq_adapter.rb
|
|
12
|
+
job["wrapped"].presence || job["class"].presence || "undefined"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def wrapped?(job)
|
|
16
|
+
job["wrapped"].present?
|
|
17
|
+
end
|
|
18
|
+
|
|
9
19
|
def tags_from_job(job, kind)
|
|
10
|
-
{
|
|
20
|
+
tags = {
|
|
11
21
|
"component" => "sidekiq",
|
|
12
22
|
"span.kind" => kind,
|
|
23
|
+
"sidekiq.wrapped" => wrapped?(job),
|
|
13
24
|
"sidekiq.queue" => job["queue"],
|
|
14
25
|
"sidekiq.jid" => job["jid"],
|
|
15
26
|
"sidekiq.retry" => job["retry"].to_s,
|
|
16
27
|
"sidekiq.args" => job["args"]&.join(", "),
|
|
17
28
|
}
|
|
29
|
+
tags["sidekiq.at"] = job["at"] if job["at"]
|
|
30
|
+
tags
|
|
18
31
|
end
|
|
19
32
|
end
|
|
20
33
|
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
##
|
|
5
|
+
# Prepend to Ruby's Net/HTTP standard HTTP library to publish a notification
|
|
6
|
+
# whenever a HTTP request is triggered. Net::HTTP has different class methods
|
|
7
|
+
# for each http method. Those methods are delegated to corresponding instance
|
|
8
|
+
# methods. Eventually, `request` method is call to dispatch the HTTP request.
|
|
9
|
+
# Therefore, a prepender that override `request` method covers all HTTP
|
|
10
|
+
# calls.
|
|
11
|
+
#
|
|
12
|
+
# For more information:
|
|
13
|
+
# https://github.com/ruby/ruby/blob/9b9cbbbc17bb5840581c7da37fd0feb0a7d4c1f3/lib/net/http.rb#L1510
|
|
14
|
+
#
|
|
15
|
+
# Note: some use cases to take care of
|
|
16
|
+
# - Create a request from input URI
|
|
17
|
+
# - Create a request from input host, port, and path string
|
|
18
|
+
# - Create a singular request and closes the connection immediately
|
|
19
|
+
# - Create a persistent connection and perform multiple HTTP requests
|
|
20
|
+
# - Notification payload must separate URI components
|
|
21
|
+
# - Create a post request with a body
|
|
22
|
+
# - Create a post request with form data
|
|
23
|
+
# - Create a request with basic authentication
|
|
24
|
+
# - Make a request via a proxy server
|
|
25
|
+
# - Streaming
|
|
26
|
+
module NetHttpPublisher
|
|
27
|
+
@prepend_mutex = Mutex.new
|
|
28
|
+
|
|
29
|
+
def self.labkit_prepend!
|
|
30
|
+
@prepend_mutex.synchronize do
|
|
31
|
+
return if @prepended
|
|
32
|
+
|
|
33
|
+
require "net/http"
|
|
34
|
+
Net::HTTP.prepend(self)
|
|
35
|
+
@prepended = true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def request(request, *args, &block)
|
|
40
|
+
return super unless started?
|
|
41
|
+
|
|
42
|
+
start_time = ::Labkit::System.monotonic_time
|
|
43
|
+
|
|
44
|
+
ActiveSupport::Notifications.instrument ::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, create_request_payload(request) do |payload|
|
|
45
|
+
response =
|
|
46
|
+
begin
|
|
47
|
+
super
|
|
48
|
+
ensure
|
|
49
|
+
payload[:duration] = (::Labkit::System.monotonic_time - start_time).to_f
|
|
50
|
+
end
|
|
51
|
+
payload[:code] = response.code
|
|
52
|
+
response
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def create_request_payload(request)
|
|
59
|
+
payload = {
|
|
60
|
+
method: request.method,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if request.uri.nil?
|
|
64
|
+
path_uri = URI(request.path)
|
|
65
|
+
payload[:host] = address
|
|
66
|
+
payload[:path] = path_uri.path
|
|
67
|
+
payload[:port] = port
|
|
68
|
+
payload[:scheme] = use_ssl? ? "https" : "http"
|
|
69
|
+
payload[:query] = path_uri.query
|
|
70
|
+
payload[:fragment] = path_uri.fragment
|
|
71
|
+
else
|
|
72
|
+
payload[:host] = request.uri.host
|
|
73
|
+
payload[:path] = request.uri.path
|
|
74
|
+
payload[:port] = request.uri.port
|
|
75
|
+
payload[:scheme] = request.uri.scheme
|
|
76
|
+
payload[:query] = request.uri.query
|
|
77
|
+
payload[:fragment] = request.uri.fragment
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if proxy?
|
|
81
|
+
payload[:proxy_host] = proxy_address
|
|
82
|
+
payload[:proxy_port] = proxy_port
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
payload
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
# A helper class to store system-related methods used in metrics, tracing, and logging
|
|
5
|
+
module System
|
|
6
|
+
# Returns the current monotonic clock time as seconds with microseconds precision.
|
|
7
|
+
#
|
|
8
|
+
# Returns the time as a Float.
|
|
9
|
+
def self.monotonic_time
|
|
10
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/labkit/tracing.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "active_support/all"
|
|
|
5
5
|
module Labkit
|
|
6
6
|
# Tracing provides distributed tracing functionality
|
|
7
7
|
module Tracing
|
|
8
|
+
autoload :AbstractInstrumenter, "labkit/tracing/abstract_instrumenter"
|
|
9
|
+
autoload :TracingCommon, "labkit/tracing/tracing_common"
|
|
8
10
|
autoload :Factory, "labkit/tracing/factory"
|
|
9
11
|
autoload :GRPC, "labkit/tracing/grpc"
|
|
10
12
|
autoload :GRPCInterceptor, "labkit/tracing/grpc_interceptor" # Deprecated
|
|
@@ -12,6 +14,7 @@ module Labkit
|
|
|
12
14
|
autoload :RackMiddleware, "labkit/tracing/rack_middleware"
|
|
13
15
|
autoload :Rails, "labkit/tracing/rails"
|
|
14
16
|
autoload :Redis, "labkit/tracing/redis"
|
|
17
|
+
autoload :ExternalHttp, "labkit/tracing/external_http"
|
|
15
18
|
autoload :Sidekiq, "labkit/tracing/sidekiq"
|
|
16
19
|
autoload :TracingUtils, "labkit/tracing/tracing_utils"
|
|
17
20
|
|
|
@@ -28,6 +31,12 @@ module Labkit
|
|
|
28
31
|
ENV["GITLAB_TRACING_URL"]
|
|
29
32
|
end
|
|
30
33
|
|
|
34
|
+
# Check if the current request is being traced.
|
|
35
|
+
def self.sampled?
|
|
36
|
+
context = OpenTracing.active_span&.context
|
|
37
|
+
context&.respond_to?(:sampled?) && context&.sampled?
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
def self.stacktrace_operations
|
|
32
41
|
@stacktrace_operations ||= Set.new(ENV["GITLAB_TRACING_INCLUDE_STACKTRACE"].to_s.split(",").map(&:strip))
|
|
33
42
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "opentracing"
|
|
4
|
+
require "active_support/all"
|
|
5
|
+
|
|
6
|
+
module Labkit
|
|
7
|
+
module Tracing
|
|
8
|
+
# https://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications/Instrumenter.html#method-c-new
|
|
9
|
+
class AbstractInstrumenter
|
|
10
|
+
def start(_name, _id, payload)
|
|
11
|
+
scope = OpenTracing.start_active_span(span_name(payload))
|
|
12
|
+
|
|
13
|
+
scope_stack.push scope
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def finish(_name, _id, payload)
|
|
17
|
+
scope = scope_stack.pop
|
|
18
|
+
span = scope.span
|
|
19
|
+
|
|
20
|
+
Labkit::Tracing::TracingUtils.log_common_fields_on_span(span, span_name(payload))
|
|
21
|
+
|
|
22
|
+
# exception_object is the standard exception payload from ActiveSupport::Notifications
|
|
23
|
+
# https://github.com/rails/rails/blob/v6.0.3.1/activesupport/lib/active_support/notifications/instrumenter.rb#L26
|
|
24
|
+
exception = payload[:exception_object].presence || payload[:exception].presence
|
|
25
|
+
Labkit::Tracing::TracingUtils.log_exception_on_span(span, exception)
|
|
26
|
+
|
|
27
|
+
tags(payload).each do |k, v|
|
|
28
|
+
span.set_tag(k, v)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
scope.close
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def scope_stack
|
|
35
|
+
Thread.current[:_labkit_trace_scope_stack] ||= []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def span_name(_payload)
|
|
39
|
+
raise "span_name not implemented"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def tags(_payload)
|
|
43
|
+
{}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
module Tracing
|
|
5
|
+
# Instrument external HTTP calls made by the HTTP client libraries. This
|
|
6
|
+
# tracing instrumenter listens to the events broadcasted from the
|
|
7
|
+
# publishers injected into the libraries whenever there is a request.
|
|
8
|
+
module ExternalHttp
|
|
9
|
+
include Labkit::Tracing::TracingCommon
|
|
10
|
+
|
|
11
|
+
autoload :RequestInstrumenter, "labkit/tracing/external_http/request_instrumenter"
|
|
12
|
+
|
|
13
|
+
def self.instrument
|
|
14
|
+
Labkit::NetHttpPublisher.labkit_prepend!
|
|
15
|
+
Labkit::ExconPublisher.labkit_prepend!
|
|
16
|
+
Labkit::HTTPClientPublisher.labkit_prepend!
|
|
17
|
+
|
|
18
|
+
subscriptions = [
|
|
19
|
+
::ActiveSupport::Notifications.subscribe(::Labkit::EXTERNAL_HTTP_NOTIFICATION_TOPIC, RequestInstrumenter.new),
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
create_unsubscriber subscriptions
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labkit
|
|
4
|
+
module Tracing
|
|
5
|
+
module ExternalHttp
|
|
6
|
+
# For more information on the payloads: lib/labkit/net_http_publisher.rb
|
|
7
|
+
class RequestInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
8
|
+
def span_name(_payload)
|
|
9
|
+
"external_http:request"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def tags(payload)
|
|
13
|
+
# Duration is calculated by start and end time
|
|
14
|
+
# Exception is already captured in lib/labkit/tracing/tracing_utils.rb
|
|
15
|
+
tags = {
|
|
16
|
+
"component" => "external_http",
|
|
17
|
+
"method" => payload[:method],
|
|
18
|
+
"code" => payload[:code],
|
|
19
|
+
"host" => payload[:host],
|
|
20
|
+
"port" => payload[:port],
|
|
21
|
+
"path" => payload[:path],
|
|
22
|
+
"scheme" => payload[:scheme],
|
|
23
|
+
}
|
|
24
|
+
unless payload[:proxy_host].nil?
|
|
25
|
+
tags["proxy_host"] = payload[:proxy_host]
|
|
26
|
+
tags["proxy_port"] = payload[:proxy_port]
|
|
27
|
+
end
|
|
28
|
+
tags
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/labkit/tracing/rails.rb
CHANGED
|
@@ -4,11 +4,9 @@ module Labkit
|
|
|
4
4
|
module Tracing
|
|
5
5
|
# Rails provides classes for instrumenting Rails events
|
|
6
6
|
module Rails
|
|
7
|
-
autoload :AbstractInstrumenter, "labkit/tracing/rails/abstract_instrumenter"
|
|
8
7
|
autoload :ActionView, "labkit/tracing/rails/action_view"
|
|
9
8
|
autoload :ActiveRecord, "labkit/tracing/rails/active_record"
|
|
10
9
|
autoload :ActiveSupport, "labkit/tracing/rails/active_support"
|
|
11
|
-
autoload :RailsCommon, "labkit/tracing/rails/rails_common"
|
|
12
10
|
|
|
13
11
|
ActionViewSubscriber = ActionView::Subscriber
|
|
14
12
|
ActiveRecordSubscriber = ActiveRecord::Subscriber
|
|
@@ -10,6 +10,20 @@ module Labkit
|
|
|
10
10
|
autoload :Subscriber, "labkit/tracing/rails/action_view/subscriber"
|
|
11
11
|
|
|
12
12
|
COMPONENT_TAG = "ActionView"
|
|
13
|
+
|
|
14
|
+
# Returns identifier relative to Rails.root. Rails supports different template types and returns corresponding identifiers:
|
|
15
|
+
# - Text template: the identifier is "text template"
|
|
16
|
+
# - Html template: the identifier is "html template"
|
|
17
|
+
# - Inline template: the identifier is "inline template"
|
|
18
|
+
# - Raw template: the identifier is the file path of the template
|
|
19
|
+
# Therefore, the amount of returned identifiers is static.
|
|
20
|
+
def self.template_identifier(payload)
|
|
21
|
+
return if !defined?(::Rails.root) || payload[:identifier].nil?
|
|
22
|
+
|
|
23
|
+
# Rails.root returns a Pathname object, whose `to_s` methods returns an absolute path without ending "/"
|
|
24
|
+
# Source: https://github.com/rails/rails/blob/v6.0.3.1/railties/lib/rails.rb#L64
|
|
25
|
+
payload[:identifier].sub("#{::Rails.root}/", "")
|
|
26
|
+
end
|
|
13
27
|
end
|
|
14
28
|
end
|
|
15
29
|
end
|
|
@@ -5,9 +5,14 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActionView
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class RenderCollectionInstrumenter < AbstractInstrumenter
|
|
8
|
+
class RenderCollectionInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
|
-
|
|
10
|
+
identifier = ActionView.template_identifier(payload)
|
|
11
|
+
if identifier.nil?
|
|
12
|
+
"render_collection"
|
|
13
|
+
else
|
|
14
|
+
"render_collection:#{identifier}"
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def tags(payload)
|
|
@@ -5,9 +5,14 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActionView
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class RenderPartialInstrumenter < AbstractInstrumenter
|
|
8
|
+
class RenderPartialInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
|
-
|
|
10
|
+
identifier = ActionView.template_identifier(payload)
|
|
11
|
+
if identifier.nil?
|
|
12
|
+
"render_partial"
|
|
13
|
+
else
|
|
14
|
+
"render_partial:#{identifier}"
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def tags(payload)
|
|
@@ -5,9 +5,14 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActionView
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class RenderTemplateInstrumenter < AbstractInstrumenter
|
|
8
|
+
class RenderTemplateInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
|
-
|
|
10
|
+
identifier = ActionView.template_identifier(payload)
|
|
11
|
+
if identifier.nil?
|
|
12
|
+
"render_template"
|
|
13
|
+
else
|
|
14
|
+
"render_template:#{identifier}"
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
|
|
13
18
|
def tags(payload)
|
|
@@ -7,7 +7,7 @@ module Labkit
|
|
|
7
7
|
# ActionView bridges action view notifications to
|
|
8
8
|
# the distributed tracing subsystem
|
|
9
9
|
class Subscriber
|
|
10
|
-
include
|
|
10
|
+
include Labkit::Tracing::TracingCommon
|
|
11
11
|
|
|
12
12
|
RENDER_TEMPLATE_NOTIFICATION_TOPIC = "render_template.action_view"
|
|
13
13
|
RENDER_COLLECTION_NOTIFICATION_TOPIC = "render_collection.action_view"
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveRecord
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class SqlInstrumenter < AbstractInstrumenter
|
|
8
|
+
class SqlInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
OPERATION_NAME_PREFIX = "active_record:"
|
|
10
10
|
DEFAULT_OPERATION_NAME = "sqlquery"
|
|
11
11
|
|
|
@@ -14,13 +14,14 @@ module Labkit
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def tags(payload)
|
|
17
|
+
sql = Labkit::Logging::Sanitizer.sanitize_sql(payload[:sql]) if Labkit::Tracing.sampled? && payload[:sql]
|
|
17
18
|
{
|
|
18
19
|
"component" => COMPONENT_TAG,
|
|
19
20
|
"span.kind" => "client",
|
|
20
21
|
"db.type" => "sql",
|
|
21
22
|
"db.connection_id" => payload[:connection_id],
|
|
22
23
|
"db.cached" => payload[:cached] || false,
|
|
23
|
-
"db.statement" =>
|
|
24
|
+
"db.statement" => sql,
|
|
24
25
|
}
|
|
25
26
|
end
|
|
26
27
|
end
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class CacheDeleteInstrumenter < AbstractInstrumenter
|
|
8
|
+
class CacheDeleteInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
10
|
"cache_delete"
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class CacheFetchHitInstrumenter < AbstractInstrumenter
|
|
8
|
+
class CacheFetchHitInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
10
|
"cache_fetch_hit"
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class CacheGenerateInstrumenter < AbstractInstrumenter
|
|
8
|
+
class CacheGenerateInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
10
|
"cache_generate"
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class CacheReadInstrumenter < AbstractInstrumenter
|
|
8
|
+
class CacheReadInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
10
|
"cache_read"
|
|
11
11
|
end
|
|
@@ -5,7 +5,7 @@ module Labkit
|
|
|
5
5
|
module Rails
|
|
6
6
|
module ActiveSupport
|
|
7
7
|
# For more information on the payloads: https://guides.rubyonrails.org/active_support_instrumentation.html
|
|
8
|
-
class CacheWriteInstrumenter < AbstractInstrumenter
|
|
8
|
+
class CacheWriteInstrumenter < Labkit::Tracing::AbstractInstrumenter
|
|
9
9
|
def span_name(payload)
|
|
10
10
|
"cache_write"
|
|
11
11
|
end
|
|
@@ -7,7 +7,7 @@ module Labkit
|
|
|
7
7
|
# ActiveSupport bridges action active support notifications to
|
|
8
8
|
# the distributed tracing subsystem
|
|
9
9
|
class Subscriber
|
|
10
|
-
include
|
|
10
|
+
include Labkit::Tracing::TracingCommon
|
|
11
11
|
|
|
12
12
|
CACHE_READ_TOPIC = "cache_read.active_support"
|
|
13
13
|
CACHE_GENERATE_TOPIC = "cache_generate.active_support"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/all"
|
|
4
|
+
|
|
5
|
+
module Labkit
|
|
6
|
+
module Tracing
|
|
7
|
+
# TracingCommon is a mixin for providing instrumentation
|
|
8
|
+
# functionality for the instrumentation classes based on
|
|
9
|
+
# ActiveSupport::Notifications
|
|
10
|
+
module TracingCommon
|
|
11
|
+
extend ::ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
class_methods do
|
|
14
|
+
def create_unsubscriber(subscriptions)
|
|
15
|
+
-> { subscriptions.each { |subscriber| ::ActiveSupport::Notifications.unsubscribe(subscriber) } }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -48,8 +48,10 @@ module Labkit
|
|
|
48
48
|
|
|
49
49
|
# Add exception logging to a span
|
|
50
50
|
def self.log_exception_on_span(span, exception)
|
|
51
|
+
return if exception.blank?
|
|
52
|
+
|
|
51
53
|
span.set_tag("error", true)
|
|
52
|
-
span.log_kv(kv_tags_for_exception(exception))
|
|
54
|
+
span.log_kv(**kv_tags_for_exception(exception))
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
# Generate key-value tags for an exception
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab-labkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Newdigate
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-02-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: actionpack
|
|
@@ -19,7 +19,7 @@ dependencies:
|
|
|
19
19
|
version: 5.0.0
|
|
20
20
|
- - "<"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version:
|
|
22
|
+
version: 7.0.0
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -29,7 +29,7 @@ dependencies:
|
|
|
29
29
|
version: 5.0.0
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
32
|
+
version: 7.0.0
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
|
34
34
|
name: activesupport
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -39,7 +39,7 @@ dependencies:
|
|
|
39
39
|
version: 5.0.0
|
|
40
40
|
- - "<"
|
|
41
41
|
- !ruby/object:Gem::Version
|
|
42
|
-
version:
|
|
42
|
+
version: 7.0.0
|
|
43
43
|
type: :runtime
|
|
44
44
|
prerelease: false
|
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -49,7 +49,7 @@ dependencies:
|
|
|
49
49
|
version: 5.0.0
|
|
50
50
|
- - "<"
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
|
-
version:
|
|
52
|
+
version: 7.0.0
|
|
53
53
|
- !ruby/object:Gem::Dependency
|
|
54
54
|
name: grpc
|
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,6 +92,20 @@ dependencies:
|
|
|
92
92
|
- - "~>"
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
94
|
version: '0.4'
|
|
95
|
+
- !ruby/object:Gem::Dependency
|
|
96
|
+
name: pg_query
|
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '1.3'
|
|
102
|
+
type: :runtime
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: '1.3'
|
|
95
109
|
- !ruby/object:Gem::Dependency
|
|
96
110
|
name: redis
|
|
97
111
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -112,6 +126,34 @@ dependencies:
|
|
|
112
126
|
- - "<"
|
|
113
127
|
- !ruby/object:Gem::Version
|
|
114
128
|
version: 5.0.0
|
|
129
|
+
- !ruby/object:Gem::Dependency
|
|
130
|
+
name: excon
|
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
|
132
|
+
requirements:
|
|
133
|
+
- - "~>"
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: 0.78.1
|
|
136
|
+
type: :development
|
|
137
|
+
prerelease: false
|
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - "~>"
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: 0.78.1
|
|
143
|
+
- !ruby/object:Gem::Dependency
|
|
144
|
+
name: faraday
|
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - "~>"
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: 1.2.0
|
|
150
|
+
type: :development
|
|
151
|
+
prerelease: false
|
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
153
|
+
requirements:
|
|
154
|
+
- - "~>"
|
|
155
|
+
- !ruby/object:Gem::Version
|
|
156
|
+
version: 1.2.0
|
|
115
157
|
- !ruby/object:Gem::Dependency
|
|
116
158
|
name: grpc-tools
|
|
117
159
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -126,6 +168,34 @@ dependencies:
|
|
|
126
168
|
- - "~>"
|
|
127
169
|
- !ruby/object:Gem::Version
|
|
128
170
|
version: '1.19'
|
|
171
|
+
- !ruby/object:Gem::Dependency
|
|
172
|
+
name: httparty
|
|
173
|
+
requirement: !ruby/object:Gem::Requirement
|
|
174
|
+
requirements:
|
|
175
|
+
- - "~>"
|
|
176
|
+
- !ruby/object:Gem::Version
|
|
177
|
+
version: 0.17.3
|
|
178
|
+
type: :development
|
|
179
|
+
prerelease: false
|
|
180
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
181
|
+
requirements:
|
|
182
|
+
- - "~>"
|
|
183
|
+
- !ruby/object:Gem::Version
|
|
184
|
+
version: 0.17.3
|
|
185
|
+
- !ruby/object:Gem::Dependency
|
|
186
|
+
name: httpclient
|
|
187
|
+
requirement: !ruby/object:Gem::Requirement
|
|
188
|
+
requirements:
|
|
189
|
+
- - "~>"
|
|
190
|
+
- !ruby/object:Gem::Version
|
|
191
|
+
version: 2.8.3
|
|
192
|
+
type: :development
|
|
193
|
+
prerelease: false
|
|
194
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
195
|
+
requirements:
|
|
196
|
+
- - "~>"
|
|
197
|
+
- !ruby/object:Gem::Version
|
|
198
|
+
version: 2.8.3
|
|
129
199
|
- !ruby/object:Gem::Dependency
|
|
130
200
|
name: pry
|
|
131
201
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -168,6 +238,20 @@ dependencies:
|
|
|
168
238
|
- - "~>"
|
|
169
239
|
- !ruby/object:Gem::Version
|
|
170
240
|
version: '12.3'
|
|
241
|
+
- !ruby/object:Gem::Dependency
|
|
242
|
+
name: rest-client
|
|
243
|
+
requirement: !ruby/object:Gem::Requirement
|
|
244
|
+
requirements:
|
|
245
|
+
- - "~>"
|
|
246
|
+
- !ruby/object:Gem::Version
|
|
247
|
+
version: 2.1.0
|
|
248
|
+
type: :development
|
|
249
|
+
prerelease: false
|
|
250
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
251
|
+
requirements:
|
|
252
|
+
- - "~>"
|
|
253
|
+
- !ruby/object:Gem::Version
|
|
254
|
+
version: 2.1.0
|
|
171
255
|
- !ruby/object:Gem::Dependency
|
|
172
256
|
name: rspec
|
|
173
257
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -261,6 +345,7 @@ extra_rdoc_files: []
|
|
|
261
345
|
files:
|
|
262
346
|
- ".gitignore"
|
|
263
347
|
- ".gitlab-ci.yml"
|
|
348
|
+
- ".gitlab/CODEOWNERS"
|
|
264
349
|
- ".rspec"
|
|
265
350
|
- ".rubocop.yml"
|
|
266
351
|
- ".ruby-version"
|
|
@@ -278,6 +363,8 @@ files:
|
|
|
278
363
|
- lib/labkit/correlation/grpc/client_interceptor.rb
|
|
279
364
|
- lib/labkit/correlation/grpc/grpc_common.rb
|
|
280
365
|
- lib/labkit/correlation/grpc/server_interceptor.rb
|
|
366
|
+
- lib/labkit/excon_publisher.rb
|
|
367
|
+
- lib/labkit/httpclient_publisher.rb
|
|
281
368
|
- lib/labkit/logging.rb
|
|
282
369
|
- lib/labkit/logging/grpc.rb
|
|
283
370
|
- lib/labkit/logging/grpc/server_interceptor.rb
|
|
@@ -294,7 +381,12 @@ files:
|
|
|
294
381
|
- lib/labkit/middleware/sidekiq/tracing/client.rb
|
|
295
382
|
- lib/labkit/middleware/sidekiq/tracing/server.rb
|
|
296
383
|
- lib/labkit/middleware/sidekiq/tracing/sidekiq_common.rb
|
|
384
|
+
- lib/labkit/net_http_publisher.rb
|
|
385
|
+
- lib/labkit/system.rb
|
|
297
386
|
- lib/labkit/tracing.rb
|
|
387
|
+
- lib/labkit/tracing/abstract_instrumenter.rb
|
|
388
|
+
- lib/labkit/tracing/external_http.rb
|
|
389
|
+
- lib/labkit/tracing/external_http/request_instrumenter.rb
|
|
298
390
|
- lib/labkit/tracing/factory.rb
|
|
299
391
|
- lib/labkit/tracing/grpc.rb
|
|
300
392
|
- lib/labkit/tracing/grpc/client_interceptor.rb
|
|
@@ -303,7 +395,6 @@ files:
|
|
|
303
395
|
- lib/labkit/tracing/jaeger_factory.rb
|
|
304
396
|
- lib/labkit/tracing/rack_middleware.rb
|
|
305
397
|
- lib/labkit/tracing/rails.rb
|
|
306
|
-
- lib/labkit/tracing/rails/abstract_instrumenter.rb
|
|
307
398
|
- lib/labkit/tracing/rails/action_view.rb
|
|
308
399
|
- lib/labkit/tracing/rails/action_view/render_collection_instrumenter.rb
|
|
309
400
|
- lib/labkit/tracing/rails/action_view/render_partial_instrumenter.rb
|
|
@@ -319,10 +410,10 @@ files:
|
|
|
319
410
|
- lib/labkit/tracing/rails/active_support/cache_read_instrumenter.rb
|
|
320
411
|
- lib/labkit/tracing/rails/active_support/cache_write_instrumenter.rb
|
|
321
412
|
- lib/labkit/tracing/rails/active_support/subscriber.rb
|
|
322
|
-
- lib/labkit/tracing/rails/rails_common.rb
|
|
323
413
|
- lib/labkit/tracing/redis.rb
|
|
324
414
|
- lib/labkit/tracing/redis/redis_interceptor.rb
|
|
325
415
|
- lib/labkit/tracing/redis/redis_interceptor_helper.rb
|
|
416
|
+
- lib/labkit/tracing/tracing_common.rb
|
|
326
417
|
- lib/labkit/tracing/tracing_utils.rb
|
|
327
418
|
homepage: https://gitlab.com/gitlab-org/labkit-ruby
|
|
328
419
|
licenses:
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "active_support/all"
|
|
4
|
-
|
|
5
|
-
module Labkit
|
|
6
|
-
module Tracing
|
|
7
|
-
module Rails
|
|
8
|
-
# https://edgeapi.rubyonrails.org/classes/ActiveSupport/Notifications/Instrumenter.html#method-c-new
|
|
9
|
-
class AbstractInstrumenter
|
|
10
|
-
def start(name, id, payload)
|
|
11
|
-
scope = OpenTracing.start_active_span(span_name(payload))
|
|
12
|
-
|
|
13
|
-
scope_stack.push scope
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def finish(name, id, payload)
|
|
17
|
-
scope = scope_stack.pop
|
|
18
|
-
span = scope.span
|
|
19
|
-
|
|
20
|
-
Labkit::Tracing::TracingUtils.log_common_fields_on_span(span, span_name(payload))
|
|
21
|
-
|
|
22
|
-
exception = payload[:exception]
|
|
23
|
-
Labkit::Tracing::TracingUtils.log_exception_on_span(span, exception) if exception
|
|
24
|
-
|
|
25
|
-
tags(payload).each do |k, v|
|
|
26
|
-
span.set_tag(k, v)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
scope.close
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def scope_stack
|
|
33
|
-
Thread.current[:_labkit_trace_scope_stack] ||= []
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def span_name(payload)
|
|
37
|
-
raise "span_name not implemented"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def tags(payload)
|
|
41
|
-
{}
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "active_support/all"
|
|
4
|
-
|
|
5
|
-
module Labkit
|
|
6
|
-
module Tracing
|
|
7
|
-
module Rails
|
|
8
|
-
# RailsCommon is a mixin for providing instrumentation
|
|
9
|
-
# functionality for the rails instrumentation classes
|
|
10
|
-
module RailsCommon
|
|
11
|
-
extend ::ActiveSupport::Concern
|
|
12
|
-
|
|
13
|
-
class_methods do
|
|
14
|
-
def create_unsubscriber(subscriptions)
|
|
15
|
-
-> { subscriptions.each { |subscriber| ::ActiveSupport::Notifications.unsubscribe(subscriber) } }
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|