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 +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
|