appsignal 4.0.9-java → 4.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbb72b56837756abd3ec4d56855208a44ced0aabac240846bc13e685365ff79e
4
- data.tar.gz: bca5d410c1a2fb91812d2cc63b19a04b0db6cafd18c368a1ece930915bbf0a66
3
+ metadata.gz: b7e56f0388f28620866e5e520d1901d555b77bf4e7f4f60e6c33b1dbcc0ff9a1
4
+ data.tar.gz: d00e77104454c2f28f6439a08293789edff5b8c758c5f349ac12d106953f323e
5
5
  SHA512:
6
- metadata.gz: cd24c802a14d24e154f99493a242c815b85ab96c7049d589d0d1e26eb097d53e91eecc7732e97b8f2ea7415edef21df79bfbcbe1015f7cb5823f292ff30f9939
7
- data.tar.gz: 5e3d0d285a6ebf6a7b8beae27fdaceec6365aa016fa1181e1572f42054fced494771eac6fd62434efb5cd4dae7ee7ca542aa1b6ac5d680139ec7d24dfd238c55
6
+ metadata.gz: 7173193284b3fdf618ab6a91d1670c0c0c01c2dec7de81393004078ced93c55988d89096e54c43b70db3ef85ed3d29804695bdff3a98fd7676fa7effa646a76f
7
+ data.tar.gz: 14b10d0ef16f27cc312d961526d21add84c329b2213e9ff518bf4d1a29e6d44c1cd7b4dbf8e3b3e1f87c19599d61f2b944d93262faaca54c0705985d481ebc88
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 4.1.0
4
+
5
+ _Published on 2024-09-26._
6
+
7
+ ### Added
8
+
9
+ - Add support for heartbeat check-ins.
10
+
11
+ Use the `Appsignal::CheckIn.heartbeat` method to send a single heartbeat check-in event from your application. This can be used, for example, in your application's main loop:
12
+
13
+ ```ruby
14
+ loop do
15
+ Appsignal::CheckIn.heartbeat("job_processor")
16
+ process_job
17
+ end
18
+ ```
19
+
20
+ Heartbeats are deduplicated and sent asynchronously, without blocking the current thread. Regardless of how often the `.heartbeat` method is called, at most one heartbeat with the same identifier will be sent every ten seconds.
21
+
22
+ Pass `continuous: true` as the second argument to send heartbeats continuously during the entire lifetime of the current process. This can be used, for example, after your application has finished its boot process:
23
+
24
+ ```ruby
25
+ def main
26
+ start_app
27
+ Appsignal::CheckIn.heartbeat("my_app", continuous: true)
28
+ end
29
+ ```
30
+
31
+ (minor [7ae7152c](https://github.com/appsignal/appsignal-ruby/commit/7ae7152cddae7c257e9d62d3bf2433cce1f4287d))
32
+ - Include the first backtrace line from error causes to show where each cause originated in the interface. (patch [496b035a](https://github.com/appsignal/appsignal-ruby/commit/496b035a3510dbb6dc47c7c59172f488ec55c986))
33
+
3
34
  ## 4.0.9
4
35
 
5
36
  _Published on 2024-09-17._
@@ -22,13 +22,11 @@ module Appsignal
22
22
  private
23
23
 
24
24
  def event(kind)
25
- {
25
+ Event.cron(
26
26
  :identifier => @identifier,
27
27
  :digest => @digest,
28
- :kind => kind,
29
- :timestamp => Time.now.utc.to_i,
30
- :check_in_type => "cron"
31
- }
28
+ :kind => kind
29
+ )
32
30
  end
33
31
  end
34
32
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module CheckIn
5
+ # @api private
6
+ class Event
7
+ class << self
8
+ def new(check_in_type:, identifier:, digest: nil, kind: nil)
9
+ {
10
+ :identifier => identifier,
11
+ :digest => digest,
12
+ :kind => kind,
13
+ :timestamp => Time.now.utc.to_i,
14
+ :check_in_type => check_in_type
15
+ }.compact
16
+ end
17
+
18
+ def cron(identifier:, digest:, kind:)
19
+ new(
20
+ :check_in_type => "cron",
21
+ :identifier => identifier,
22
+ :digest => digest,
23
+ :kind => kind
24
+ )
25
+ end
26
+
27
+ def heartbeat(identifier:)
28
+ new(
29
+ :check_in_type => "heartbeat",
30
+ :identifier => identifier
31
+ )
32
+ end
33
+
34
+ def redundant?(event, other)
35
+ return false if
36
+ other[:check_in_type] != event[:check_in_type] ||
37
+ other[:identifier] != event[:identifier]
38
+
39
+ return false if event[:check_in_type] == "cron" && (
40
+ other[:digest] != event[:digest] ||
41
+ other[:kind] != event[:kind]
42
+ )
43
+
44
+ return false if
45
+ event[:check_in_type] != "cron" &&
46
+ event[:check_in_type] != "heartbeat"
47
+
48
+ true
49
+ end
50
+
51
+ def describe(events)
52
+ if events.empty?
53
+ # This shouldn't happen.
54
+ "no check-in events"
55
+ elsif events.length > 1
56
+ "#{events.length} check-in events"
57
+ else
58
+ event = events.first
59
+ if event[:check_in_type] == "cron"
60
+ "cron check-in `#{event[:identifier] || "unknown"}` " \
61
+ "#{event[:kind] || "unknown"} event (digest #{event[:digest] || "unknown"})"
62
+ elsif event[:check_in_type] == "heartbeat"
63
+ "heartbeat check-in `#{event[:identifier] || "unknown"}` event"
64
+ else
65
+ "unknown check-in event"
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -29,7 +29,7 @@ module Appsignal
29
29
  def schedule(event)
30
30
  unless Appsignal.active?
31
31
  Appsignal.internal_logger.debug(
32
- "Cannot transmit #{describe([event])}: AppSignal is not active"
32
+ "Cannot transmit #{Event.describe([event])}: AppSignal is not active"
33
33
  )
34
34
  return
35
35
  end
@@ -37,7 +37,7 @@ module Appsignal
37
37
  @mutex.synchronize do
38
38
  if @queue.closed?
39
39
  Appsignal.internal_logger.debug(
40
- "Cannot transmit #{describe([event])}: AppSignal is stopped"
40
+ "Cannot transmit #{Event.describe([event])}: AppSignal is stopped"
41
41
  )
42
42
  return
43
43
  end
@@ -48,7 +48,7 @@ module Appsignal
48
48
  start_waker(INITIAL_DEBOUNCE_SECONDS) if @waker.nil?
49
49
 
50
50
  Appsignal.internal_logger.debug(
51
- "Scheduling #{describe([event])} to be transmitted"
51
+ "Scheduling #{Event.describe([event])} to be transmitted"
52
52
  )
53
53
 
54
54
  # Make sure to start the thread after an event has been added.
@@ -92,7 +92,7 @@ module Appsignal
92
92
  end
93
93
 
94
94
  def transmit(events)
95
- description = describe(events)
95
+ description = Event.describe(events)
96
96
 
97
97
  begin
98
98
  response = CheckIn.transmitter.transmit(events, :format => :ndjson)
@@ -110,42 +110,18 @@ module Appsignal
110
110
  end
111
111
  end
112
112
 
113
- def describe(events)
114
- if events.empty?
115
- # This shouldn't happen.
116
- "no check-in events"
117
- elsif events.length > 1
118
- "#{events.length} check-in events"
119
- else
120
- event = events.first
121
- if event[:check_in_type] == "cron"
122
- "cron check-in `#{event[:identifier] || "unknown"}` " \
123
- "#{event[:kind] || "unknown"} event (digest #{event[:digest] || "unknown"})" \
124
- else
125
- "unknown check-in event"
126
- end
127
- end
128
- end
129
-
130
113
  # Must be called from within a `@mutex.synchronize` block.
131
114
  def add_event(event)
132
115
  # Remove redundant events, keeping the newly added one, which
133
116
  # should be the one with the most recent timestamp.
134
- if event[:check_in_type] == "cron"
135
- # Remove any existing cron check-in event with the same identifier,
136
- # digest and kind as the one we're adding.
137
- @events.reject! do |existing_event|
138
- next unless existing_event[:identifier] == event[:identifier] &&
139
- existing_event[:digest] == event[:digest] &&
140
- existing_event[:kind] == event[:kind] &&
141
- existing_event[:check_in_type] == "cron"
117
+ @events.reject! do |existing_event|
118
+ next unless Event.redundant?(event, existing_event)
142
119
 
143
- Appsignal.internal_logger.debug(
144
- "Replacing previously scheduled #{describe([existing_event])}"
145
- )
120
+ Appsignal.internal_logger.debug(
121
+ "Replacing previously scheduled #{Event.describe([existing_event])}"
122
+ )
146
123
 
147
- true
148
- end
124
+ true
149
125
  end
150
126
 
151
127
  @events << event
@@ -2,10 +2,21 @@
2
2
 
3
3
  module Appsignal
4
4
  module CheckIn
5
+ HEARTBEAT_CONTINUOUS_INTERVAL_SECONDS = 30
5
6
  class << self
7
+ # @api private
8
+ def continuous_heartbeats
9
+ @continuous_heartbeats ||= []
10
+ end
11
+
12
+ # @api private
13
+ def kill_continuous_heartbeats
14
+ continuous_heartbeats.each(&:kill)
15
+ end
16
+
6
17
  # Track cron check-ins.
7
18
  #
8
- # Track the execution of certain processes by sending a cron check-in.
19
+ # Track the execution of scheduled processes by sending a cron check-in.
9
20
  #
10
21
  # To track the duration of a piece of code, pass a block to {.cron}
11
22
  # to report both when the process starts, and when it finishes.
@@ -40,6 +51,37 @@ module Appsignal
40
51
  output
41
52
  end
42
53
 
54
+ # Track heartbeat check-ins.
55
+ #
56
+ # Track the execution of long-lived processes by sending a heartbeat
57
+ # check-in.
58
+ #
59
+ # @example Send a heartbeat check-in
60
+ # Appsignal::CheckIn.heartbeat("main_loop")
61
+ #
62
+ # @param identifier [String] identifier of the heartbeat check-in to report.
63
+ # @param continuous [Boolean] whether the heartbeats should be sent continuously
64
+ # during the lifetime of the process. Defaults to `false`.
65
+ # @yield the block to monitor.
66
+ # @return [void]
67
+ # @since 4.1.0
68
+ # @see https://docs.appsignal.com/check-ins/heartbeat
69
+ def heartbeat(identifier, continuous: false)
70
+ if continuous
71
+ continuous_heartbeats << Thread.new do
72
+ loop do
73
+ heartbeat(identifier)
74
+ sleep HEARTBEAT_CONTINUOUS_INTERVAL_SECONDS
75
+ end
76
+ end
77
+
78
+ return
79
+ end
80
+
81
+ event = Event.heartbeat(:identifier => identifier)
82
+ scheduler.schedule(event)
83
+ end
84
+
43
85
  # @api private
44
86
  def transmitter
45
87
  @transmitter ||= Transmitter.new(
@@ -60,5 +102,6 @@ module Appsignal
60
102
  end
61
103
  end
62
104
 
105
+ require "appsignal/check_in/event"
63
106
  require "appsignal/check_in/scheduler"
64
107
  require "appsignal/check_in/cron"
@@ -627,7 +627,8 @@ module Appsignal
627
627
  causes_sample_data = causes.map do |e|
628
628
  {
629
629
  :name => e.class.name,
630
- :message => cleaned_error_message(e)
630
+ :message => cleaned_error_message(e),
631
+ :first_line => first_formatted_backtrace_line(e)
631
632
  }
632
633
  end
633
634
 
@@ -639,6 +640,36 @@ module Appsignal
639
640
  )
640
641
  end
641
642
 
643
+ BACKTRACE_REGEX =
644
+ %r{(?<gem>[\w-]+ \(.+\) )?(?<path>:?/?\w+?.+?):(?<line>:?\d+)(?<group>:in `(?<method>.+)')?$}.freeze # rubocop:disable Layout/LineLength
645
+
646
+ def first_formatted_backtrace_line(error)
647
+ backtrace = cleaned_backtrace(error.backtrace)
648
+ first_line = backtrace&.first
649
+ return unless first_line
650
+
651
+ captures = BACKTRACE_REGEX.match(first_line)
652
+ return unless captures
653
+
654
+ captures.named_captures
655
+ .merge("original" => first_line)
656
+ .tap do |c|
657
+ config = Appsignal.config
658
+ c.delete("group") # Unused key, only for easier matching
659
+ # Strip of whitespace at the end of the gem name
660
+ c["gem"] = c["gem"]&.strip
661
+ # Strip the app path from the path if present
662
+ root_path = config.root_path
663
+ if c["path"].start_with?(root_path)
664
+ c["path"].delete_prefix!(root_path)
665
+ # Relative paths shouldn't start with a slash
666
+ c["path"].delete_prefix!("/")
667
+ end
668
+ # Add revision for linking to the repository from the UI
669
+ c["revision"] = config[:revision]
670
+ end
671
+ end
672
+
642
673
  def set_sample_data(key, data)
643
674
  return unless key && data
644
675
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "4.0.9"
4
+ VERSION = "4.1.0"
5
5
  end
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: 4.0.9
4
+ version: 4.1.0
5
5
  platform: java
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-09-17 00:00:00.000000000 Z
13
+ date: 2024-09-26 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: logger
@@ -181,6 +181,7 @@ files:
181
181
  - lib/appsignal/capistrano.rb
182
182
  - lib/appsignal/check_in.rb
183
183
  - lib/appsignal/check_in/cron.rb
184
+ - lib/appsignal/check_in/event.rb
184
185
  - lib/appsignal/check_in/scheduler.rb
185
186
  - lib/appsignal/cli.rb
186
187
  - lib/appsignal/cli/demo.rb
@@ -325,7 +326,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
325
326
  - !ruby/object:Gem::Version
326
327
  version: '0'
327
328
  requirements: []
328
- rubygems_version: 3.5.14
329
+ rubygems_version: 3.3.7
329
330
  signing_key:
330
331
  specification_version: 4
331
332
  summary: Logs performance and exception data from your app to appsignal.com