appsignal 4.0.9-java → 4.1.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 +31 -0
- data/lib/appsignal/check_in/cron.rb +3 -5
- data/lib/appsignal/check_in/event.rb +72 -0
- data/lib/appsignal/check_in/scheduler.rb +10 -34
- data/lib/appsignal/check_in.rb +44 -1
- data/lib/appsignal/transaction.rb +32 -1
- data/lib/appsignal/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7e56f0388f28620866e5e520d1901d555b77bf4e7f4f60e6c33b1dbcc0ff9a1
|
4
|
+
data.tar.gz: d00e77104454c2f28f6439a08293789edff5b8c758c5f349ac12d106953f323e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
135
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
120
|
+
Appsignal.internal_logger.debug(
|
121
|
+
"Replacing previously scheduled #{Event.describe([existing_event])}"
|
122
|
+
)
|
146
123
|
|
147
|
-
|
148
|
-
end
|
124
|
+
true
|
149
125
|
end
|
150
126
|
|
151
127
|
@events << event
|
data/lib/appsignal/check_in.rb
CHANGED
@@ -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
|
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
|
|
data/lib/appsignal/version.rb
CHANGED
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
|
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-
|
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.
|
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
|