stackify-ruby-apm 1.0.1 → 1.1.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.
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
-