epsagon 0.0.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/lib/epsagon.rb +42 -0
- data/lib/epsagon_opentelemetry.rb +42 -0
- data/lib/instrumentation/faraday.rb +93 -0
- data/lib/instrumentation/net_http.rb +78 -0
- data/lib/instrumentation/sinatra.rb +111 -0
- data/lib/util.rb +13 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 036b9458b78db168edb2cb90dd4898f812eaa81a3e057abd69fc073fef9552d2
|
4
|
+
data.tar.gz: 1b2b2dce82f55073377db72400737a1243f8dab08426780d59f6a5faf62a2a13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5bbbc9237c61ad7eb0818803eea213ed8b1003210b199c3c29355f1536b6dc4b7e990b628b92b8c015f4fab1c623eb92f07352d6c6cb5b6a00d0aeb12aa0dfc8
|
7
|
+
data.tar.gz: 81da4b0e55ecd53569564c22cfe1a986dce0835970bf3a522a7bc100e5b5a6c373275c6ac2bccd9d0a4500dddb8c26dca54d402d499116fa90201ba8de5edef5
|
data/lib/epsagon.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'net/http'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'opentelemetry'
|
7
|
+
|
8
|
+
require_relative 'instrumentation/sinatra'
|
9
|
+
require_relative 'instrumentation/net_http'
|
10
|
+
require_relative 'instrumentation/faraday'
|
11
|
+
require_relative 'util'
|
12
|
+
|
13
|
+
Bundler.require
|
14
|
+
|
15
|
+
def metadata_only?
|
16
|
+
ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false'
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug?
|
20
|
+
ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true'
|
21
|
+
end
|
22
|
+
|
23
|
+
# #config opentelemetry with epsaon extensions:
|
24
|
+
OpenTelemetry::SDK.configure do |c|
|
25
|
+
c.use 'EpsagonSinatraInstrumentation'
|
26
|
+
c.use 'EpsagonNetHTTPInstrumentation'
|
27
|
+
c.use 'EpsagonFaradayInstrumentation'
|
28
|
+
if ENV['EPSAGON_BACKEND']
|
29
|
+
c.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
30
|
+
OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
|
31
|
+
epasgon_token: ENV['EPSAGON_TOKEN'],
|
32
|
+
epasgon_app_name: ENV['EPSAGON_APP_NAME']
|
33
|
+
},
|
34
|
+
endpoint: ENV['EPSAGON_BACKEND'],
|
35
|
+
insecure: false)
|
36
|
+
)
|
37
|
+
else
|
38
|
+
c.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
39
|
+
OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'net/http'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'opentelemetry'
|
7
|
+
|
8
|
+
require_relative 'instrumentation/sinatra'
|
9
|
+
require_relative 'instrumentation/net_http'
|
10
|
+
require_relative 'instrumentation/faraday'
|
11
|
+
require_relative 'util'
|
12
|
+
|
13
|
+
Bundler.require
|
14
|
+
|
15
|
+
def metadata_only?
|
16
|
+
ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false'
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug?
|
20
|
+
ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true'
|
21
|
+
end
|
22
|
+
|
23
|
+
# #config opentelemetry with epsaon extensions:
|
24
|
+
OpenTelemetry::SDK.configure do |c|
|
25
|
+
c.use 'EpsagonSinatraInstrumentation'
|
26
|
+
c.use 'EpsagonNetHTTPInstrumentation'
|
27
|
+
c.use 'EpsagonFaradayInstrumentation'
|
28
|
+
if debug?
|
29
|
+
c.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
30
|
+
OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
|
31
|
+
)
|
32
|
+
else
|
33
|
+
c.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
|
34
|
+
EpsagonExporter.new(headers: {
|
35
|
+
epasgon_token: ENV['EPSAGON_TOKEN'],
|
36
|
+
epasgon_app_name: ENV['EPSAGON_APP_NAME']
|
37
|
+
},
|
38
|
+
endpoint: '7fybd5lgpc.execute-api.us-east-2.amazonaws.com:443/dev/trace',
|
39
|
+
insecure: false)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require_relative '../util'
|
5
|
+
|
6
|
+
class EpsagonFaradayMiddleware < ::Faraday::Middleware
|
7
|
+
HTTP_METHODS_SYMBOL_TO_STRING = {
|
8
|
+
connect: 'CONNECT',
|
9
|
+
delete: 'DELETE',
|
10
|
+
get: 'GET',
|
11
|
+
head: 'HEAD',
|
12
|
+
options: 'OPTIONS',
|
13
|
+
patch: 'PATCH',
|
14
|
+
post: 'POST',
|
15
|
+
put: 'PUT',
|
16
|
+
trace: 'TRACE'
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
http_method = HTTP_METHODS_SYMBOL_TO_STRING[env.method]
|
21
|
+
path, path_params = env.url.path.split(';')
|
22
|
+
|
23
|
+
attributes = {
|
24
|
+
'type' => 'http',
|
25
|
+
'operation' => http_method,
|
26
|
+
'http.scheme' => env.url.scheme,
|
27
|
+
'http.request.path' => path
|
28
|
+
}
|
29
|
+
|
30
|
+
unless metadata_only?
|
31
|
+
attributes.merge!(Util.epsagon_query_attributes(env.url.query))
|
32
|
+
attributes.merge!({
|
33
|
+
'http.request.path_params' => path_params,
|
34
|
+
'http.request.headers' => env.request_headers.to_json,
|
35
|
+
'http.request.body' => env.body,
|
36
|
+
'http.request.headers.User-Agent' => env.request_headers['User-Agent']
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
tracer.in_span(
|
41
|
+
env.url.host,
|
42
|
+
attributes: attributes,
|
43
|
+
kind: :client
|
44
|
+
) do |span|
|
45
|
+
OpenTelemetry.propagation.http.inject(env.request_headers)
|
46
|
+
|
47
|
+
app.call(env).on_complete { |req| trace_response(span, req.response) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :app
|
54
|
+
|
55
|
+
def tracer
|
56
|
+
EpsagonFaradayInstrumentation.instance.tracer
|
57
|
+
end
|
58
|
+
|
59
|
+
def trace_response(span, response)
|
60
|
+
span.set_attribute('http.status_code', response.status)
|
61
|
+
|
62
|
+
unless metadata_only?
|
63
|
+
span.set_attribute('http.response.headers', response.headers.to_json)
|
64
|
+
span.set_attribute('http.response.body', response.body)
|
65
|
+
end
|
66
|
+
span.status = OpenTelemetry::Trace::Status.http_to_status(
|
67
|
+
response.status
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module EpsagonFaradayPatch
|
73
|
+
def adapter(*args)
|
74
|
+
use(:epsagon_open_telemetry) unless @handlers.any? do |handler|
|
75
|
+
handler.klass == EpsagonFaradayMiddleware
|
76
|
+
end
|
77
|
+
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class EpsagonFaradayInstrumentation < OpenTelemetry::Instrumentation::Base
|
83
|
+
install do |_config|
|
84
|
+
::Faraday::Middleware.register_middleware(
|
85
|
+
epsagon_open_telemetry: EpsagonFaradayMiddleware
|
86
|
+
)
|
87
|
+
::Faraday::RackBuilder.prepend(EpsagonFaradayPatch)
|
88
|
+
end
|
89
|
+
|
90
|
+
present do
|
91
|
+
defined?(::Faraday)
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'opentelemetry'
|
4
|
+
|
5
|
+
require_relative '../util'
|
6
|
+
|
7
|
+
module EpsagonNetHTTPExtension
|
8
|
+
HTTP_METHODS_TO_SPAN_NAMES = Hash.new { |h, k| h[k] = "HTTP #{k}" }
|
9
|
+
USE_SSL_TO_SCHEME = { false => 'http', true => 'https' }.freeze
|
10
|
+
|
11
|
+
def request(req, body = nil, &block)
|
12
|
+
# Do not trace recursive call for starting the connection
|
13
|
+
return super(req, body, &block) unless started?
|
14
|
+
|
15
|
+
attributes = Hash[OpenTelemetry::Common::HTTP::ClientContext.attributes]
|
16
|
+
path_with_params, query = req.path.split('?')
|
17
|
+
path, path_params = path_with_params.split(';')
|
18
|
+
attributes.merge!({
|
19
|
+
'type' => 'http',
|
20
|
+
'operation' => req.method,
|
21
|
+
'http.scheme' => USE_SSL_TO_SCHEME[use_ssl?],
|
22
|
+
'http.request.path' => path
|
23
|
+
})
|
24
|
+
|
25
|
+
unless metadata_only?
|
26
|
+
headers = Hash[req.each_header.to_a]
|
27
|
+
attributes.merge!({
|
28
|
+
'http.request.path_params' => path_params,
|
29
|
+
'http.request.body' => body,
|
30
|
+
'http.request.headers' => headers.to_json,
|
31
|
+
'http.request.headers.User-Agent' => headers['user-agent']
|
32
|
+
})
|
33
|
+
attributes.merge!(Util.epsagon_query_attributes(query))
|
34
|
+
end
|
35
|
+
tracer.in_span(
|
36
|
+
@address,
|
37
|
+
attributes: attributes,
|
38
|
+
kind: :Client
|
39
|
+
) do |span|
|
40
|
+
OpenTelemetry.propagation.http.inject(req)
|
41
|
+
|
42
|
+
super(req, body, &block).tap do |response|
|
43
|
+
annotate_span_with_response!(span, response)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def annotate_span_with_response!(span, response)
|
51
|
+
return unless response&.code
|
52
|
+
|
53
|
+
status_code = response.code.to_i
|
54
|
+
|
55
|
+
span.set_attribute('http.status_code', status_code)
|
56
|
+
unless metadata_only?
|
57
|
+
span.set_attribute('http.response.headers', Hash[response.each_header.to_a].to_json)
|
58
|
+
span.set_attribute('http.response.body', response.body)
|
59
|
+
end
|
60
|
+
span.status = OpenTelemetry::Trace::Status.http_to_status(
|
61
|
+
status_code
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def tracer
|
66
|
+
EpsagonSinatraInstrumentation.instance.tracer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class EpsagonNetHTTPInstrumentation < OpenTelemetry::Instrumentation::Base
|
71
|
+
install do |_|
|
72
|
+
::Net::HTTP.prepend(EpsagonNetHTTPExtension)
|
73
|
+
end
|
74
|
+
|
75
|
+
present do
|
76
|
+
defined?(::Net::HTTP)
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sinatra'
|
4
|
+
require 'opentelemetry'
|
5
|
+
|
6
|
+
require_relative '../util'
|
7
|
+
|
8
|
+
class EpsagonTracerMiddleware
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
path, path_params = request.path.split(';')
|
16
|
+
request_headers = JSON.generate(Hash[*env.select { |k, _v| k.start_with? 'HTTP_' }
|
17
|
+
.collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
|
18
|
+
.collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
|
19
|
+
.sort
|
20
|
+
.flatten])
|
21
|
+
|
22
|
+
attributes = {
|
23
|
+
'operation' => env['REQUEST_METHOD'],
|
24
|
+
'type' => 'http',
|
25
|
+
'http.scheme' => env['PATH_INFO'],
|
26
|
+
'http.request.path' => path,
|
27
|
+
'http.request.headers' => request_headers
|
28
|
+
}
|
29
|
+
|
30
|
+
unless metadata_only?
|
31
|
+
request.body.rewind
|
32
|
+
request_body = request.body.read
|
33
|
+
request.body.rewind
|
34
|
+
|
35
|
+
attributes.merge!(Util.epsagon_query_attributes(request.query_string))
|
36
|
+
|
37
|
+
attributes.merge!({
|
38
|
+
'http.request.body' => request_body,
|
39
|
+
'http.request.path_params' => path_params,
|
40
|
+
'http.request.headers.User-Agent' => env['HTTP_USER_AGENT']
|
41
|
+
})
|
42
|
+
end
|
43
|
+
|
44
|
+
tracer.in_span(
|
45
|
+
env['HTTP_HOST'],
|
46
|
+
attributes: attributes,
|
47
|
+
kind: :Server,
|
48
|
+
with_parent: parent_context(env)
|
49
|
+
) do |http_span|
|
50
|
+
tracer.in_span('sinatra') do |framework_span|
|
51
|
+
app.call(env).tap { |resp| trace_response(http_span, framework_span, env, resp) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :app
|
59
|
+
|
60
|
+
def parent_context(env)
|
61
|
+
OpenTelemetry.propagation.http.extract(env)
|
62
|
+
end
|
63
|
+
|
64
|
+
def tracer
|
65
|
+
EpsagonSinatraInstrumentation.instance.tracer
|
66
|
+
end
|
67
|
+
|
68
|
+
def trace_response(http_span, framework_span, env, resp)
|
69
|
+
status, headers, response_body = resp
|
70
|
+
|
71
|
+
unless metadata_only?
|
72
|
+
http_span.set_attribute('http.response.headers', JSON.generate(headers))
|
73
|
+
http_span.set_attribute('http.response.body', response_body.join)
|
74
|
+
end
|
75
|
+
|
76
|
+
http_span.set_attribute('http.status_code', status)
|
77
|
+
framework_span.set_attribute('http.route', env['sinatra.route'].split.last) if env['sinatra.route']
|
78
|
+
http_span.status = OpenTelemetry::Trace::Status.http_to_status(status)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module EpsagonTracerExtension
|
83
|
+
# Sinatra hook after extension is registered
|
84
|
+
def self.registered(app)
|
85
|
+
# Create tracing `render` method
|
86
|
+
::Sinatra::Base.module_eval do
|
87
|
+
def render(_engine, data, *)
|
88
|
+
template_name = data.is_a?(Symbol) ? data : :literal
|
89
|
+
|
90
|
+
Sinatra::Instrumentation.instance.tracer.in_span(
|
91
|
+
'sinatra.render_template',
|
92
|
+
attributes: { 'sinatra.template_name' => template_name.to_s }
|
93
|
+
) do
|
94
|
+
super
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
app.use EpsagonTracerMiddleware
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class EpsagonSinatraInstrumentation < OpenTelemetry::Instrumentation::Base
|
104
|
+
install do |_|
|
105
|
+
::Sinatra::Base.register EpsagonTracerExtension
|
106
|
+
end
|
107
|
+
|
108
|
+
present do
|
109
|
+
defined?(::Sinatra)
|
110
|
+
end
|
111
|
+
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module Util
|
6
|
+
def self.epsagon_query_attributes(query_string)
|
7
|
+
if query_string&.include? '='
|
8
|
+
{ 'http.request.query_params' => CGI.parse(query_string).to_json }
|
9
|
+
else
|
10
|
+
{ 'http.request.query' => query_string }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: epsagon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Assaf Paneth
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-06 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Epsagon for ruby
|
14
|
+
email: assaf.paneth@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/epsagon.rb
|
20
|
+
- lib/epsagon_opentelemetry.rb
|
21
|
+
- lib/instrumentation/faraday.rb
|
22
|
+
- lib/instrumentation/net_http.rb
|
23
|
+
- lib/instrumentation/sinatra.rb
|
24
|
+
- lib/util.rb
|
25
|
+
homepage: https://rubygems.org/gems/epsagon
|
26
|
+
licenses:
|
27
|
+
- MIT
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubygems_version: 3.1.4
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: Epsagon for ruby
|
48
|
+
test_files: []
|