appsignal 4.10.0-java → 5.0.0.rc.1-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 +42 -0
- data/Rakefile +87 -0
- data/appsignal.gemspec +8 -0
- data/build_matrix.yml +14 -1
- data/ext/appsignal_extension.c +14 -0
- data/ext/base.rb +8 -1
- data/lib/appsignal/backends.rb +55 -0
- data/lib/appsignal/config.rb +140 -1
- data/lib/appsignal/demo.rb +1 -1
- 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 +236 -20
- data/lib/appsignal/helpers/metrics.rb +3 -24
- data/lib/appsignal/hooks/action_cable.rb +18 -6
- data/lib/appsignal/hooks/active_job.rb +128 -39
- 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 -14
- data/lib/appsignal/integrations/data_mapper.rb +14 -2
- data/lib/appsignal/integrations/delayed_job_plugin.rb +45 -5
- 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 +224 -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 +57 -10
- data/lib/appsignal/integrations/shoryuken.rb +140 -12
- data/lib/appsignal/integrations/sidekiq.rb +73 -16
- data/lib/appsignal/integrations/webmachine.rb +47 -4
- 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 +45 -0
- data/lib/appsignal/opentelemetry/messaging.rb +82 -0
- data/lib/appsignal/opentelemetry/rendering.rb +29 -0
- data/lib/appsignal/opentelemetry/sql_db_system.rb +89 -0
- data/lib/appsignal/opentelemetry.rb +337 -0
- data/lib/appsignal/rack/abstract_middleware.rb +63 -4
- data/lib/appsignal/rack/body_wrapper.rb +18 -5
- data/lib/appsignal/rack/event_handler.rb +36 -3
- 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 +39 -11
- data/lib/appsignal/sample_data.rb +4 -0
- data/lib/appsignal/transaction/base_backend.rb +107 -0
- data/lib/appsignal/transaction/extension_backend.rb +203 -0
- data/lib/appsignal/transaction/opentelemetry_backend.rb +796 -0
- data/lib/appsignal/transaction.rb +537 -144
- data/lib/appsignal/utils/stdout_and_logger_message.rb +9 -0
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +9 -0
- data/sig/appsignal.rbi +426 -37
- data/sig/appsignal.rbs +386 -27
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77d0e22fb5459d6180f21e04d33c74b67fccf48bd536f9d421f2360803336edd
|
|
4
|
+
data.tar.gz: 4080ad48951daa435a0798951a5f36d98e62fe3b9cc62a0a94b04043e0b02b23
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3dcc132106477e0181cff58f8bbed8ed1db7d6c3cebb81dac5b2cc9fc1a8e8b96cfcaf768bf1bfc4fff9c2529ddf6aa920ac30b9fc8367c7687f477f948ab048
|
|
7
|
+
data.tar.gz: e053bcbe1bb2ec6ad4e09efb80cbd86593ab9342acfe16bda373f596bc96e507920a611cff9802fd46a8219851feaf909a59b2e35b5d9e7bce60c4f2eb1716e2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# AppSignal for Ruby gem Changelog
|
|
2
2
|
|
|
3
|
+
## 5.0.0.rc.1
|
|
4
|
+
|
|
5
|
+
_Published on 2026-08-21._
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Add a `collector_endpoint` configuration option (`APPSIGNAL_COLLECTOR_ENDPOINT` environment variable) that puts the integration in _collector mode_. In collector mode AppSignal reports traces, metrics and logs to an AppSignal Collector, over OTLP/HTTP.
|
|
10
|
+
|
|
11
|
+
Collector mode requires Ruby 3.1 or newer, and the OpenTelemetry gems, which are not installed by default. Add the `appsignal-opentelemetry` gem alongside `appsignal` to install them. When they are missing or too old, AppSignal logs a warning and keeps reporting through its agent.
|
|
12
|
+
|
|
13
|
+
(major [84d83b65](https://github.com/appsignal/appsignal-ruby/commit/84d83b657d65a93859ba73f295216a3b0ee9cab1), [90768626](https://github.com/appsignal/appsignal-ruby/commit/9076862677444c7772e6f8fbe1e3d5a646a4849d))
|
|
14
|
+
- Add configuration options that map to OpenTelemetry resource attributes in collector mode: `service_name`, `filter_attributes`, `filter_function_parameters`, `filter_request_query_parameters`, `filter_request_payload`, `response_headers`, `send_function_parameters`, `send_request_query_parameters` and `send_request_payload`.
|
|
15
|
+
|
|
16
|
+
In collector mode, existing options are passed to the collector as resource attributes as well: `name`, environment, `hostname`, `revision`, `ignore_actions`, `ignore_errors`, `ignore_namespaces`, `request_headers`, `filter_session_data` and `send_session_data`.
|
|
17
|
+
|
|
18
|
+
Setting any of these without `collector_endpoint`, or setting `filter_parameters`, `filter_metadata` or `send_params` with it, logs a warning at startup.
|
|
19
|
+
|
|
20
|
+
(minor [84d83b65](https://github.com/appsignal/appsignal-ruby/commit/84d83b657d65a93859ba73f295216a3b0ee9cab1), [90768626](https://github.com/appsignal/appsignal-ruby/commit/9076862677444c7772e6f8fbe1e3d5a646a4849d))
|
|
21
|
+
|
|
22
|
+
## 4.10.1
|
|
23
|
+
|
|
24
|
+
_Published on 2026-08-20._
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Use the CA certificate configured in `APPSIGNAL_CA_FILE_PATH` to download the
|
|
29
|
+
AppSignal agent when installing this gem.
|
|
30
|
+
|
|
31
|
+
The gem downloads the agent from our servers over HTTPS during installation. It
|
|
32
|
+
always used the CA certificate bundled with the gem to verify that connection,
|
|
33
|
+
whatever the value of the `ca_file_path` configuration option. This meant the
|
|
34
|
+
installation failed behind a proxy that intercepts TLS connections, such as
|
|
35
|
+
Zscaler, even when `APPSIGNAL_CA_FILE_PATH` pointed to a CA bundle that trusts
|
|
36
|
+
the proxy.
|
|
37
|
+
|
|
38
|
+
The bundled CA certificate is still used when `APPSIGNAL_CA_FILE_PATH` is not
|
|
39
|
+
set.
|
|
40
|
+
|
|
41
|
+
Thanks [@Cosmo](https://github.com/Cosmo) for your contribution!
|
|
42
|
+
|
|
43
|
+
(patch [5a126864](https://github.com/appsignal/appsignal-ruby/commit/5a126864314b0c4f244dab653c9548f48f7fd413), [0c49fc51](https://github.com/appsignal/appsignal-ruby/commit/0c49fc51a385d8e70a324792dc7dace3109b8424))
|
|
44
|
+
|
|
3
45
|
## 4.10.0
|
|
4
46
|
|
|
5
47
|
_Published on 2026-08-10._
|
data/Rakefile
CHANGED
|
@@ -72,7 +72,50 @@ GITHUB_ACTION_WORKFLOW_FILE = ".github/workflows/ci.yml"
|
|
|
72
72
|
PRIMARY_JOB_GEMSET = "no_dependencies"
|
|
73
73
|
DEFAULT_RUNS_ON = "ubuntu-latest"
|
|
74
74
|
|
|
75
|
+
COLLECTOR_GEMFILE_PARTIAL = "collector.rb"
|
|
76
|
+
|
|
75
77
|
namespace :build_matrix do
|
|
78
|
+
namespace :gemfiles do
|
|
79
|
+
# Generates a `<base>-collector.gemfile` next to each base gemfile. The
|
|
80
|
+
# variant layers the optional OpenTelemetry gems (`gemfiles/collector.rb`)
|
|
81
|
+
# on top of the base, so collector-mode test runs resolve them while the
|
|
82
|
+
# base gemfiles (and the gemspec) stay OpenTelemetry-free. Regenerate
|
|
83
|
+
# whenever a base gemfile is added or removed.
|
|
84
|
+
task :generate do
|
|
85
|
+
base_gemfiles =
|
|
86
|
+
Dir["gemfiles/*.gemfile"]
|
|
87
|
+
.map { |path| File.basename(path) }
|
|
88
|
+
.reject { |name| name.end_with?("-collector.gemfile") }
|
|
89
|
+
.sort
|
|
90
|
+
|
|
91
|
+
base_gemfiles.each do |base|
|
|
92
|
+
name = base.sub(/\.gemfile\z/, "")
|
|
93
|
+
contents =
|
|
94
|
+
"# DO NOT EDIT\n" \
|
|
95
|
+
"# This is a generated file by the " \
|
|
96
|
+
"`rake build_matrix:gemfiles:generate` task.\n" \
|
|
97
|
+
"# It layers the optional OpenTelemetry gems (gemfiles/" \
|
|
98
|
+
"#{COLLECTOR_GEMFILE_PARTIAL}) on top of #{base}.\n" \
|
|
99
|
+
"\n" \
|
|
100
|
+
"eval_gemfile File.expand_path(#{base.inspect}, __dir__)\n" \
|
|
101
|
+
"eval_gemfile File.expand_path(" \
|
|
102
|
+
"#{COLLECTOR_GEMFILE_PARTIAL.inspect}, __dir__)\n"
|
|
103
|
+
File.write("gemfiles/#{name}-collector.gemfile", contents)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
puts "Generated #{base_gemfiles.count} `-collector` gemfiles."
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
task :validate => :generate do
|
|
110
|
+
output = `git status --porcelain gemfiles`
|
|
111
|
+
if output.include?("-collector.gemfile")
|
|
112
|
+
puts "The `-collector` gemfiles are out of date. The changes were not committed."
|
|
113
|
+
puts "Please run `rake build_matrix:gemfiles:generate` and commit the changes."
|
|
114
|
+
exit 1
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
76
119
|
namespace :github do
|
|
77
120
|
task :generate do
|
|
78
121
|
yaml = YAML.load_file("build_matrix.yml")
|
|
@@ -113,6 +156,21 @@ namespace :build_matrix do
|
|
|
113
156
|
job["steps"] << test_step
|
|
114
157
|
builds[build_matrix_key(ruby["ruby"], :ruby_gem => ruby_gem["gem"])] = job
|
|
115
158
|
end
|
|
159
|
+
|
|
160
|
+
# On collector-capable Rubies, additionally run the gem's
|
|
161
|
+
# `-collector` gemfile (base gems + optional OpenTelemetry gems) so
|
|
162
|
+
# collector-mode specs are exercised. These always depend on the
|
|
163
|
+
# primary job for the Ruby version and run on Ubuntu only.
|
|
164
|
+
next unless collector_ruby?(matrix, ruby_version)
|
|
165
|
+
|
|
166
|
+
collector_gem = "#{ruby_gem["gem"]}-collector"
|
|
167
|
+
collector_job = build_job(ruby_version, :ruby_gem => collector_gem)
|
|
168
|
+
collector_job["env"] = matrix["env"]
|
|
169
|
+
.merge("BUNDLE_GEMFILE" => "gemfiles/#{collector_gem}.gemfile")
|
|
170
|
+
collector_job["needs"] = build_matrix_key(ruby["ruby"])
|
|
171
|
+
collector_job["steps"] << test_step
|
|
172
|
+
builds[build_matrix_key(ruby["ruby"], :ruby_gem => collector_gem)] =
|
|
173
|
+
collector_job
|
|
116
174
|
end
|
|
117
175
|
|
|
118
176
|
# Add build for macOS
|
|
@@ -191,6 +249,14 @@ namespace :build_matrix do
|
|
|
191
249
|
out << "#{bundler_version} #{gemfile_env} ./script/bundler_wrapper install --quiet || { echo 'Bundling failed'; exit 1; }"
|
|
192
250
|
out << "echo 'Running #{gemfile} in #{ruby_version}'"
|
|
193
251
|
out << "#{bundler_version} #{gemfile_env} ./script/bundler_wrapper exec rspec || { echo 'Running specs failed'; exit 1; }"
|
|
252
|
+
|
|
253
|
+
next unless collector_ruby?(matrix, ruby_version)
|
|
254
|
+
|
|
255
|
+
collector_env = "env BUNDLE_GEMFILE=gemfiles/#{gemfile}-collector.gemfile"
|
|
256
|
+
out << "echo 'Bundling #{gemfile}-collector in #{ruby_version}'"
|
|
257
|
+
out << "#{bundler_version} #{collector_env} ./script/bundler_wrapper install --quiet || { echo 'Bundling failed'; exit 1; }"
|
|
258
|
+
out << "echo 'Running #{gemfile}-collector in #{ruby_version}'"
|
|
259
|
+
out << "#{bundler_version} #{collector_env} ./script/bundler_wrapper exec rspec || { echo 'Running specs failed'; exit 1; }"
|
|
194
260
|
end
|
|
195
261
|
# rubocop:enable Layout/LineLength
|
|
196
262
|
out << ""
|
|
@@ -207,6 +273,10 @@ namespace :build_matrix do
|
|
|
207
273
|
end
|
|
208
274
|
end
|
|
209
275
|
|
|
276
|
+
def collector_ruby?(matrix, ruby_version)
|
|
277
|
+
Array(matrix.dig("collector", "ruby")).include?(ruby_version)
|
|
278
|
+
end
|
|
279
|
+
|
|
210
280
|
def gemset_for_ruby(ruby, matrix)
|
|
211
281
|
gems = matrix["gems"]
|
|
212
282
|
if ruby["gems"]
|
|
@@ -288,6 +358,23 @@ namespace :build do
|
|
|
288
358
|
desc "Build all gem versions"
|
|
289
359
|
task :all => ["ruby:gem", "jruby:gem"]
|
|
290
360
|
|
|
361
|
+
desc "Build every package in this repository"
|
|
362
|
+
task :packages => :all do
|
|
363
|
+
# Build the companion gem by delegating to its own Rakefile rather than
|
|
364
|
+
# duplicating its build logic here. mono builds each package directly, so
|
|
365
|
+
# this task is only a convenience for developers building from the root.
|
|
366
|
+
#
|
|
367
|
+
# Clear the Bundler environment first so the sub-build resolves against the
|
|
368
|
+
# package's own Gemfile instead of inheriting this repository's
|
|
369
|
+
# BUNDLE_GEMFILE. Install its dependencies first, then build it.
|
|
370
|
+
Bundler.with_unbundled_env do
|
|
371
|
+
Dir.chdir("packages/opentelemetry") do
|
|
372
|
+
sh "bundle install"
|
|
373
|
+
sh "bundle exec rake build:all"
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
291
378
|
desc "Clean up all gem build artifacts"
|
|
292
379
|
task :clean do
|
|
293
380
|
FileUtils.rm_rf File.expand_path("pkg", __dir__)
|
data/appsignal.gemspec
CHANGED
|
@@ -7,6 +7,7 @@ IGNORED_PATHS = [
|
|
|
7
7
|
".changesets/",
|
|
8
8
|
".github/",
|
|
9
9
|
"gemfiles/",
|
|
10
|
+
"packages/",
|
|
10
11
|
"script/",
|
|
11
12
|
"spec/",
|
|
12
13
|
|
|
@@ -60,6 +61,13 @@ Gem::Specification.new do |gem|
|
|
|
60
61
|
# Needs 2.0+ because we rely on Rack::Events
|
|
61
62
|
gem.add_dependency "rack", ">= 2.0.0"
|
|
62
63
|
|
|
64
|
+
# The OpenTelemetry SDK and OTLP exporters are *optional* and intentionally
|
|
65
|
+
# not declared here: they're only needed in collector mode (Ruby 3.1+), so
|
|
66
|
+
# bundling them would break Ruby 2.7 and burden non-collector users. Apps
|
|
67
|
+
# that enable collector mode add them to their own Gemfile; the minimum
|
|
68
|
+
# versions live in `lib/appsignal/opentelemetry/dependencies.rb` and are
|
|
69
|
+
# enforced at boot by `Appsignal::OpenTelemetry.configure`.
|
|
70
|
+
|
|
63
71
|
gem.add_development_dependency "pry"
|
|
64
72
|
gem.add_development_dependency "rake", ">= 12"
|
|
65
73
|
gem.add_development_dependency "rspec", "~> 3.8"
|
data/build_matrix.yml
CHANGED
|
@@ -2,7 +2,7 @@ github:
|
|
|
2
2
|
name: Ruby gem CI
|
|
3
3
|
"on":
|
|
4
4
|
push:
|
|
5
|
-
branches: ["main", "develop"]
|
|
5
|
+
branches: ["main", "develop", "*-stable"]
|
|
6
6
|
pull_request:
|
|
7
7
|
types: [opened, reopened, synchronize]
|
|
8
8
|
schedule:
|
|
@@ -107,6 +107,18 @@ matrix:
|
|
|
107
107
|
JRUBY_OPTS: ""
|
|
108
108
|
COV: "1"
|
|
109
109
|
|
|
110
|
+
# Ruby versions that additionally run each gem's `-collector` gemfile (the
|
|
111
|
+
# optional OpenTelemetry gems layered on top). Collector mode requires Ruby
|
|
112
|
+
# 3.1+ (the OTel metrics SDK's fork hooks rely on `Process._fork`) and does
|
|
113
|
+
# not support JRuby's forking model, so only these versions are listed.
|
|
114
|
+
collector:
|
|
115
|
+
ruby:
|
|
116
|
+
- "4.0.0"
|
|
117
|
+
- "3.4.1"
|
|
118
|
+
- "3.3.4"
|
|
119
|
+
- "3.2.5"
|
|
120
|
+
- "3.1.6"
|
|
121
|
+
|
|
110
122
|
gemsets: # By default all gems are tested
|
|
111
123
|
none:
|
|
112
124
|
- "no_dependencies"
|
|
@@ -281,6 +293,7 @@ matrix:
|
|
|
281
293
|
- "3.4.1"
|
|
282
294
|
- "3.3.4"
|
|
283
295
|
- "3.2.5"
|
|
296
|
+
- gem: "mongo"
|
|
284
297
|
- gem: "resque-2"
|
|
285
298
|
- gem: "resque-3"
|
|
286
299
|
only:
|
data/ext/appsignal_extension.c
CHANGED
|
@@ -834,10 +834,23 @@ static VALUE add_distribution_value(VALUE self, VALUE key, VALUE value, VALUE ta
|
|
|
834
834
|
return Qnil;
|
|
835
835
|
}
|
|
836
836
|
|
|
837
|
+
// Per-thread running total of object allocations, incremented on every Ruby
|
|
838
|
+
// NEWOBJ event. Thread-local because MRI maps each Ruby thread to its own OS
|
|
839
|
+
// thread, so this attributes allocations to the thread doing the work, which is
|
|
840
|
+
// the thread the transaction runs on. Collector mode reads it through
|
|
841
|
+
// Appsignal::Extension.allocation_count and diffs two snapshots to get the
|
|
842
|
+
// allocations made during a transaction or an event.
|
|
843
|
+
static __thread unsigned long long appsignal_thread_allocation_count = 0;
|
|
844
|
+
|
|
837
845
|
static void track_allocation(rb_event_flag_t flag, VALUE arg1, VALUE arg2, ID arg3, VALUE arg4) {
|
|
846
|
+
appsignal_thread_allocation_count++;
|
|
838
847
|
appsignal_track_allocation();
|
|
839
848
|
}
|
|
840
849
|
|
|
850
|
+
static VALUE allocation_count(VALUE self) {
|
|
851
|
+
return ULL2NUM(appsignal_thread_allocation_count);
|
|
852
|
+
}
|
|
853
|
+
|
|
841
854
|
static VALUE install_allocation_event_hook(VALUE self) {
|
|
842
855
|
// This event hook is only available on Ruby 2.1 and 2.2
|
|
843
856
|
#if defined(RUBY_INTERNAL_EVENT_NEWOBJ)
|
|
@@ -956,6 +969,7 @@ void Init_appsignal_extension(void) {
|
|
|
956
969
|
|
|
957
970
|
// Other helper methods
|
|
958
971
|
rb_define_singleton_method(Extension, "install_allocation_event_hook", install_allocation_event_hook, 0);
|
|
972
|
+
rb_define_singleton_method(Extension, "allocation_count", allocation_count, 0);
|
|
959
973
|
rb_define_singleton_method(Extension, "running_in_container?", running_in_container, 0);
|
|
960
974
|
rb_define_singleton_method(Extension, "set_environment_metadata", set_environment_metadata, 2);
|
|
961
975
|
|
data/ext/base.rb
CHANGED
|
@@ -22,6 +22,13 @@ def ext_path(path)
|
|
|
22
22
|
File.join(EXT_PATH, path)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def ca_file_path
|
|
26
|
+
configured_path = ENV.fetch("APPSIGNAL_CA_FILE_PATH", nil)
|
|
27
|
+
return configured_path if configured_path && !configured_path.strip.empty?
|
|
28
|
+
|
|
29
|
+
CA_CERT_PATH
|
|
30
|
+
end
|
|
31
|
+
|
|
25
32
|
def report
|
|
26
33
|
@report ||=
|
|
27
34
|
begin
|
|
@@ -132,7 +139,7 @@ def download_archive(type)
|
|
|
132
139
|
proxy, _error = http_proxy
|
|
133
140
|
args = [
|
|
134
141
|
download_url,
|
|
135
|
-
{ :ssl_ca_cert =>
|
|
142
|
+
{ :ssl_ca_cert => ca_file_path,
|
|
136
143
|
:proxy => proxy }
|
|
137
144
|
]
|
|
138
145
|
if URI.respond_to?(:open) # rubocop:disable Style/GuardClause
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appsignal/metrics/extension_backend"
|
|
4
|
+
require "appsignal/metrics/opentelemetry_backend"
|
|
5
|
+
require "appsignal/logger/extension_backend"
|
|
6
|
+
require "appsignal/logger/opentelemetry_backend"
|
|
7
|
+
require "appsignal/transaction/base_backend"
|
|
8
|
+
require "appsignal/transaction/extension_backend"
|
|
9
|
+
require "appsignal/transaction/opentelemetry_backend"
|
|
10
|
+
|
|
11
|
+
module Appsignal
|
|
12
|
+
# @!visibility private
|
|
13
|
+
#
|
|
14
|
+
# Looks up the active backend for each AppSignal subsystem. In normal
|
|
15
|
+
# operation, subsystems route through the C-extension (and its agent).
|
|
16
|
+
# When collector mode is configured and the OpenTelemetry SDK has booted
|
|
17
|
+
# successfully, supported subsystems route through OTel instead.
|
|
18
|
+
#
|
|
19
|
+
# Centralizes the mode-check so per-subsystem call sites don't repeat the
|
|
20
|
+
# "if collector? then OTel else Extension" branch. Future subsystems plug
|
|
21
|
+
# in by adding one more lookup method here.
|
|
22
|
+
module Backends
|
|
23
|
+
class << self
|
|
24
|
+
def metrics
|
|
25
|
+
if collector?
|
|
26
|
+
Appsignal::Metrics::OpenTelemetryBackend
|
|
27
|
+
else
|
|
28
|
+
Appsignal::Metrics::ExtensionBackend
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def logger
|
|
33
|
+
if collector?
|
|
34
|
+
Appsignal::Logger::OpenTelemetryBackend
|
|
35
|
+
else
|
|
36
|
+
Appsignal::Logger::ExtensionBackend
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def transaction
|
|
41
|
+
if collector?
|
|
42
|
+
Appsignal::Transaction::OpenTelemetryBackend
|
|
43
|
+
else
|
|
44
|
+
Appsignal::Transaction::ExtensionBackend
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def collector?
|
|
51
|
+
Appsignal.config&.collector_mode? || false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/appsignal/config.rb
CHANGED
|
@@ -91,6 +91,7 @@ module Appsignal
|
|
|
91
91
|
DEFAULT_CONFIG = {
|
|
92
92
|
:activejob_report_errors => "all",
|
|
93
93
|
:ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
|
|
94
|
+
:collector_endpoint => nil,
|
|
94
95
|
:dns_servers => [],
|
|
95
96
|
:enable_allocation_tracking => true,
|
|
96
97
|
:enable_at_exit_hook => "on_error",
|
|
@@ -107,8 +108,12 @@ module Appsignal
|
|
|
107
108
|
:enable_rake_performance_instrumentation => false,
|
|
108
109
|
:endpoint => "https://push.appsignal.com",
|
|
109
110
|
:files_world_accessible => true,
|
|
111
|
+
:filter_attributes => [],
|
|
112
|
+
:filter_function_parameters => [],
|
|
110
113
|
:filter_metadata => [],
|
|
111
114
|
:filter_parameters => [],
|
|
115
|
+
:filter_request_payload => [],
|
|
116
|
+
:filter_request_query_parameters => [],
|
|
112
117
|
:filter_session_data => [],
|
|
113
118
|
:ignore_actions => [],
|
|
114
119
|
:ignore_errors => [],
|
|
@@ -139,9 +144,14 @@ module Appsignal
|
|
|
139
144
|
REQUEST_METHOD REQUEST_PATH SERVER_NAME SERVER_PORT
|
|
140
145
|
SERVER_PROTOCOL
|
|
141
146
|
],
|
|
147
|
+
:response_headers => [],
|
|
142
148
|
:send_environment_metadata => true,
|
|
149
|
+
:send_function_parameters => nil,
|
|
143
150
|
:send_params => true,
|
|
151
|
+
:send_request_payload => nil,
|
|
152
|
+
:send_request_query_parameters => nil,
|
|
144
153
|
:send_session_data => true,
|
|
154
|
+
:service_name => nil,
|
|
145
155
|
:sidekiq_report_errors => "all",
|
|
146
156
|
:default_tags => {}
|
|
147
157
|
}.freeze
|
|
@@ -167,6 +177,7 @@ module Appsignal
|
|
|
167
177
|
:name => "APPSIGNAL_APP_NAME",
|
|
168
178
|
:bind_address => "APPSIGNAL_BIND_ADDRESS",
|
|
169
179
|
:ca_file_path => "APPSIGNAL_CA_FILE_PATH",
|
|
180
|
+
:collector_endpoint => "APPSIGNAL_COLLECTOR_ENDPOINT",
|
|
170
181
|
:enable_at_exit_hook => "APPSIGNAL_ENABLE_AT_EXIT_HOOK",
|
|
171
182
|
:hostname => "APPSIGNAL_HOSTNAME",
|
|
172
183
|
:host_role => "APPSIGNAL_HOST_ROLE",
|
|
@@ -177,6 +188,7 @@ module Appsignal
|
|
|
177
188
|
:logging_endpoint => "APPSIGNAL_LOGGING_ENDPOINT",
|
|
178
189
|
:endpoint => "APPSIGNAL_PUSH_API_ENDPOINT",
|
|
179
190
|
:push_api_key => "APPSIGNAL_PUSH_API_KEY",
|
|
191
|
+
:service_name => "APPSIGNAL_SERVICE_NAME",
|
|
180
192
|
:sidekiq_report_errors => "APPSIGNAL_SIDEKIQ_REPORT_ERRORS",
|
|
181
193
|
:statsd_port => "APPSIGNAL_STATSD_PORT",
|
|
182
194
|
:nginx_port => "APPSIGNAL_NGINX_PORT",
|
|
@@ -221,21 +233,29 @@ module Appsignal
|
|
|
221
233
|
:ownership_set_namespace => "APPSIGNAL_OWNERSHIP_SET_NAMESPACE",
|
|
222
234
|
:running_in_container => "APPSIGNAL_RUNNING_IN_CONTAINER",
|
|
223
235
|
:send_environment_metadata => "APPSIGNAL_SEND_ENVIRONMENT_METADATA",
|
|
236
|
+
:send_function_parameters => "APPSIGNAL_SEND_FUNCTION_PARAMETERS",
|
|
224
237
|
:send_params => "APPSIGNAL_SEND_PARAMS",
|
|
238
|
+
:send_request_payload => "APPSIGNAL_SEND_REQUEST_PAYLOAD",
|
|
239
|
+
:send_request_query_parameters => "APPSIGNAL_SEND_REQUEST_QUERY_PARAMETERS",
|
|
225
240
|
:send_session_data => "APPSIGNAL_SEND_SESSION_DATA"
|
|
226
241
|
}.freeze
|
|
227
242
|
|
|
228
243
|
# @!visibility private
|
|
229
244
|
ARRAY_OPTIONS = {
|
|
230
245
|
:dns_servers => "APPSIGNAL_DNS_SERVERS",
|
|
246
|
+
:filter_attributes => "APPSIGNAL_FILTER_ATTRIBUTES",
|
|
247
|
+
:filter_function_parameters => "APPSIGNAL_FILTER_FUNCTION_PARAMETERS",
|
|
231
248
|
:filter_metadata => "APPSIGNAL_FILTER_METADATA",
|
|
232
249
|
:filter_parameters => "APPSIGNAL_FILTER_PARAMETERS",
|
|
250
|
+
:filter_request_payload => "APPSIGNAL_FILTER_REQUEST_PAYLOAD",
|
|
251
|
+
:filter_request_query_parameters => "APPSIGNAL_FILTER_REQUEST_QUERY_PARAMETERS",
|
|
233
252
|
:filter_session_data => "APPSIGNAL_FILTER_SESSION_DATA",
|
|
234
253
|
:ignore_actions => "APPSIGNAL_IGNORE_ACTIONS",
|
|
235
254
|
:ignore_errors => "APPSIGNAL_IGNORE_ERRORS",
|
|
236
255
|
:ignore_logs => "APPSIGNAL_IGNORE_LOGS",
|
|
237
256
|
:ignore_namespaces => "APPSIGNAL_IGNORE_NAMESPACES",
|
|
238
|
-
:request_headers => "APPSIGNAL_REQUEST_HEADERS"
|
|
257
|
+
:request_headers => "APPSIGNAL_REQUEST_HEADERS",
|
|
258
|
+
:response_headers => "APPSIGNAL_RESPONSE_HEADERS"
|
|
239
259
|
}.freeze
|
|
240
260
|
|
|
241
261
|
# @!visibility private
|
|
@@ -248,6 +268,39 @@ module Appsignal
|
|
|
248
268
|
:default_tags => "APPSIGNAL_DEFAULT_TAGS"
|
|
249
269
|
}.freeze
|
|
250
270
|
|
|
271
|
+
# Collector mode requires Ruby 3.1+. The OpenTelemetry Ruby SDK relies on
|
|
272
|
+
# `Process._fork` (introduced in Ruby 3.1) for its fork hooks, without
|
|
273
|
+
# which background reader threads don't restart in child processes and
|
|
274
|
+
# buffered telemetry is lost after a fork.
|
|
275
|
+
# @!visibility private
|
|
276
|
+
MIN_RUBY_VERSION_FOR_COLLECTOR_MODE = "3.1"
|
|
277
|
+
|
|
278
|
+
# Configuration options that only have an effect when the integration is
|
|
279
|
+
# in collector mode. When the agent is in use, setting any of these emits
|
|
280
|
+
# a warning at startup.
|
|
281
|
+
# @!visibility private
|
|
282
|
+
COLLECTOR_ONLY_OPTIONS = [
|
|
283
|
+
:filter_attributes,
|
|
284
|
+
:filter_function_parameters,
|
|
285
|
+
:filter_request_payload,
|
|
286
|
+
:filter_request_query_parameters,
|
|
287
|
+
:response_headers,
|
|
288
|
+
:send_function_parameters,
|
|
289
|
+
:send_request_payload,
|
|
290
|
+
:send_request_query_parameters,
|
|
291
|
+
:service_name
|
|
292
|
+
].freeze
|
|
293
|
+
|
|
294
|
+
# Existing AppSignal options that only affect the agent's handling of
|
|
295
|
+
# trace data. In collector mode these don't filter anything; users need
|
|
296
|
+
# their collector-mode equivalents (see COLLECTOR_ONLY_OPTIONS).
|
|
297
|
+
# @!visibility private
|
|
298
|
+
AGENT_ONLY_TRACE_OPTIONS = [
|
|
299
|
+
:filter_metadata,
|
|
300
|
+
:filter_parameters,
|
|
301
|
+
:send_params
|
|
302
|
+
].freeze
|
|
303
|
+
|
|
251
304
|
# @!visibility private
|
|
252
305
|
attr_reader :root_path, :env, :config_hash
|
|
253
306
|
|
|
@@ -452,6 +505,57 @@ module Appsignal
|
|
|
452
505
|
valid? && active_for_env?
|
|
453
506
|
end
|
|
454
507
|
|
|
508
|
+
# Check if collector mode is configured.
|
|
509
|
+
#
|
|
510
|
+
# Returns true when a non-empty `collector_endpoint` is set and the
|
|
511
|
+
# running Ruby version is at least {MIN_RUBY_VERSION_FOR_COLLECTOR_MODE}.
|
|
512
|
+
# On older Rubies, `collector_endpoint` is ignored (with a warning) and
|
|
513
|
+
# the AppSignal agent is used instead.
|
|
514
|
+
#
|
|
515
|
+
# This is the *intent* check — it answers "did the user ask for
|
|
516
|
+
# collector mode, and could we honor it?". It does not say whether the
|
|
517
|
+
# OpenTelemetry SDK actually booted. See {#collector_mode?} for that.
|
|
518
|
+
#
|
|
519
|
+
# Memoised: the result is cached on first call so hot paths avoid
|
|
520
|
+
# re-running the string-strip predicate, and so the unsupported-Ruby
|
|
521
|
+
# warning is emitted at most once per `Config` instance.
|
|
522
|
+
#
|
|
523
|
+
# @return [Boolean] True if collector mode is configured.
|
|
524
|
+
def collector_mode_configured?
|
|
525
|
+
return @collector_mode_configured if defined?(@collector_mode_configured)
|
|
526
|
+
|
|
527
|
+
endpoint = config_hash[:collector_endpoint]
|
|
528
|
+
configured = !endpoint.nil? && !endpoint.to_s.strip.empty?
|
|
529
|
+
|
|
530
|
+
if configured && Gem::Version.new(RUBY_VERSION) <
|
|
531
|
+
Gem::Version.new(MIN_RUBY_VERSION_FOR_COLLECTOR_MODE)
|
|
532
|
+
Appsignal::Utils::StdoutAndLoggerMessage.warning(
|
|
533
|
+
"Collector mode requires Ruby #{MIN_RUBY_VERSION_FOR_COLLECTOR_MODE} or higher " \
|
|
534
|
+
"(running Ruby #{RUBY_VERSION}). The `collector_endpoint` option will be " \
|
|
535
|
+
"ignored and the AppSignal agent will be used instead."
|
|
536
|
+
)
|
|
537
|
+
@collector_mode_configured = false
|
|
538
|
+
else
|
|
539
|
+
@collector_mode_configured = configured
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# Check if AppSignal is actively running in collector mode.
|
|
544
|
+
#
|
|
545
|
+
# True only if collector mode is {#collector_mode_configured? configured}
|
|
546
|
+
# *and* `Appsignal::OpenTelemetry.configure` has successfully booted the
|
|
547
|
+
# SDK in this process. Use this for backend dispatch on hot paths
|
|
548
|
+
# (metric and log emits): if the OTel boot failed, callers fall back to
|
|
549
|
+
# the agent backend rather than silently dropping data into no-op
|
|
550
|
+
# providers.
|
|
551
|
+
#
|
|
552
|
+
# @return [Boolean] True if collector mode is configured and started.
|
|
553
|
+
def collector_mode?
|
|
554
|
+
collector_mode_configured? &&
|
|
555
|
+
defined?(Appsignal::OpenTelemetry) &&
|
|
556
|
+
Appsignal::OpenTelemetry.started?
|
|
557
|
+
end
|
|
558
|
+
|
|
455
559
|
# @!visibility private
|
|
456
560
|
def write_to_environment
|
|
457
561
|
ENV["_APPSIGNAL_ACTIVE"] = active?.to_s
|
|
@@ -528,6 +632,30 @@ module Appsignal
|
|
|
528
632
|
else
|
|
529
633
|
@valid = true
|
|
530
634
|
end
|
|
635
|
+
|
|
636
|
+
warn_for_mode_mismatch
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
# Emit warnings when a configuration option is set that has no effect in
|
|
640
|
+
# the current mode (collector vs. agent).
|
|
641
|
+
#
|
|
642
|
+
# Uses {#collector_mode_configured?} (intent) rather than
|
|
643
|
+
# {#collector_mode?} so the warnings fire based on what the user asked
|
|
644
|
+
# for, independent of whether the OpenTelemetry SDK successfully booted.
|
|
645
|
+
# @!visibility private
|
|
646
|
+
def warn_for_mode_mismatch
|
|
647
|
+
if collector_mode_configured?
|
|
648
|
+
warn_user_modified(AGENT_ONLY_TRACE_OPTIONS) do |option|
|
|
649
|
+
"The collector is in use. The '#{option}' configuration option is " \
|
|
650
|
+
"only used by the agent for trace data and will be ignored."
|
|
651
|
+
end
|
|
652
|
+
else
|
|
653
|
+
warn_user_modified(COLLECTOR_ONLY_OPTIONS) do |option|
|
|
654
|
+
"The agent is in use. The '#{option}' configuration option is " \
|
|
655
|
+
"only used by the collector and will be ignored. Set " \
|
|
656
|
+
"'collector_endpoint' to use the collector."
|
|
657
|
+
end
|
|
658
|
+
end
|
|
531
659
|
end
|
|
532
660
|
|
|
533
661
|
# Deep freeze the config object so it cannot be modified during the runtime
|
|
@@ -551,6 +679,17 @@ module Appsignal
|
|
|
551
679
|
|
|
552
680
|
private
|
|
553
681
|
|
|
682
|
+
# Yield a warning for each option in `options` whose effective value
|
|
683
|
+
# differs from the default. Setting an option to its default value is
|
|
684
|
+
# a no-op, so we don't warn about it.
|
|
685
|
+
def warn_user_modified(options)
|
|
686
|
+
options.each do |option|
|
|
687
|
+
next if config_hash[option] == DEFAULT_CONFIG[option]
|
|
688
|
+
|
|
689
|
+
logger.warn(yield(option))
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
554
693
|
def logger
|
|
555
694
|
Appsignal.internal_logger
|
|
556
695
|
end
|
data/lib/appsignal/demo.rb
CHANGED
|
@@ -7,13 +7,27 @@ module Appsignal
|
|
|
7
7
|
class RenderFormatter < Appsignal::EventFormatter
|
|
8
8
|
BLANK = ""
|
|
9
9
|
|
|
10
|
+
def opentelemetry_attributes(_payload)
|
|
11
|
+
Appsignal::OpenTelemetry::Rendering.attributes
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def format(payload)
|
|
11
|
-
|
|
15
|
+
# The title is the template's path made relative to the application's
|
|
16
|
+
# root, so a template rendered outside an application gets no title.
|
|
17
|
+
return unless payload[:identifier] && root_path
|
|
12
18
|
|
|
13
19
|
[payload[:identifier].sub(root_path, BLANK), nil]
|
|
14
20
|
end
|
|
15
21
|
|
|
22
|
+
# The application's root, which a template's path is made relative to.
|
|
23
|
+
#
|
|
24
|
+
# Whether there is an application is decided here, when the event is
|
|
25
|
+
# formatted, rather than when this file is loaded. AppSignal can be
|
|
26
|
+
# required before Rails is, and deciding it at load time would leave
|
|
27
|
+
# every template render in the application without a title.
|
|
16
28
|
def root_path
|
|
29
|
+
return unless defined?(Rails)
|
|
30
|
+
|
|
17
31
|
@root_path ||= "#{Rails.root}/"
|
|
18
32
|
end
|
|
19
33
|
end
|
|
@@ -21,24 +35,22 @@ module Appsignal
|
|
|
21
35
|
end
|
|
22
36
|
end
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)
|
|
44
|
-
end
|
|
38
|
+
Appsignal::EventFormatter.register(
|
|
39
|
+
"render_partial.action_view",
|
|
40
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
41
|
+
)
|
|
42
|
+
Appsignal::EventFormatter.register(
|
|
43
|
+
"render_template.action_view",
|
|
44
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
45
|
+
)
|
|
46
|
+
# Action View reports the template's path for these two as well, so they are
|
|
47
|
+
# titled the same way. A collection reports the partial it rendered for each
|
|
48
|
+
# item, and a layout reports itself.
|
|
49
|
+
Appsignal::EventFormatter.register(
|
|
50
|
+
"render_collection.action_view",
|
|
51
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
52
|
+
)
|
|
53
|
+
Appsignal::EventFormatter.register(
|
|
54
|
+
"render_layout.action_view",
|
|
55
|
+
Appsignal::EventFormatter::ActionView::RenderFormatter
|
|
56
|
+
)
|