appsignal 5.0.1-java → 5.0.3-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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c0a9fb60c548877c42b28d1699c4aa625067bf6477d5f42c3d668a8fd477c77
4
- data.tar.gz: 36cee0453b823e884447de04c004e02ee6a74981a6c4f2ee82e6b53dcecbf9e0
3
+ metadata.gz: f511b69fdbdeb226062bbaf05e00fe52eb3bd20854c3eeb202edba0b815b1db5
4
+ data.tar.gz: e1261b245afbc044c149b030ba2b4f4656c7d56f56e37223006ed21a68d1a8be
5
5
  SHA512:
6
- metadata.gz: 810417245e4d326a430379896d16c859838cba12b5e8de84c652c45343b8ea016ea4d4776363d4f7696226372e2d4f5f84a4698fe695e95c75263e190ba0aaa0
7
- data.tar.gz: 5fcb6ca575a989b8123f2ba225036520fbe74ff88cbcdec64b3b12eb919aaff35adc3c0c9f10e331be6a7764d4d238c185c2cd7a546d9b3459278d99721531ae
6
+ metadata.gz: a18f025051286fc68ff81d27cdc6d4d2fd91b71f79e3d439a9d957e39c98350c9dd46fc7626738c8dfddd13908f13884f9be13155475dedfc8e2844e10f44021
7
+ data.tar.gz: eb8047d550aaa8afd456483ef40db0d188ba492215e582b6f7ddc5c73b6d7e1d957c39bf0183e2b2d5fa1dbd5531a5064d66ac874a1112193feddf9d90f1cd94
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 5.0.3
4
+
5
+ _Published on 2026-10-08._
6
+
7
+ ### Fixed
8
+
9
+ - In collector mode, report counter metrics correctly from processes forked after AppSignal starts, such as Puma workers when using `preload_app!`. Before this change, applications running several forked processes would lose data points or report incorrect values for them. (patch [4e5a3e78](https://github.com/appsignal/appsignal-ruby/commit/4e5a3e781b4ce2b2620b172a15198a2a59e0ebee))
10
+
11
+ ## 5.0.2
12
+
13
+ _Published on 2026-10-06._
14
+
15
+ ### Changed
16
+
17
+ - Use the revision set by Heroku, Render, Kamal or Scalingo when the application also has a `REVISION` file. Since version 4.6.0, a `REVISION` file took precedence over the `HEROKU_SLUG_COMMIT`, `RENDER_GIT_COMMIT`, `KAMAL_VERSION` and `CONTAINER_VERSION` environment variables, so an application that kept a placeholder `REVISION` file in source control reported that placeholder as its revision. The `revision` config option and the `APP_REVISION` environment variable still take precedence over both.
18
+
19
+ Thanks to @rmm5t for their contribution!
20
+
21
+ (patch [bc14ef9c](https://github.com/appsignal/appsignal-ruby/commit/bc14ef9c6db3de71bf2ca8f23ad088f29294c0ff))
22
+
3
23
  ## 5.0.1
4
24
 
5
25
  _Published on 2026-10-02._
data/README.md CHANGED
@@ -318,7 +318,7 @@ Also see our [SUPPORT.md file](SUPPORT.md).
318
318
  [custom-instrumentation]: https://docs.appsignal.com/ruby/instrumentation.html
319
319
  [front-end-monitoring]: https://docs.appsignal.com/front-end.html
320
320
  [exception-handling]: https://docs.appsignal.com/ruby/instrumentation/exception-handling.html
321
- [tagging]: https://docs.appsignal.com/guides/custom-data/tagging-request.html
321
+ [tagging]: https://docs.appsignal.com/guides/tagging#ruby
322
322
  [host-metrics]: https://docs.appsignal.com/metrics/host.html
323
323
  [custom-metrics]: https://docs.appsignal.com/metrics/custom.html
324
324
 
@@ -177,9 +177,7 @@ module Appsignal
177
177
  }.freeze
178
178
 
179
179
  # Environment variables set by deployment platforms that name the
180
- # revision that is being deployed, in the order the agent reads them.
181
- # The agent detects the revision this way as well, but only for the data
182
- # it sends itself, so the gem has to do it for collector mode.
180
+ # revision that is being deployed, in the order they are read.
183
181
  # @!visibility private
184
182
  PLATFORM_REVISION_ENV_VARS = [
185
183
  "HEROKU_SLUG_COMMIT",
@@ -812,10 +810,10 @@ module Appsignal
812
810
 
813
811
  hash[:enable_at_exit_hook] = "always" if Appsignal::Extension.running_in_container?
814
812
 
815
- # Set the revision from a REVISION file in the project root, which
816
- # helps with Capistrano and Hatchbox.io deployments, or from the
817
- # environment variable the deployment platform sets.
818
- revision = detect_revision_from_file || detect_revision_from_platform
813
+ # Set the revision from the environment variable the deployment
814
+ # platform sets, or from a REVISION file in the project root, which
815
+ # helps with Capistrano and Hatchbox.io deployments.
816
+ revision = detect_revision_from_platform || detect_revision_from_file
819
817
  hash[:revision] = revision if revision
820
818
 
821
819
  hostname = detect_hostname
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Appsignal
6
+ module OpenTelemetry
7
+ # @!visibility private
8
+ #
9
+ # The resource attributes that identify the current process:
10
+ # `service.instance.id` and `process.pid`. The OpenTelemetry providers are
11
+ # built once, so a process forked after that inherits its parent's values
12
+ # until {.attach_fork_hook} refreshes them in the child.
13
+ module ProcessResource
14
+ SERVICE_INSTANCE_ID = "service.instance.id"
15
+ PROCESS_PID = "process.pid"
16
+ REFRESH_FAILED = "Could not refresh the OpenTelemetry resource after fork"
17
+
18
+ class << self
19
+ # A random UUIDv4, generated again when the process ID changes.
20
+ def service_instance_id
21
+ pid = Process.pid
22
+ unless @service_instance_id_pid == pid
23
+ @service_instance_id = SecureRandom.uuid
24
+ @service_instance_id_pid = pid
25
+ end
26
+ @service_instance_id
27
+ end
28
+
29
+ def resource
30
+ ::OpenTelemetry::SDK::Resources::Resource.create(
31
+ SERVICE_INSTANCE_ID => service_instance_id,
32
+ PROCESS_PID => Process.pid
33
+ )
34
+ end
35
+
36
+ def attach_fork_hook
37
+ return if @fork_hook_attached
38
+
39
+ Process.singleton_class.prepend(ForkHook)
40
+ @fork_hook_attached = true
41
+ end
42
+
43
+ def before_fork
44
+ return unless Appsignal::OpenTelemetry.started?
45
+
46
+ providers.transform_values do |provider|
47
+ current = current_resource(provider)
48
+ current && attribute(current, SERVICE_INSTANCE_ID)
49
+ end
50
+ rescue => e
51
+ log_failure("#{e.class}: #{e.message}")
52
+ nil
53
+ end
54
+
55
+ def after_fork(instance_ids_before_fork)
56
+ return unless instance_ids_before_fork
57
+
58
+ update = resource
59
+ providers.each do |name, provider|
60
+ refresh(name, provider, update, instance_ids_before_fork[name])
61
+ end
62
+ rescue => e
63
+ log_failure("#{e.class}: #{e.message}")
64
+ end
65
+
66
+ private
67
+
68
+ def providers
69
+ {
70
+ "tracer" => ::OpenTelemetry.tracer_provider,
71
+ "meter" => ::OpenTelemetry.meter_provider,
72
+ "logger" => ::OpenTelemetry.logger_provider
73
+ }
74
+ end
75
+
76
+ def refresh(name, provider, update, instance_id_before_fork)
77
+ current = current_resource(provider)
78
+ unless current
79
+ log_failure("the #{name} provider's resource could not be read")
80
+ return
81
+ end
82
+ return unless attribute(current, SERVICE_INSTANCE_ID) == instance_id_before_fork
83
+
84
+ refreshed = current.merge(update)
85
+ write_resource(provider, refreshed)
86
+ return if current_resource(provider).equal?(refreshed)
87
+
88
+ log_failure("the #{name} provider did not keep the new resource")
89
+ end
90
+
91
+ # The SDK providers take their resource only in their constructors, and
92
+ # read it from `@resource` when they export.
93
+ def current_resource(provider)
94
+ if provider.respond_to?(:resource)
95
+ resource = provider.resource
96
+ unless provider.respond_to?(:resource=)
97
+ # Without a setter, only `@resource` can be written, and it isn't there.
98
+ return unless provider.instance_variable_defined?(:@resource)
99
+ # The reader returns something else, so writing `@resource` would change nothing.
100
+ return unless provider.instance_variable_get(:@resource).equal?(resource)
101
+ end
102
+ elsif provider.instance_variable_defined?(:@resource)
103
+ resource = provider.instance_variable_get(:@resource)
104
+ end
105
+ # If it's not a resource, it isn't what the provider exports.
106
+ resource if resource.is_a?(::OpenTelemetry::SDK::Resources::Resource)
107
+ end
108
+
109
+ def write_resource(provider, resource)
110
+ if provider.respond_to?(:resource=)
111
+ provider.resource = resource
112
+ else
113
+ provider.instance_variable_set(:@resource, resource)
114
+ end
115
+ end
116
+
117
+ def attribute(resource, key)
118
+ resource.attribute_enumerator.each { |k, v| return v if k == key }
119
+ nil
120
+ end
121
+
122
+ def log_failure(reason)
123
+ Appsignal.internal_logger.warn("#{REFRESH_FAILED}: #{reason}")
124
+ end
125
+ end
126
+
127
+ module ForkHook
128
+ def _fork
129
+ instance_ids = ProcessResource.before_fork
130
+ pid = super
131
+ ProcessResource.after_fork(instance_ids) if pid.zero?
132
+ pid
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -8,6 +8,7 @@ require "appsignal/opentelemetry/http_method"
8
8
  require "appsignal/opentelemetry/http_response"
9
9
  require "appsignal/opentelemetry/http_server_request"
10
10
  require "appsignal/opentelemetry/messaging"
11
+ require "appsignal/opentelemetry/process_resource"
11
12
  require "appsignal/opentelemetry/proxied_exporter"
12
13
  require "appsignal/opentelemetry/rendering"
13
14
  require "appsignal/opentelemetry/sql_db_system"
@@ -59,7 +60,9 @@ module Appsignal
59
60
  # `LoggerProvider` take a `resource:` kwarg that replaces (not
60
61
  # merges), so we do the merge ourselves and use the same merged
61
62
  # resource for the tracer provider to keep all three in sync.
62
- resource = ::OpenTelemetry::SDK::Resources::Resource.default.merge(build_resource(config))
63
+ resource = ProcessResource.resource
64
+ .merge(::OpenTelemetry::SDK::Resources::Resource.default)
65
+ .merge(build_resource(config))
63
66
 
64
67
  span_exporter = build_exporter(
65
68
  ::OpenTelemetry::Exporter::OTLP::Exporter,
@@ -103,6 +106,7 @@ module Appsignal
103
106
  )
104
107
 
105
108
  @started = true
109
+ ProcessResource.attach_fork_hook
106
110
  rescue LoadError => e
107
111
  @started = false
108
112
  Appsignal::Utils::StdoutAndLoggerMessage.error(
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Appsignal
4
4
  # @return [String]
5
- VERSION = "5.0.1"
5
+ VERSION = "5.0.3"
6
6
  end
data/sig/appsignal.rbi CHANGED
@@ -8,7 +8,7 @@
8
8
  module Appsignal
9
9
  extend Appsignal::Helpers::Metrics
10
10
  extend Appsignal::Helpers::Instrumentation
11
- VERSION = T.let("5.0.1", T.untyped)
11
+ VERSION = T.let("5.0.3", T.untyped)
12
12
 
13
13
  class << self
14
14
  # The loaded AppSignal configuration.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 5.0.3
5
5
  platform: java
6
6
  authors:
7
7
  - Robert Beekman
@@ -306,6 +306,7 @@ files:
306
306
  - lib/appsignal/opentelemetry/http_response.rb
307
307
  - lib/appsignal/opentelemetry/http_server_request.rb
308
308
  - lib/appsignal/opentelemetry/messaging.rb
309
+ - lib/appsignal/opentelemetry/process_resource.rb
309
310
  - lib/appsignal/opentelemetry/proxied_exporter.rb
310
311
  - lib/appsignal/opentelemetry/rendering.rb
311
312
  - lib/appsignal/opentelemetry/sql_db_system.rb