stackify-ruby-apm 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +38 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +17 -11
  5. data/Gemfile.lock +98 -95
  6. data/Rakefile +7 -5
  7. data/docker/stackify-ruby +8 -0
  8. data/docker/stackify-ruby-rvm +10 -0
  9. data/docker/stackify-ruby-test +28 -0
  10. data/lib/{stackify → stackify_apm}/agent.rb +42 -33
  11. data/lib/{stackify → stackify_apm}/config.rb +56 -39
  12. data/lib/{stackify → stackify_apm}/context.rb +5 -6
  13. data/lib/{stackify → stackify_apm}/context/request.rb +0 -0
  14. data/lib/{stackify → stackify_apm}/context/request/socket.rb +0 -0
  15. data/lib/{stackify → stackify_apm}/context/request/url.rb +2 -6
  16. data/lib/stackify_apm/context/response.rb +33 -0
  17. data/lib/{stackify → stackify_apm}/context_builder.rb +2 -5
  18. data/lib/{stackify → stackify_apm}/error.rb +7 -6
  19. data/lib/stackify_apm/error/exception.rb +37 -0
  20. data/lib/stackify_apm/error/log.rb +24 -0
  21. data/lib/stackify_apm/error_builder.rb +61 -0
  22. data/lib/stackify_apm/helper/database_helper.rb +27 -0
  23. data/lib/{stackify → stackify_apm}/instrumenter.rb +12 -19
  24. data/lib/{stackify → stackify_apm}/internal_error.rb +0 -0
  25. data/lib/{stackify → stackify_apm}/log.rb +0 -0
  26. data/lib/{stackify → stackify_apm}/logger/log_device.rb +22 -11
  27. data/lib/{stackify → stackify_apm}/logger/logger_high_version.rb +1 -6
  28. data/lib/{stackify → stackify_apm}/logger/logger_lower_version.rb +2 -1
  29. data/lib/stackify_apm/middleware.rb +70 -0
  30. data/lib/{stackify → stackify_apm}/naively_hashable.rb +1 -3
  31. data/lib/{stackify → stackify_apm}/normalizers.rb +3 -2
  32. data/lib/{stackify → stackify_apm}/normalizers/action_controller.rb +0 -0
  33. data/lib/{stackify → stackify_apm}/normalizers/action_mailer.rb +0 -0
  34. data/lib/{stackify → stackify_apm}/normalizers/action_view.rb +0 -0
  35. data/lib/{stackify → stackify_apm}/normalizers/active_record.rb +3 -25
  36. data/lib/{stackify → stackify_apm}/railtie.rb +5 -7
  37. data/lib/{stackify → stackify_apm}/root_info.rb +2 -6
  38. data/lib/{stackify → stackify_apm}/serializers.rb +3 -2
  39. data/lib/{stackify → stackify_apm}/serializers/errors.rb +7 -10
  40. data/lib/{stackify → stackify_apm}/serializers/transactions.rb +11 -18
  41. data/lib/{stackify → stackify_apm}/span.rb +8 -9
  42. data/lib/{stackify → stackify_apm}/span/context.rb +3 -1
  43. data/lib/{stackify → stackify_apm}/spies.rb +3 -2
  44. data/lib/{stackify → stackify_apm}/spies/action_dispatch.rb +3 -4
  45. data/lib/stackify_apm/spies/curb.rb +49 -0
  46. data/lib/stackify_apm/spies/curb/easy.rb +157 -0
  47. data/lib/stackify_apm/spies/curb/multi.rb +43 -0
  48. data/lib/{stackify → stackify_apm}/spies/httpclient.rb +10 -8
  49. data/lib/{stackify → stackify_apm}/spies/httprb.rb +7 -9
  50. data/lib/{stackify → stackify_apm}/spies/mongo.rb +5 -3
  51. data/lib/{stackify → stackify_apm}/spies/net_http.rb +4 -5
  52. data/lib/{stackify → stackify_apm}/spies/redis.rb +19 -18
  53. data/lib/stackify_apm/spies/sequel.rb +65 -0
  54. data/lib/{stackify → stackify_apm}/spies/sinatra.rb +7 -10
  55. data/lib/stackify_apm/spies/sinatra_activerecord/mysql_adapter.rb +201 -0
  56. data/lib/stackify_apm/spies/sinatra_activerecord/postgresql_adapter.rb +94 -0
  57. data/lib/stackify_apm/spies/sinatra_activerecord/sqlite_adapter.rb +46 -0
  58. data/lib/stackify_apm/spies/stackify_logger.rb +60 -0
  59. data/lib/{stackify → stackify_apm}/spies/tilt.rb +3 -3
  60. data/lib/stackify_apm/stacktrace.rb +18 -0
  61. data/lib/stackify_apm/stacktrace/frame.rb +47 -0
  62. data/lib/{stackify → stackify_apm}/stacktrace_builder.rb +10 -11
  63. data/lib/{stackify → stackify_apm}/subscriber.rb +20 -14
  64. data/lib/{stackify → stackify_apm}/trace_logger.rb +10 -16
  65. data/lib/stackify_apm/transaction.rb +127 -0
  66. data/lib/{stackify → stackify_apm}/util.rb +3 -1
  67. data/lib/{stackify → stackify_apm}/util/dig.rb +1 -1
  68. data/lib/{stackify → stackify_apm}/util/inflector.rb +0 -0
  69. data/lib/{stackify → stackify_apm}/util/inspector.rb +1 -3
  70. data/lib/stackify_apm/util/lru_cache.rb +49 -0
  71. data/lib/stackify_apm/util/trace_log_watcher.rb +37 -0
  72. data/lib/stackify_apm/version.rb +6 -0
  73. data/lib/{stackify → stackify_apm}/worker.rb +8 -7
  74. data/lib/stackify_ruby_apm.rb +18 -15
  75. data/run-test-docker.sh +50 -0
  76. data/run-test.sh +1 -3
  77. data/stackify-ruby-apm.gemspec +14 -11
  78. metadata +86 -59
  79. data/lib/stackify/context/response.rb +0 -37
  80. data/lib/stackify/error/exception.rb +0 -36
  81. data/lib/stackify/error/log.rb +0 -25
  82. data/lib/stackify/error_builder.rb +0 -65
  83. data/lib/stackify/middleware.rb +0 -74
  84. data/lib/stackify/spies/sinatra_activerecord/mysql_adapter.rb +0 -177
  85. data/lib/stackify/spies/sinatra_activerecord/postgresql_adapter.rb +0 -96
  86. data/lib/stackify/spies/sinatra_activerecord/sqlite_adapter.rb +0 -48
  87. data/lib/stackify/stacktrace.rb +0 -19
  88. data/lib/stackify/stacktrace/frame.rb +0 -50
  89. data/lib/stackify/transaction.rb +0 -132
  90. data/lib/stackify/util/lru_cache.rb +0 -49
  91. data/lib/stackify/version.rb +0 -4
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StackifyRubyAPM
4
- class Context
5
- # @api private
6
- class Response
7
- include NaivelyHashable
8
-
9
- def initialize(
10
- status_code,
11
- headers: {},
12
- headers_sent: true,
13
- finished: true
14
- )
15
- @status_code = status_code
16
- headers = self.make_xstackifyId_header(headers)
17
- @headers = headers
18
- @headers_sent = headers_sent
19
- @finished = finished
20
- end
21
-
22
- attr_accessor :status_code, :headers, :headers_sent, :finished
23
-
24
- def make_xstackifyId_header(headers)
25
- if (StackifyRubyAPM.agent.current_transaction &&
26
- StackifyRubyAPM.agent.current_transaction.id)
27
- transaction_id = StackifyRubyAPM.agent.current_transaction.id
28
- clientId = StackifyRubyAPM.agent.config.clientId
29
- deviceId = StackifyRubyAPM.agent.config.deviceId
30
- if clientId && deviceId
31
- headers['X-StackifyID'] = "V1|#{transaction_id}|C#{clientId}|CD#{deviceId}"
32
- end
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,36 +0,0 @@
1
-
2
- module StackifyRubyAPM
3
- class Error
4
- # @api private
5
- class Exception
6
- MOD_SPLIT = '::'.freeze
7
-
8
- def initialize(exception, **attrs)
9
- @message =
10
- "#{exception.class}: #{exception.message}"
11
- @type = exception.class.to_s
12
- @module = format_module exception
13
-
14
- attrs.each do |key, val|
15
- send(:"#{key}=", val)
16
- end
17
- end
18
-
19
- attr_accessor(
20
- :attributes,
21
- :code,
22
- :handled,
23
- :message,
24
- :module,
25
- :stacktrace,
26
- :type
27
- )
28
-
29
- private
30
-
31
- def format_module(exception)
32
- exception.class.to_s.split(MOD_SPLIT)[0...-1].join(MOD_SPLIT)
33
- end
34
- end
35
- end
36
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StackifyRubyAPM
4
- class Error
5
- # @api private
6
- class Log
7
- def initialize(message, attrs = {})
8
- @message = message
9
-
10
- attrs.each do |key, val|
11
- send(:"#{key}=", val)
12
- end
13
- end
14
-
15
- attr_accessor(
16
- :level,
17
- :logger_name,
18
- :message,
19
- :param_message,
20
- :stacktrace
21
- )
22
- end
23
- end
24
- end
25
-
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This module build errors/exceptions and the Frames/stacktraces affected/related to the error
4
- #
5
- module StackifyRubyAPM
6
- # @api private
7
- class ErrorBuilder
8
- def initialize(agent)
9
- @agent = agent
10
- end
11
-
12
- def build_exception(exception, handled: true)
13
- error = Error.new
14
- error.exception = Error::Exception.new(exception, handled: handled)
15
-
16
- if exception.backtrace
17
- add_stacktrace error, :exception, exception.backtrace
18
- end
19
-
20
- add_transaction_id error
21
-
22
- if (transaction = StackifyRubyAPM.current_transaction)
23
- error.context = transaction.context.dup
24
- end
25
-
26
- error
27
- end
28
-
29
- def build_log(message, backtrace: nil, **attrs)
30
- error = Error.new
31
- error.log = Error::Log.new(message, **attrs)
32
-
33
- if backtrace
34
- add_stacktrace error, :log, backtrace
35
- end
36
-
37
- add_transaction_id error
38
-
39
- error
40
- end
41
-
42
- private
43
-
44
- def add_stacktrace(error, kind, backtrace)
45
- stacktrace =
46
- @agent.stacktrace_builder.build(backtrace, :error)
47
- return unless stacktrace
48
-
49
- case kind
50
- when :exception
51
- error.exception.stacktrace = stacktrace
52
- when :log
53
- error.log.stacktrace = stacktrace
54
- end
55
-
56
- error.culprit = stacktrace.frames.first.function
57
- end
58
-
59
- def add_transaction_id(error)
60
- return unless (transaction = StackifyRubyAPM.current_transaction)
61
- error.transaction_id = transaction.id
62
- end
63
- end
64
- end
65
-
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This class monkey patches the Rack Middleware method: e.g., call(env)
4
- # It also handles the submission of transactions to Agent.
5
- #
6
- # If Agent is running, build_transaction(env) is called:
7
- #
8
- # -> BUILDS transaction: /stackify_ruby_apm.rb (#self.transaction)
9
- # -> builds a [Context] from a Rack `env`: /stackify_ruby_apm.rb (#self.build_context)
10
- # -> context may include information about the request,
11
- # response, current user and more
12
- #
13
- # -> SUBMITS transaction: stackify/transaction.rb (#submit)
14
- # -> instrumenter submits transaction
15
- # -> worker will run (if != running), responsible for sending transactions to APM
16
- # for every 10 secs (based on config.flush_interval)
17
- # -> transaction will be stored in queue by Agent
18
- # -> worker will constantly execute transaction sending to APM
19
- #
20
-
21
- module StackifyRubyAPM
22
-
23
- class Middleware
24
- def initialize(app)
25
- @app = app
26
- end
27
-
28
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
29
- # This is where the requests are received, built into a transaction, and then submitted
30
- #
31
- def call(env)
32
- begin
33
-
34
- #if running? && !path_ignored?(env)
35
- if running?
36
- transaction = build_transaction(env)
37
- end
38
-
39
- resp = @app.call env
40
-
41
- submit_transaction(transaction, *resp) if transaction
42
- rescue InternalError
43
- raise # Don't report StackifyRubyAPM errors
44
- rescue ::Exception => e
45
- transaction.submit('500', status: 500) if transaction
46
- raise
47
- ensure
48
- transaction.release if transaction
49
- end
50
-
51
- resp
52
- end
53
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
54
-
55
- def submit_transaction(transaction, status, headers, _body)
56
- result = status.to_i
57
- transaction.submit(result, status: status, headers: headers)
58
- end
59
-
60
- # Start of transaction building with params: name, type, context
61
- #
62
- def build_transaction(env)
63
- StackifyRubyAPM.transaction 'Rack', 'request', context: StackifyRubyAPM.build_context(env)
64
- end
65
-
66
- def running?
67
- StackifyRubyAPM.running?
68
- end
69
-
70
- def config
71
- StackifyRubyAPM.agent.config
72
- end
73
- end
74
- end
@@ -1,177 +0,0 @@
1
- # frozen_string_literal: true
2
- # Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
3
- #
4
- module StackifyRubyAPM
5
- # @api private
6
- module Spies
7
- # @api private
8
- class MysqlAdapterSpy
9
- TYPE = 'db.sinatra_active_record.sql'.freeze
10
- if ActiveRecord::VERSION::MAJOR.to_i >= 5
11
- # rubocop:disable Metrics/MethodLength
12
- def install
13
- ActiveRecord::ConnectionAdapters::MySQL::DatabaseStatements.class_eval do
14
- alias exec_query_without_apm exec_query
15
- alias exec_delete_without_apm exec_delete
16
- alias exec_update_without_apm exec_update
17
-
18
- def exec_update(sql, name = nil, binds = [])
19
- result = nil
20
- unless StackifyRubyAPM.current_transaction
21
- exec_update_without_apm(sql, name, binds)
22
- end
23
-
24
- ctx = Span::Context.new(
25
- CATEGORY: 'Database',
26
- SUBCATEGORY: 'Execute',
27
- COMPONENT_CATEGORY: 'DB Query',
28
- COMPONENT_DETAIL: 'Execute SQL Query',
29
- SQL: sql,
30
- PROVIDER: "mysql"
31
- )
32
- result = exec_update_without_apm(sql, name, binds)
33
- StackifyRubyAPM.span name, TYPE, context: ctx do
34
- return result
35
- end
36
- end
37
-
38
- def exec_delete(sql, name = nil, binds = [])
39
- result = nil
40
- unless StackifyRubyAPM.current_transaction
41
- exec_delete_without_apm(sql, name, binds)
42
- end
43
-
44
- ctx = Span::Context.new(
45
- CATEGORY: 'Database',
46
- SUBCATEGORY: 'Execute',
47
- COMPONENT_CATEGORY: 'DB Query',
48
- COMPONENT_DETAIL: 'Execute SQL Query',
49
- SQL: sql,
50
- PROVIDER: "mysql"
51
- )
52
-
53
- result = exec_delete_without_apm(sql, name, binds)
54
- StackifyRubyAPM.span name, TYPE, context: ctx do
55
- return result
56
- end
57
- end
58
-
59
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
60
- result = nil
61
- unless StackifyRubyAPM.current_transaction
62
- exec_query_without_apm(sql, name, binds)
63
- end
64
-
65
- ctx = Span::Context.new(
66
- CATEGORY: 'Database',
67
- SUBCATEGORY: 'Execute',
68
- COMPONENT_CATEGORY: 'DB Query',
69
- COMPONENT_DETAIL: 'Execute SQL Query',
70
- SQL: sql,
71
- PROVIDER: "mysql"
72
- )
73
-
74
- result = exec_query_without_apm(sql, name, binds)
75
- StackifyRubyAPM.span name, TYPE, context: ctx do
76
- return result
77
- end
78
- end
79
- end
80
- end
81
- # rubocop:enable Metrics/MethodLength
82
- else
83
- # rubocop:disable Metrics/MethodLength
84
- def install
85
- ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do
86
- alias exec_query_without_apm exec_query
87
- alias exec_delete_without_apm exec_delete
88
- alias exec_update_without_apm exec_update
89
- alias exec_insert_without_apm exec_insert
90
-
91
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
92
- result = nil
93
- unless StackifyRubyAPM.current_transaction
94
- exec_insert_without_apm(sql, name, binds, pk = nil, sequence_name = nil)
95
- end
96
- ctx = Span::Context.new(
97
- CATEGORY: 'Database',
98
- SUBCATEGORY: 'Execute',
99
- COMPONENT_CATEGORY: 'DB Query',
100
- COMPONENT_DETAIL: 'Execute SQL Query',
101
- SQL: sql,
102
- PROVIDER: "mysql"
103
- )
104
- result = exec_insert_without_apm(sql, name, binds, pk = nil, sequence_name = nil)
105
- StackifyRubyAPM.span name, TYPE, context: ctx do
106
- return result
107
- end
108
- end
109
-
110
- def exec_update(sql, name, binds)
111
- result = nil
112
- unless StackifyRubyAPM.current_transaction
113
- exec_update_without_apm(sql, name, binds)
114
- end
115
- ctx = Span::Context.new(
116
- CATEGORY: 'Database',
117
- SUBCATEGORY: 'Execute',
118
- COMPONENT_CATEGORY: 'DB Query',
119
- COMPONENT_DETAIL: 'Execute SQL Query',
120
- SQL: sql,
121
- PROVIDER: "mysql"
122
- )
123
- result = exec_update_without_apm(sql, name, binds)
124
- StackifyRubyAPM.span name, TYPE, context: ctx do
125
- return result
126
- end
127
- end
128
-
129
- def exec_delete(sql, name, binds)
130
- result = nil
131
- unless StackifyRubyAPM.current_transaction
132
- exec_delete_without_apm(sql, name, binds)
133
- end
134
- ctx = Span::Context.new(
135
- CATEGORY: 'Database',
136
- SUBCATEGORY: 'Execute',
137
- COMPONENT_CATEGORY: 'DB Query',
138
- COMPONENT_DETAIL: 'Execute SQL Query',
139
- SQL: sql,
140
- PROVIDER: "mysql"
141
- )
142
- result = exec_delete_without_apm(sql, name, binds)
143
- StackifyRubyAPM.span name, TYPE, context: ctx do
144
- return result
145
- end
146
- end
147
-
148
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
149
- result = nil
150
- unless StackifyRubyAPM.current_transaction
151
- exec_query_without_apm(sql, name, binds)
152
- end
153
- ctx = Span::Context.new(
154
- CATEGORY: 'Database',
155
- SUBCATEGORY: 'Execute',
156
- COMPONENT_CATEGORY: 'DB Query',
157
- COMPONENT_DETAIL: 'Execute SQL Query',
158
- SQL: sql,
159
- PROVIDER: "mysql"
160
- )
161
- result = exec_query_without_apm(sql, name, binds)
162
- StackifyRubyAPM.span name, TYPE, context: ctx do
163
- return result
164
- end
165
- end
166
- end
167
- end
168
- # rubocop:enable Metrics/MethodLength
169
- end
170
- end
171
- if ActiveRecord::VERSION::MAJOR.to_i >= 5
172
- register 'ActiveRecord::ConnectionAdapters::MySQL::DatabaseStatements', 'active_record/connection_adapters/mysql/database_statements', MysqlAdapterSpy.new
173
- else
174
- register 'ActiveRecord::ConnectionAdapters::Mysql2Adapter', 'active_record/connection_adapters/mysql2_adapter', MysqlAdapterSpy.new
175
- end
176
- end
177
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
- # Spies for active record when sinatra framework is used and active_record is being extended. (mysql adapter)
3
- #
4
- module StackifyRubyAPM
5
- # @api private
6
- module Spies
7
- # @api private
8
- class PostgresqlAdapterSpy
9
- TYPE = 'db.sinatra_active_record.sql'.freeze
10
-
11
- # rubocop:disable Metrics/MethodLength
12
- def install
13
- ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements.class_eval do
14
- alias exec_query_without_apm exec_query
15
- alias exec_delete_without_apm exec_delete
16
- alias exec_update_without_apm exec_update
17
-
18
- def exec_update(sql, name = nil, binds = [])
19
- result = nil
20
-
21
- unless StackifyRubyAPM.current_transaction
22
- exec_update_without_apm(sql, name, binds)
23
- end
24
-
25
- ctx = Span::Context.new(
26
- CATEGORY: 'Database',
27
- SUBCATEGORY: 'Execute',
28
- COMPONENT_CATEGORY: 'DB Query',
29
- COMPONENT_DETAIL: 'Execute SQL Query',
30
- SQL: sql,
31
- PROVIDER: "mysql"
32
- )
33
-
34
- result = exec_update_without_apm(sql, name, binds)
35
-
36
- StackifyRubyAPM.span name, TYPE, context: ctx do
37
- return result
38
- end
39
- end
40
-
41
- def exec_delete(sql, name = nil, binds = [])
42
- result = nil
43
-
44
- unless StackifyRubyAPM.current_transaction
45
- exec_delete_without_apm(sql, name, binds)
46
- end
47
-
48
- ctx = Span::Context.new(
49
- CATEGORY: 'Database',
50
- SUBCATEGORY: 'Execute',
51
- COMPONENT_CATEGORY: 'DB Query',
52
- COMPONENT_DETAIL: 'Execute SQL Query',
53
- SQL: sql,
54
- PROVIDER: "postgresql"
55
- )
56
-
57
- result = exec_delete_without_apm(sql, name, binds)
58
-
59
- StackifyRubyAPM.span name, TYPE, context: ctx do
60
- return result
61
- end
62
- end
63
-
64
-
65
- def exec_query(sql, name = "SQL", binds = [], prepare: false)
66
- result = nil
67
-
68
- unless StackifyRubyAPM.current_transaction
69
- exec_query_without_apm(sql, name, binds)
70
- end
71
-
72
- ctx = Span::Context.new(
73
- CATEGORY: 'Database',
74
- SUBCATEGORY: 'Execute',
75
- COMPONENT_CATEGORY: 'DB Query',
76
- COMPONENT_DETAIL: 'Execute SQL Query',
77
- SQL: sql,
78
- PROVIDER: "postgresql"
79
- )
80
-
81
- result = exec_query_without_apm(sql, name, binds)
82
-
83
- StackifyRubyAPM.span name, TYPE, context: ctx do
84
- return result
85
- end
86
- end
87
-
88
- end
89
- end
90
- # rubocop:enable Metrics/MethodLength
91
- end
92
-
93
- register 'ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements', 'active_record/connection_adapters/postgresql/database_statements', PostgresqlAdapterSpy.new
94
- end
95
- end
96
-