epsagon 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|