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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/Rakefile +195 -4
- data/appsignal.gemspec +8 -0
- data/build_matrix.yml +12 -0
- data/ext/appsignal_extension.c +14 -0
- data/lib/appsignal/backends.rb +55 -0
- data/lib/appsignal/cli/diagnose.rb +2 -10
- data/lib/appsignal/config.rb +377 -13
- data/lib/appsignal/demo.rb +12 -10
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +34 -22
- data/lib/appsignal/event_formatter/active_job/perform_formatter.rb +35 -0
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +19 -0
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +27 -0
- data/lib/appsignal/event_formatter/recorded_elsewhere.rb +17 -0
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +24 -0
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +5 -0
- data/lib/appsignal/event_formatter/view_component/render_formatter.rb +21 -10
- data/lib/appsignal/event_formatter.rb +78 -0
- data/lib/appsignal/extension.rb +4 -0
- data/lib/appsignal/helpers/instrumentation.rb +324 -20
- data/lib/appsignal/helpers/metrics.rb +3 -24
- data/lib/appsignal/hooks/action_cable.rb +26 -8
- data/lib/appsignal/hooks/active_job.rb +184 -47
- data/lib/appsignal/hooks/at_exit.rb +4 -1
- data/lib/appsignal/hooks/excon.rb +20 -0
- data/lib/appsignal/hooks/faraday.rb +16 -0
- data/lib/appsignal/hooks/http.rb +5 -0
- data/lib/appsignal/hooks/resque.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +32 -2
- data/lib/appsignal/hooks/shoryuken.rb +3 -3
- data/lib/appsignal/hooks/sidekiq.rb +1 -1
- data/lib/appsignal/integrations/action_cable.rb +5 -2
- data/lib/appsignal/integrations/active_support_notifications.rb +59 -19
- data/lib/appsignal/integrations/data_mapper.rb +14 -2
- data/lib/appsignal/integrations/delayed_job_plugin.rb +81 -8
- data/lib/appsignal/integrations/dry_monitor.rb +39 -15
- data/lib/appsignal/integrations/excon/appsignal_middleware.rb +21 -0
- data/lib/appsignal/integrations/excon.rb +52 -15
- data/lib/appsignal/integrations/faraday.rb +47 -12
- data/lib/appsignal/integrations/http.rb +43 -1
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +73 -4
- data/lib/appsignal/integrations/net_http.rb +31 -2
- data/lib/appsignal/integrations/puma.rb +4 -1
- data/lib/appsignal/integrations/que.rb +256 -37
- data/lib/appsignal/integrations/railtie.rb +4 -1
- data/lib/appsignal/integrations/rake.rb +9 -3
- data/lib/appsignal/integrations/redis.rb +22 -1
- data/lib/appsignal/integrations/redis_client.rb +22 -1
- data/lib/appsignal/integrations/resque.rb +81 -11
- data/lib/appsignal/integrations/shoryuken.rb +159 -12
- data/lib/appsignal/integrations/sidekiq.rb +94 -16
- data/lib/appsignal/integrations/webmachine.rb +56 -5
- data/lib/appsignal/loaders/padrino.rb +2 -1
- data/lib/appsignal/logger/extension_backend.rb +24 -0
- data/lib/appsignal/logger/opentelemetry_backend.rb +66 -0
- data/lib/appsignal/logger.rb +13 -9
- data/lib/appsignal/metrics/extension_backend.rb +47 -0
- data/lib/appsignal/metrics/opentelemetry_backend.rb +89 -0
- data/lib/appsignal/opentelemetry/attributes.rb +31 -0
- data/lib/appsignal/opentelemetry/dependencies.rb +35 -0
- data/lib/appsignal/opentelemetry/error_type.rb +37 -0
- data/lib/appsignal/opentelemetry/http_client_request.rb +83 -0
- data/lib/appsignal/opentelemetry/http_method.rb +59 -0
- data/lib/appsignal/opentelemetry/http_response.rb +30 -0
- data/lib/appsignal/opentelemetry/http_server_request.rb +79 -0
- data/lib/appsignal/opentelemetry/messaging.rb +82 -0
- data/lib/appsignal/opentelemetry/proxied_exporter.rb +83 -0
- data/lib/appsignal/opentelemetry/rendering.rb +29 -0
- data/lib/appsignal/opentelemetry/sql_db_system.rb +89 -0
- data/lib/appsignal/opentelemetry.rb +495 -0
- data/lib/appsignal/rack/abstract_middleware.rb +66 -4
- data/lib/appsignal/rack/body_wrapper.rb +18 -5
- data/lib/appsignal/rack/event_handler.rb +44 -4
- data/lib/appsignal/rack/grape_middleware.rb +1 -0
- data/lib/appsignal/rack/hanami_middleware.rb +2 -1
- data/lib/appsignal/rack/instrumentation_middleware.rb +1 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +1 -0
- data/lib/appsignal/rack/sinatra_instrumentation.rb +1 -0
- data/lib/appsignal/rack.rb +68 -12
- data/lib/appsignal/sample_data.rb +4 -0
- data/lib/appsignal/transaction/base_backend.rb +128 -0
- data/lib/appsignal/transaction/extension_backend.rb +229 -0
- data/lib/appsignal/transaction/opentelemetry_backend.rb +847 -0
- data/lib/appsignal/transaction.rb +714 -164
- data/lib/appsignal/utils/request_headers.rb +78 -0
- data/lib/appsignal/utils/stdout_and_logger_message.rb +9 -0
- data/lib/appsignal/utils.rb +1 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +10 -0
- data/sig/appsignal.rbi +630 -37
- data/sig/appsignal.rbs +582 -27
- 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
|