puma-plugin-telemetry 1.1.0
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 +7 -0
- data/.github/CODEOWNERS +5 -0
- data/.github/workflows/build.yml +46 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +10 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +76 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +67 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/Rakefile +10 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docs/example-datadog_backlog_size.png +0 -0
- data/docs/example-datadog_queue_time.png +0 -0
- data/docs/examples.md +163 -0
- data/lib/puma/plugin/telemetry/config.rb +113 -0
- data/lib/puma/plugin/telemetry/data.rb +269 -0
- data/lib/puma/plugin/telemetry/targets/datadog_statsd_target.rb +51 -0
- data/lib/puma/plugin/telemetry/targets/io_target.rb +42 -0
- data/lib/puma/plugin/telemetry/version.rb +9 -0
- data/lib/puma/plugin/telemetry.rb +106 -0
- data/lib/rack/request_queue_time_middleware.rb +57 -0
- data/puma-plugin-telemetry.gemspec +34 -0
- metadata +88 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'puma'
|
4
|
+
require 'puma/plugin'
|
5
|
+
|
6
|
+
require 'puma/plugin/telemetry/version'
|
7
|
+
require 'puma/plugin/telemetry/data'
|
8
|
+
require 'puma/plugin/telemetry/targets/datadog_statsd_target'
|
9
|
+
require 'puma/plugin/telemetry/targets/io_target'
|
10
|
+
require 'puma/plugin/telemetry/config'
|
11
|
+
|
12
|
+
module Puma
|
13
|
+
class Plugin
|
14
|
+
# Telemetry plugin for puma, supporting:
|
15
|
+
#
|
16
|
+
# - multiple targets, decide where to push puma telemetry information, i.e. datadog, cloudwatch, logs
|
17
|
+
# - filtering, select which metrics are interesting for you, extend when necessery
|
18
|
+
#
|
19
|
+
module Telemetry
|
20
|
+
class Error < StandardError; end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_writer :config
|
24
|
+
|
25
|
+
def config
|
26
|
+
@config ||= Config.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure
|
30
|
+
yield(config)
|
31
|
+
end
|
32
|
+
|
33
|
+
def build(launcher = nil)
|
34
|
+
socket_telemetry(puma_telemetry, launcher)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def puma_telemetry
|
40
|
+
stats = ::Puma.stats_hash
|
41
|
+
data_class = if stats.key?(:workers)
|
42
|
+
ClusteredData
|
43
|
+
else
|
44
|
+
WorkerData
|
45
|
+
end
|
46
|
+
data_class
|
47
|
+
.new(stats)
|
48
|
+
.metrics(config.puma_telemetry)
|
49
|
+
end
|
50
|
+
|
51
|
+
def socket_telemetry(telemetry, launcher)
|
52
|
+
return telemetry if launcher.nil?
|
53
|
+
return telemetry unless config.socket_telemetry?
|
54
|
+
|
55
|
+
telemetry.merge! SocketData.new(launcher.binder.ios, config.socket_parser)
|
56
|
+
.metrics
|
57
|
+
|
58
|
+
telemetry
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Contents of actual Puma Plugin
|
63
|
+
#
|
64
|
+
module PluginInstanceMethods
|
65
|
+
def start(launcher)
|
66
|
+
unless Puma::Plugin::Telemetry.config.enabled?
|
67
|
+
launcher.events.log 'plugin=telemetry msg="disabled, exiting..."'
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
@launcher = launcher
|
72
|
+
@launcher.events.log 'plugin=telemetry msg="enabled, setting up runner..."'
|
73
|
+
|
74
|
+
in_background do
|
75
|
+
sleep Puma::Plugin::Telemetry.config.initial_delay
|
76
|
+
run!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def run!
|
81
|
+
loop do
|
82
|
+
@launcher.events.debug 'plugin=telemetry msg="publish"'
|
83
|
+
|
84
|
+
call(Puma::Plugin::Telemetry.build(@launcher))
|
85
|
+
rescue Errno::EPIPE
|
86
|
+
# Occurs when trying to output to STDOUT while puma is shutting down
|
87
|
+
rescue StandardError => e
|
88
|
+
@launcher.events.error "plugin=telemetry err=#{e.class} msg=#{e.message.inspect}"
|
89
|
+
ensure
|
90
|
+
sleep Puma::Plugin::Telemetry.config.frequency
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def call(telemetry)
|
95
|
+
Puma::Plugin::Telemetry.config.targets.each do |target|
|
96
|
+
target.call(telemetry)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
Puma::Plugin.create do
|
105
|
+
include Puma::Plugin::Telemetry::PluginInstanceMethods
|
106
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Measures the queue time (= time between receiving the request in downstream
|
4
|
+
# load balancer and starting request in ruby process)
|
5
|
+
class RequestQueueTimeMiddleware
|
6
|
+
ENV_KEY = 'rack.request_queue_time'
|
7
|
+
|
8
|
+
def initialize(app, statsd:, process: Process)
|
9
|
+
@app = app
|
10
|
+
@statsd = statsd
|
11
|
+
@process = process
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
queue_time = measure_queue_time(env)
|
16
|
+
|
17
|
+
report_queue_time(queue_time)
|
18
|
+
|
19
|
+
env[ENV_KEY] = queue_time
|
20
|
+
|
21
|
+
@app.call(env)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def measure_queue_time(env)
|
27
|
+
start_time = queue_start(env)
|
28
|
+
|
29
|
+
return unless start_time
|
30
|
+
|
31
|
+
queue_time = request_start.to_f - start_time.to_f
|
32
|
+
|
33
|
+
queue_time unless queue_time.negative?
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the content of the x-amzn-trace-id header, the epoch time in seconds.
|
37
|
+
# see also: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-request-tracing.html
|
38
|
+
def queue_start(env)
|
39
|
+
value = env['HTTP_X_AMZN_TRACE_ID']
|
40
|
+
value&.split('Root=')&.last&.split('-')&.fetch(1)&.to_i(16)
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_start
|
44
|
+
@process.clock_gettime(Process::CLOCK_REALTIME)
|
45
|
+
end
|
46
|
+
|
47
|
+
def report_queue_time(queue_time)
|
48
|
+
return if queue_time.nil?
|
49
|
+
|
50
|
+
@statsd.timing('queue.time', queue_time)
|
51
|
+
|
52
|
+
return unless defined?(Datadog) && Datadog.respond_to?(:tracer)
|
53
|
+
|
54
|
+
span = Datadog.tracer.active_root_span
|
55
|
+
span&.set_tag('request.queue_time', queue_time)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/puma/plugin/telemetry/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'puma-plugin-telemetry'
|
7
|
+
spec.version = Puma::Plugin::Telemetry::VERSION
|
8
|
+
spec.authors = ['Leszek Zalewski']
|
9
|
+
spec.email = ['tnt@babbel.com']
|
10
|
+
|
11
|
+
spec.license = 'MIT'
|
12
|
+
|
13
|
+
spec.summary = 'Puma plugin, adding ability to publish various metrics to your prefered targets.'
|
14
|
+
spec.homepage = 'https://github.com/babbel/puma-plugin-telemetry'
|
15
|
+
|
16
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
20
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
21
|
+
spec.metadata['github_repo'] = 'ssh://github.com/babbel/puma-plugin-telemetry'
|
22
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
27
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
28
|
+
end
|
29
|
+
spec.bindir = 'exe'
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ['lib']
|
32
|
+
|
33
|
+
spec.add_dependency 'puma', '>= 5.0'
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puma-plugin-telemetry
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leszek Zalewski
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: puma
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- tnt@babbel.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".github/CODEOWNERS"
|
35
|
+
- ".github/workflows/build.yml"
|
36
|
+
- ".gitignore"
|
37
|
+
- ".rspec"
|
38
|
+
- ".rubocop.yml"
|
39
|
+
- ".tool-versions"
|
40
|
+
- CHANGELOG.md
|
41
|
+
- CODE_OF_CONDUCT.md
|
42
|
+
- Gemfile
|
43
|
+
- Gemfile.lock
|
44
|
+
- LICENSE.txt
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- bin/console
|
48
|
+
- bin/setup
|
49
|
+
- docs/example-datadog_backlog_size.png
|
50
|
+
- docs/example-datadog_queue_time.png
|
51
|
+
- docs/examples.md
|
52
|
+
- lib/puma/plugin/telemetry.rb
|
53
|
+
- lib/puma/plugin/telemetry/config.rb
|
54
|
+
- lib/puma/plugin/telemetry/data.rb
|
55
|
+
- lib/puma/plugin/telemetry/targets/datadog_statsd_target.rb
|
56
|
+
- lib/puma/plugin/telemetry/targets/io_target.rb
|
57
|
+
- lib/puma/plugin/telemetry/version.rb
|
58
|
+
- lib/rack/request_queue_time_middleware.rb
|
59
|
+
- puma-plugin-telemetry.gemspec
|
60
|
+
homepage: https://github.com/babbel/puma-plugin-telemetry
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata:
|
64
|
+
homepage_uri: https://github.com/babbel/puma-plugin-telemetry
|
65
|
+
source_code_uri: https://github.com/babbel/puma-plugin-telemetry
|
66
|
+
changelog_uri: https://github.com/babbel/puma-plugin-telemetry/blob/main/CHANGELOG.md
|
67
|
+
github_repo: ssh://github.com/babbel/puma-plugin-telemetry
|
68
|
+
rubygems_mfa_required: 'true'
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.6.0
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubygems_version: 3.2.27
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: Puma plugin, adding ability to publish various metrics to your prefered targets.
|
88
|
+
test_files: []
|