appsignal 4.10.4-java → 5.0.0-java

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -0
  3. data/Rakefile +195 -4
  4. data/appsignal.gemspec +8 -0
  5. data/build_matrix.yml +12 -0
  6. data/ext/appsignal_extension.c +14 -0
  7. data/lib/appsignal/backends.rb +55 -0
  8. data/lib/appsignal/cli/diagnose.rb +2 -10
  9. data/lib/appsignal/config.rb +377 -13
  10. data/lib/appsignal/demo.rb +12 -10
  11. data/lib/appsignal/event_formatter/action_view/render_formatter.rb +34 -22
  12. data/lib/appsignal/event_formatter/active_job/perform_formatter.rb +35 -0
  13. data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +19 -0
  14. data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +27 -0
  15. data/lib/appsignal/event_formatter/recorded_elsewhere.rb +17 -0
  16. data/lib/appsignal/event_formatter/rom/sql_formatter.rb +24 -0
  17. data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +5 -0
  18. data/lib/appsignal/event_formatter/view_component/render_formatter.rb +21 -10
  19. data/lib/appsignal/event_formatter.rb +78 -0
  20. data/lib/appsignal/extension.rb +4 -0
  21. data/lib/appsignal/helpers/instrumentation.rb +324 -20
  22. data/lib/appsignal/helpers/metrics.rb +3 -24
  23. data/lib/appsignal/hooks/action_cable.rb +26 -8
  24. data/lib/appsignal/hooks/active_job.rb +184 -47
  25. data/lib/appsignal/hooks/at_exit.rb +4 -1
  26. data/lib/appsignal/hooks/excon.rb +20 -0
  27. data/lib/appsignal/hooks/faraday.rb +16 -0
  28. data/lib/appsignal/hooks/http.rb +5 -0
  29. data/lib/appsignal/hooks/resque.rb +1 -1
  30. data/lib/appsignal/hooks/sequel.rb +32 -2
  31. data/lib/appsignal/hooks/shoryuken.rb +3 -3
  32. data/lib/appsignal/hooks/sidekiq.rb +1 -1
  33. data/lib/appsignal/integrations/action_cable.rb +5 -2
  34. data/lib/appsignal/integrations/active_support_notifications.rb +59 -19
  35. data/lib/appsignal/integrations/data_mapper.rb +14 -2
  36. data/lib/appsignal/integrations/delayed_job_plugin.rb +81 -8
  37. data/lib/appsignal/integrations/dry_monitor.rb +39 -15
  38. data/lib/appsignal/integrations/excon/appsignal_middleware.rb +21 -0
  39. data/lib/appsignal/integrations/excon.rb +52 -15
  40. data/lib/appsignal/integrations/faraday.rb +47 -12
  41. data/lib/appsignal/integrations/http.rb +43 -1
  42. data/lib/appsignal/integrations/mongo_ruby_driver.rb +73 -4
  43. data/lib/appsignal/integrations/net_http.rb +31 -2
  44. data/lib/appsignal/integrations/puma.rb +4 -1
  45. data/lib/appsignal/integrations/que.rb +256 -37
  46. data/lib/appsignal/integrations/railtie.rb +4 -1
  47. data/lib/appsignal/integrations/rake.rb +9 -3
  48. data/lib/appsignal/integrations/redis.rb +22 -1
  49. data/lib/appsignal/integrations/redis_client.rb +22 -1
  50. data/lib/appsignal/integrations/resque.rb +81 -11
  51. data/lib/appsignal/integrations/shoryuken.rb +159 -12
  52. data/lib/appsignal/integrations/sidekiq.rb +94 -16
  53. data/lib/appsignal/integrations/webmachine.rb +56 -5
  54. data/lib/appsignal/loaders/padrino.rb +2 -1
  55. data/lib/appsignal/logger/extension_backend.rb +24 -0
  56. data/lib/appsignal/logger/opentelemetry_backend.rb +66 -0
  57. data/lib/appsignal/logger.rb +13 -9
  58. data/lib/appsignal/metrics/extension_backend.rb +47 -0
  59. data/lib/appsignal/metrics/opentelemetry_backend.rb +89 -0
  60. data/lib/appsignal/opentelemetry/attributes.rb +31 -0
  61. data/lib/appsignal/opentelemetry/dependencies.rb +35 -0
  62. data/lib/appsignal/opentelemetry/error_type.rb +37 -0
  63. data/lib/appsignal/opentelemetry/http_client_request.rb +83 -0
  64. data/lib/appsignal/opentelemetry/http_method.rb +59 -0
  65. data/lib/appsignal/opentelemetry/http_response.rb +30 -0
  66. data/lib/appsignal/opentelemetry/http_server_request.rb +79 -0
  67. data/lib/appsignal/opentelemetry/messaging.rb +82 -0
  68. data/lib/appsignal/opentelemetry/proxied_exporter.rb +83 -0
  69. data/lib/appsignal/opentelemetry/rendering.rb +29 -0
  70. data/lib/appsignal/opentelemetry/sql_db_system.rb +89 -0
  71. data/lib/appsignal/opentelemetry.rb +495 -0
  72. data/lib/appsignal/rack/abstract_middleware.rb +66 -4
  73. data/lib/appsignal/rack/body_wrapper.rb +18 -5
  74. data/lib/appsignal/rack/event_handler.rb +44 -4
  75. data/lib/appsignal/rack/grape_middleware.rb +1 -0
  76. data/lib/appsignal/rack/hanami_middleware.rb +2 -1
  77. data/lib/appsignal/rack/instrumentation_middleware.rb +1 -0
  78. data/lib/appsignal/rack/rails_instrumentation.rb +1 -0
  79. data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -0
  80. data/lib/appsignal/rack.rb +68 -12
  81. data/lib/appsignal/sample_data.rb +4 -0
  82. data/lib/appsignal/transaction/base_backend.rb +128 -0
  83. data/lib/appsignal/transaction/extension_backend.rb +229 -0
  84. data/lib/appsignal/transaction/opentelemetry_backend.rb +847 -0
  85. data/lib/appsignal/transaction.rb +714 -164
  86. data/lib/appsignal/utils/request_headers.rb +78 -0
  87. data/lib/appsignal/utils/stdout_and_logger_message.rb +9 -0
  88. data/lib/appsignal/utils.rb +1 -0
  89. data/lib/appsignal/version.rb +1 -1
  90. data/lib/appsignal.rb +10 -0
  91. data/sig/appsignal.rbi +630 -37
  92. data/sig/appsignal.rbs +582 -27
  93. metadata +25 -1
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ class Transaction
5
+ # @!visibility private
6
+ #
7
+ # The transaction backend used in agent mode. Wraps a per-transaction
8
+ # handle on the C extension (`Appsignal::Extension::Transaction`) and
9
+ # forwards every call to it.
10
+ #
11
+ # In agent mode `Appsignal::Backends.transaction` returns this class;
12
+ # `Appsignal::Transaction#initialize` instantiates one and stores it in
13
+ # `@backend`.
14
+ class ExtensionBackend < BaseBackend
15
+ # rubocop:disable Layout/LineLength
16
+ BACKTRACE_REGEX =
17
+ %r{(?<gem>[\w-]+ \(.+\) )?(?<path>:?/?\w+?.+?):(?<line>:?\d+)(?::in `(?<method>.+)')?$}.freeze
18
+ # rubocop:enable Layout/LineLength
19
+
20
+ # @!visibility private
21
+ attr_writer :breadcrumbs
22
+
23
+ # The `opentelemetry_*` keyword arguments (context, scope, kind and
24
+ # relationship) shape the OpenTelemetry span in collector mode. Agent mode
25
+ # has no notion of them, so they are accepted and ignored. They are listed
26
+ # explicitly, rather than swallowed with `**`, so an unexpected keyword
27
+ # still raises, matching the OpenTelemetry backend.
28
+ # rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument
29
+ def initialize(
30
+ transaction_id,
31
+ namespace,
32
+ handle: nil,
33
+ opentelemetry_context: nil,
34
+ opentelemetry_scope: nil,
35
+ opentelemetry_kind: nil,
36
+ opentelemetry_relationship: nil
37
+ )
38
+ super()
39
+ @handle = handle ||
40
+ Appsignal::Extension.start_transaction(transaction_id, namespace, 0) ||
41
+ Appsignal::Extension::MockTransaction.new
42
+ @breadcrumbs = []
43
+ end
44
+ # rubocop:enable Metrics/ParameterLists, Lint/UnusedMethodArgument
45
+
46
+ # Agent mode has no span kind or instrumentation scope;
47
+ # `opentelemetry_kind` and `opentelemetry_scope` are ignored here.
48
+ def start_event(opentelemetry_kind: nil, opentelemetry_scope: nil) # rubocop:disable Lint/UnusedMethodArgument
49
+ @handle.start_event(0)
50
+ end
51
+
52
+ def finish_event(name, title, body, body_format)
53
+ @handle.finish_event(name, title, body, body_format, 0)
54
+ end
55
+
56
+ # Agent mode has no span kind or instrumentation scope;
57
+ # `opentelemetry_kind` and `opentelemetry_scope` are ignored here.
58
+ def record_event(name, title, body, body_format, duration, opentelemetry_kind: nil, opentelemetry_scope: nil, opentelemetry_attributes: nil) # rubocop:disable Lint/UnusedMethodArgument, Metrics/ParameterLists, Layout/LineLength
59
+ @handle.record_event(name, title, body, body_format, duration, 0)
60
+ end
61
+
62
+ def set_action(action)
63
+ @handle.set_action(action)
64
+ end
65
+
66
+ def set_namespace(namespace)
67
+ @handle.set_namespace(namespace)
68
+ end
69
+
70
+ def set_queue_start(start)
71
+ @handle.set_queue_start(start)
72
+ end
73
+
74
+ def set_metadata(key, value)
75
+ @handle.set_metadata(key, value)
76
+ end
77
+
78
+ # Agent mode has no OpenTelemetry spans, so there is nothing to set the
79
+ # attributes on. A transaction event has no attribute equivalent in the
80
+ # agent protocol either, so they are dropped rather than stored somewhere
81
+ # else.
82
+ def set_attributes(_attributes)
83
+ end
84
+
85
+ # The agent has a single params slot, so every params channel maps to one
86
+ # `:params` bucket. The transaction merges the channels into it, and only
87
+ # the `:params` key ever reaches `set_sample_data`.
88
+ PARAMS_MAPPING = {
89
+ :params => :params,
90
+ :request_payload => :params,
91
+ :function_parameters => :params,
92
+ :query_parameters => :params
93
+ }.freeze
94
+
95
+ def params_mapping
96
+ PARAMS_MAPPING
97
+ end
98
+
99
+ PARAMS_OPTIONS = {
100
+ :params => { :filter => :filter_parameters, :send => :send_params }
101
+ }.freeze
102
+
103
+ def params_options
104
+ PARAMS_OPTIONS
105
+ end
106
+
107
+ HEADERS_MAPPING = {
108
+ :request_headers => [
109
+ :environment,
110
+ lambda { |name, value| [Appsignal::Utils::RequestHeaders.rack_name(name), value] }
111
+ ],
112
+ :request_environment => [:environment, nil]
113
+ }.freeze
114
+
115
+ def headers_mapping
116
+ HEADERS_MAPPING
117
+ end
118
+
119
+ HEADERS_ALLOWLIST = { :environment => [:request_headers, false] }.freeze
120
+
121
+ def headers_allowlist
122
+ HEADERS_ALLOWLIST
123
+ end
124
+
125
+ # `data` is a raw Ruby Hash/Array; the C extension wants a `Data` object,
126
+ # so serialize it here (mirrors how `set_error` serializes its backtrace).
127
+ def set_sample_data(key, data)
128
+ @handle.set_sample_data(key, Appsignal::Utils::Data.generate(data))
129
+ end
130
+
131
+ # Buffer breadcrumbs, keeping the last `BREADCRUMB_LIMIT`, and flush them as
132
+ # sample data on completion.
133
+ def add_breadcrumb(breadcrumb)
134
+ @breadcrumbs.push(breadcrumb)
135
+ @breadcrumbs = @breadcrumbs.last(Appsignal::Transaction::BREADCRUMB_LIMIT)
136
+ end
137
+
138
+ # Serializes the backtrace to a C-extension `Data` object and records the
139
+ # error, then flushes the causes as `error_causes` sample data in the
140
+ # agent's first-line shape.
141
+ def set_error(class_name, message, backtrace, causes, root_cause_missing)
142
+ backtrace_data =
143
+ if backtrace
144
+ Appsignal::Utils::Data.generate(backtrace)
145
+ else
146
+ Appsignal::Extension.data_array_new
147
+ end
148
+ @handle.set_error(class_name, message, backtrace_data)
149
+
150
+ set_sample_data("error_causes", error_causes_sample_data(causes, root_cause_missing))
151
+ end
152
+
153
+ def finish
154
+ @handle.finish(0)
155
+ end
156
+
157
+ def complete
158
+ unless @breadcrumbs.empty?
159
+ @handle.set_sample_data("breadcrumbs", Appsignal::Utils::Data.generate(@breadcrumbs))
160
+ end
161
+ @handle.complete
162
+ end
163
+
164
+ # Discarding in agent mode drops the transaction: the extension handle is
165
+ # simply abandoned and never told to complete, so nothing is sent. There
166
+ # is no `ignore_subtrace` concept on the agent path. This mirrors the
167
+ # pre-backend behavior, where `Transaction#complete` returned before
168
+ # touching the handle on a discarded transaction.
169
+ def discard
170
+ end
171
+
172
+ # The extension transaction holds a single error, so the Transaction
173
+ # reports additional errors as duplicate transactions instead.
174
+ def supports_multiple_errors?
175
+ false
176
+ end
177
+
178
+ def duplicate(new_transaction_id)
179
+ self.class.new(
180
+ new_transaction_id, nil, :handle => @handle.duplicate(new_transaction_id)
181
+ ).tap { |backend| backend.breadcrumbs = @breadcrumbs.dup }
182
+ end
183
+
184
+ def to_json # rubocop:disable Lint/ToJSON
185
+ @handle.to_json
186
+ end
187
+
188
+ private
189
+
190
+ # Projects the neutral causes to the agent's first-line shape. A truncated
191
+ # chain marks its last entry as not the root cause.
192
+ def error_causes_sample_data(causes, root_cause_missing)
193
+ sample_data = causes.map do |cause|
194
+ {
195
+ :name => cause[:name],
196
+ :message => cause[:message],
197
+ :first_line => first_formatted_backtrace_line(cause[:backtrace])
198
+ }
199
+ end
200
+ sample_data.last[:is_root_cause] = false if root_cause_missing && sample_data.any?
201
+ sample_data
202
+ end
203
+
204
+ # Parses the first backtrace line into the fields the UI links on (gem,
205
+ # path, line, method), with the path made relative to the app root.
206
+ def first_formatted_backtrace_line(backtrace)
207
+ first_line = backtrace&.first
208
+ return unless first_line
209
+
210
+ captures = BACKTRACE_REGEX.match(first_line)
211
+ return unless captures
212
+
213
+ captures.named_captures
214
+ .merge("original" => first_line)
215
+ .tap do |c|
216
+ config = Appsignal.config
217
+ c["gem"] = c["gem"]&.strip
218
+ root_path = config.root_path
219
+ if c["path"].start_with?(root_path)
220
+ c["path"].delete_prefix!(root_path)
221
+ c["path"].delete_prefix!("/")
222
+ end
223
+ c["revision"] = config[:revision]
224
+ c["line"] = c["line"].to_i
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end