async-tools 0.2.6 → 0.2.8
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/Gemfile.lock +1 -1
- data/lib/async/app/metrics/ruby_runtime_metrics_collector.rb +28 -0
- data/lib/async/app/web_server/health_app.rb +16 -0
- data/lib/async/app/web_server/metrics_app/serializer.rb +23 -0
- data/lib/async/app/web_server/metrics_app/store.rb +17 -0
- data/lib/async/app/web_server/metrics_app.rb +19 -0
- data/lib/async/app/web_server/router.rb +15 -0
- data/lib/async/app/web_server.rb +18 -0
- data/lib/async/app.rb +11 -8
- data/lib/async/bus.rb +3 -3
- data/lib/async/tools/version.rb +1 -1
- metadata +9 -6
- data/lib/async/app/metrics/ruby_runtime_monitor.rb +0 -25
- data/lib/async/app/metrics/serializer.rb +0 -24
- data/lib/async/app/metrics/server.rb +0 -33
- data/lib/async/app/metrics/store.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0906d98b66177300144f1f52414ab79d077bfd75874ba4ebdee3bcac7c12f91b'
|
4
|
+
data.tar.gz: 26b18c911296ac5ea3ac5673417b9fef92e83eae475a5ac7ba79a4037e0a7c91
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b3f48863641bd02bd74d58267c5c3f1c44023d25ca8f06df50daec535913118d5ff121e6d3f38f158d41f365001aa81e80d2353ee0abdf17c2028a9ff4fdcf8
|
7
|
+
data.tar.gz: 86128a933957cf1efae7f67e47361cc02c72201836abf715db193b40cc36300bda9fb9ee7b0fafd9e1555ea57afd9f8a39f87da23fced2786cc3057487551448
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::Metrics::RubyRuntimeMetricsCollector
|
4
|
+
include Async::App::Component
|
5
|
+
|
6
|
+
INTERVAL = 5
|
7
|
+
|
8
|
+
def run
|
9
|
+
Async::Timer.new(INTERVAL, run_on_start: true, on_error: ->(e) { warn(e) }) do
|
10
|
+
bus.publish("metrics.updated", metrics)
|
11
|
+
end
|
12
|
+
info { "Started" }
|
13
|
+
end
|
14
|
+
|
15
|
+
def metrics
|
16
|
+
fibers = ObjectSpace.each_object(Fiber)
|
17
|
+
threads = ObjectSpace.each_object(Thread)
|
18
|
+
ractors = ObjectSpace.each_object(Ractor)
|
19
|
+
{
|
20
|
+
ruby_fibers: { value: fibers.count },
|
21
|
+
ruby_fibers_active: { value: fibers.count(&:alive?) },
|
22
|
+
ruby_threads: { value: threads.count },
|
23
|
+
ruby_threads_active: { value: threads.count(&:alive?) },
|
24
|
+
ruby_ractors: { value: ractors.count },
|
25
|
+
ruby_memory: { value: GetProcessMem.new.bytes.to_s("F"), suffix: "bytes" }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer::HealthApp
|
4
|
+
include Async::App::Component
|
5
|
+
|
6
|
+
PATHS = ["/health", "/health/"].freeze
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@healthy = false
|
10
|
+
|
11
|
+
bus.subscribe("health.updated") { @healthy = _1 }
|
12
|
+
end
|
13
|
+
|
14
|
+
def can_handle?(request) = PATHS.include?(request.path)
|
15
|
+
def call(_) = [@healthy ? 200 : 500]
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer::MetricsApp::Serializer
|
4
|
+
def initialize(prefix:, store:)
|
5
|
+
@prefix = prefix
|
6
|
+
@store = store
|
7
|
+
end
|
8
|
+
|
9
|
+
def serialize
|
10
|
+
@store.flat_map { metric_line(_1) }
|
11
|
+
.compact
|
12
|
+
.join("\n")
|
13
|
+
.then { "#{_1}\n" }
|
14
|
+
end
|
15
|
+
|
16
|
+
def metric_name(value) = "#{@prefix}_#{value[:name]}_#{value[:suffix]}"
|
17
|
+
|
18
|
+
def metric_labels(value) = value[:labels].map { |tag, tag_value| "#{tag}=#{tag_value.to_s.inspect}" }.join(",")
|
19
|
+
|
20
|
+
def metric_line(value)
|
21
|
+
"#{metric_name(value)}{#{metric_labels(value)}} #{value[:value]}" if value.key?(:value)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer::MetricsApp::Store
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def set(name, value:, suffix: "total", **labels)
|
7
|
+
key = [name, labels]
|
8
|
+
metrics[key] ||= { name:, labels:, suffix:, value: }
|
9
|
+
metrics[key].merge!(value:)
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(&) = metrics.values.each(&)
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def metrics = @metrics ||= {}
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer::MetricsApp
|
4
|
+
include Async::App::Component
|
5
|
+
|
6
|
+
PATHS = ["/metrics", "/metrics/"].freeze
|
7
|
+
|
8
|
+
def initialize(metrics_prefix:)
|
9
|
+
store = Store.new
|
10
|
+
@serializer = Serializer.new(prefix: metrics_prefix, store:)
|
11
|
+
|
12
|
+
bus.subscribe("metrics.updated") do |metrics|
|
13
|
+
metrics.each { store.set(_1, **_2) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def can_handle?(request) = PATHS.include?(request.path)
|
18
|
+
def call(*) = [200, {}, @serializer.serialize]
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer::Router
|
4
|
+
extend Async::App::Injector
|
5
|
+
|
6
|
+
def initialize(*apps)
|
7
|
+
@apps = apps
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(request)
|
11
|
+
@apps.each { return Protocol::HTTP::Response[*_1.call(request)] if _1.can_handle?(request) }
|
12
|
+
|
13
|
+
Protocol::HTTP::Response[404, {}, ["Not found"]]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Async::App::WebServer
|
4
|
+
include Async::Logger
|
5
|
+
|
6
|
+
def initialize(metrics_prefix:, port: 8080)
|
7
|
+
@router = Async::App::WebServer::Router.new(
|
8
|
+
MetricsApp.new(metrics_prefix:),
|
9
|
+
HealthApp.new
|
10
|
+
)
|
11
|
+
@endpoint = Async::HTTP::Endpoint.parse("http://0.0.0.0:#{port}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
Async { Async::HTTP::Server.new(@router, @endpoint).run }
|
16
|
+
info { "Started on #{@endpoint.url}" }
|
17
|
+
end
|
18
|
+
end
|
data/lib/async/app.rb
CHANGED
@@ -18,15 +18,19 @@ class Async::App
|
|
18
18
|
init_container!
|
19
19
|
|
20
20
|
start_event_logger!
|
21
|
-
|
21
|
+
start_web_server!
|
22
|
+
|
23
|
+
start_runtime_metrics_collector!
|
24
|
+
|
22
25
|
run!
|
26
|
+
|
23
27
|
info { "Started" }
|
28
|
+
bus.publish("health.updated", true)
|
24
29
|
rescue StandardError => e
|
25
30
|
fatal { e }
|
26
31
|
stop
|
27
32
|
exit(1)
|
28
33
|
end
|
29
|
-
# rubocop:enable Style/GlobalVars
|
30
34
|
|
31
35
|
def container = @container ||= Dry::Container.new
|
32
36
|
def run! = nil
|
@@ -35,9 +39,12 @@ class Async::App
|
|
35
39
|
|
36
40
|
def stop
|
37
41
|
@task&.stop
|
42
|
+
$__ASYNC_APP = nil
|
38
43
|
info { "Stopped" }
|
39
44
|
end
|
40
45
|
|
46
|
+
# rubocop:enable Style/GlobalVars
|
47
|
+
|
41
48
|
private
|
42
49
|
|
43
50
|
def set_traps!
|
@@ -63,11 +70,7 @@ class Async::App
|
|
63
70
|
exit(1)
|
64
71
|
end
|
65
72
|
|
66
|
-
def
|
67
|
-
Metrics::Server.new(prefix: app_name).tap(&:run).tap do |server|
|
68
|
-
bus.subscribe("metrics.updated") { server.update_metrics(_1) }
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
73
|
+
def start_web_server! = WebServer.new(metrics_prefix: app_name).run
|
72
74
|
def start_event_logger! = EventLogger.new.run
|
75
|
+
def start_runtime_metrics_collector! = Async::App::Metrics::RubyRuntimeMetricsCollector.new.run
|
73
76
|
end
|
data/lib/async/bus.rb
CHANGED
@@ -19,12 +19,12 @@ class Async::Bus
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# NON-BLOCKING, runs subscriber in a task
|
22
|
-
def async_subscribe(
|
23
|
-
subscribe(
|
22
|
+
def async_subscribe(pattern, parent: Async::Task.current)
|
23
|
+
subscribe(pattern) do |event|
|
24
24
|
parent.async do
|
25
25
|
yield(event)
|
26
26
|
rescue StandardError => e
|
27
|
-
log_error(
|
27
|
+
log_error(pattern, e)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
data/lib/async/tools/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gleb Sinyavskiy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -64,10 +64,13 @@ files:
|
|
64
64
|
- lib/async/app/component.rb
|
65
65
|
- lib/async/app/event_logger.rb
|
66
66
|
- lib/async/app/injector.rb
|
67
|
-
- lib/async/app/metrics/
|
68
|
-
- lib/async/app/
|
69
|
-
- lib/async/app/
|
70
|
-
- lib/async/app/
|
67
|
+
- lib/async/app/metrics/ruby_runtime_metrics_collector.rb
|
68
|
+
- lib/async/app/web_server.rb
|
69
|
+
- lib/async/app/web_server/health_app.rb
|
70
|
+
- lib/async/app/web_server/metrics_app.rb
|
71
|
+
- lib/async/app/web_server/metrics_app/serializer.rb
|
72
|
+
- lib/async/app/web_server/metrics_app/store.rb
|
73
|
+
- lib/async/app/web_server/router.rb
|
71
74
|
- lib/async/bus.rb
|
72
75
|
- lib/async/cache.rb
|
73
76
|
- lib/async/channel.rb
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Async::App::Metrics::RubyRuntimeMonitor
|
4
|
-
include Async::Logger
|
5
|
-
|
6
|
-
INTERVAL = 2
|
7
|
-
|
8
|
-
def run
|
9
|
-
Async::Timer.new(INTERVAL, run_on_start: true, on_error: ->(e) { warn(e) }) do
|
10
|
-
fibers = ObjectSpace.each_object(Fiber)
|
11
|
-
threads = ObjectSpace.each_object(Thread)
|
12
|
-
ractors = ObjectSpace.each_object(Ractor)
|
13
|
-
|
14
|
-
yield({
|
15
|
-
ruby_fibers: { value: fibers.count },
|
16
|
-
ruby_fibers_active: { value: fibers.count(&:alive?) },
|
17
|
-
ruby_threads: { value: threads.count },
|
18
|
-
ruby_threads_active: { value: threads.count(&:alive?) },
|
19
|
-
ruby_ractors: { value: ractors.count },
|
20
|
-
ruby_memory: { value: GetProcessMem.new.bytes.to_s("F"), suffix: "bytes" }
|
21
|
-
})
|
22
|
-
end
|
23
|
-
info { "Started" }
|
24
|
-
end
|
25
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Async::App::Metrics::Serializer
|
4
|
-
def initialize(prefix:)
|
5
|
-
@prefix = prefix
|
6
|
-
end
|
7
|
-
|
8
|
-
def serialize(metrics)
|
9
|
-
metrics.flat_map { metric_line(_1) }
|
10
|
-
.compact
|
11
|
-
.join("\n")
|
12
|
-
.then { "#{_1}\n" }
|
13
|
-
end
|
14
|
-
|
15
|
-
def metric_name(value) = "#{@prefix}_#{value[:name]}_#{value[:suffix]}"
|
16
|
-
|
17
|
-
def metric_labels(value) = value[:labels].map { |tag, tag_value| "#{tag}=#{tag_value.to_s.inspect}" }.join(",")
|
18
|
-
|
19
|
-
def metric_line(value)
|
20
|
-
labels = metric_labels(value)
|
21
|
-
|
22
|
-
"#{metric_name(value)}{#{labels}} #{value[:value]}" if value.key?(:value)
|
23
|
-
end
|
24
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Async::App::Metrics::Server
|
4
|
-
include Async::Logger
|
5
|
-
|
6
|
-
PATHS = ["/metrics", "/metrics/"].freeze
|
7
|
-
|
8
|
-
def initialize(prefix:, port: 8080)
|
9
|
-
@prefix = prefix
|
10
|
-
@port = port
|
11
|
-
end
|
12
|
-
|
13
|
-
def run
|
14
|
-
Async::App::Metrics::RubyRuntimeMonitor.new.run { update_metrics(_1) }
|
15
|
-
|
16
|
-
endpoint = Async::HTTP::Endpoint.parse("http://0.0.0.0:#{@port}")
|
17
|
-
Async { Async::HTTP::Server.new(self, endpoint).run }
|
18
|
-
info { "Started on #{endpoint.url}" }
|
19
|
-
end
|
20
|
-
|
21
|
-
def call(request)
|
22
|
-
return Protocol::HTTP::Response[404, {}, ["Not found"]] unless PATHS.include?(request.path)
|
23
|
-
|
24
|
-
Protocol::HTTP::Response[200, {}, serializer.serialize(metrics_store)]
|
25
|
-
end
|
26
|
-
|
27
|
-
def update_metrics(metrics) = metrics.each { metrics_store.set(_1, **_2) }
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def metrics_store = @metrics_store ||= Async::App::Metrics::Store.new
|
32
|
-
def serializer = @serializer ||= Async::App::Metrics::Serializer.new(prefix: @prefix)
|
33
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Async::App::Metrics::Store
|
4
|
-
include Enumerable
|
5
|
-
|
6
|
-
def set(name, value:, suffix: "total", **labels)
|
7
|
-
key = [name, labels]
|
8
|
-
counters[key] ||= { name:, labels:, suffix:, value: }
|
9
|
-
counters[key].merge!(value:)
|
10
|
-
end
|
11
|
-
|
12
|
-
def each(&) = counters.values.each(&)
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def counters = @counters ||= {}
|
17
|
-
end
|