appsignal 5.0.2-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: 62c71c72cf65e314eed6a65860763d7e5875664e630852702ff77af7dd8cf901
4
- data.tar.gz: d28d7b78d58597f8e4ce6f37111110405acd0cdcdc38efc290b6131c57501c86
3
+ metadata.gz: f511b69fdbdeb226062bbaf05e00fe52eb3bd20854c3eeb202edba0b815b1db5
4
+ data.tar.gz: e1261b245afbc044c149b030ba2b4f4656c7d56f56e37223006ed21a68d1a8be
5
5
  SHA512:
6
- metadata.gz: 54f8a659c2178f588165b2c24cdc8d7906d4ce290b4904eb9fb1c8240eda1c314bfa062faa1f718a1996b2b2dbe3c75cdf7d7893f6fce64344c763700cf7451a
7
- data.tar.gz: 1dcb25bc71bed601aac8cf9a499182227f8f8b36c2550a203780b57a80f8758a061fb6a7c7b9427ec39b46828abfb6a6c47225aa602f89516d0d6142aa36878c
6
+ metadata.gz: a18f025051286fc68ff81d27cdc6d4d2fd91b71f79e3d439a9d957e39c98350c9dd46fc7626738c8dfddd13908f13884f9be13155475dedfc8e2844e10f44021
7
+ data.tar.gz: eb8047d550aaa8afd456483ef40db0d188ba492215e582b6f7ddc5c73b6d7e1d957c39bf0183e2b2d5fa1dbd5531a5064d66ac874a1112193feddf9d90f1cd94
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
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
+
3
11
  ## 5.0.2
4
12
 
5
13
  _Published on 2026-10-06._
@@ -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.2"
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.2", 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.2
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