epsagon 0.0.0

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