puma-plugin-telemetry_too 0.0.1.alpha1

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.
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Plugin
5
+ module TelemetryToo
6
+ module Transforms
7
+ # A pass-through transform - it returns the telemetry Hash it was given
8
+ class PassthroughTransform
9
+ def self.call(telemetry)
10
+ telemetry
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Plugin
5
+ module TelemetryToo
6
+ VERSION = '0.0.1.alpha1'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma'
4
+ require 'puma/plugin'
5
+
6
+ require 'puma/plugin/telemetry_too/version'
7
+ require 'puma/plugin/telemetry_too/data'
8
+ require 'puma/plugin/telemetry_too/targets/datadog_statsd_target'
9
+ require 'puma/plugin/telemetry_too/targets/io_target'
10
+ require 'puma/plugin/telemetry_too/targets/log_target'
11
+ require 'puma/plugin/telemetry_too/config'
12
+
13
+ module Puma
14
+ class Plugin
15
+ # TelemetryToo plugin for puma, supporting:
16
+ #
17
+ # - multiple targets, decide where to push puma telemetry information, i.e. datadog, cloudwatch, logs
18
+ # - filtering, select which metrics are interesting for you, extend when necessery
19
+ #
20
+ module TelemetryToo
21
+ class Error < StandardError; end
22
+
23
+ class << self
24
+ attr_writer :config
25
+
26
+ def config
27
+ @config ||= Config.new
28
+ end
29
+
30
+ def configure
31
+ yield(config)
32
+ end
33
+
34
+ def build(launcher = nil)
35
+ socket_telemetry(puma_telemetry, launcher)
36
+ end
37
+
38
+ private
39
+
40
+ def puma_telemetry
41
+ stats = ::Puma.stats_hash
42
+ data_class = if stats.key?(:workers)
43
+ ClusteredData
44
+ else
45
+ WorkerData
46
+ end
47
+ data_class
48
+ .new(stats)
49
+ .metrics(config.puma_telemetry)
50
+ end
51
+
52
+ def socket_telemetry(telemetry, launcher)
53
+ return telemetry if launcher.nil?
54
+ return telemetry unless config.socket_telemetry?
55
+
56
+ telemetry.merge! SocketData.new(launcher.binder.ios, config.socket_parser)
57
+ .metrics
58
+
59
+ telemetry
60
+ end
61
+ end
62
+
63
+ # Contents of actual Puma Plugin
64
+ #
65
+ module PluginInstanceMethods
66
+ def start(launcher)
67
+ @launcher = launcher
68
+
69
+ unless Puma::Plugin::TelemetryToo.config.enabled?
70
+ log_writer.log 'plugin=telemetry msg="disabled, exiting..."'
71
+ return
72
+ end
73
+
74
+ log_writer.log 'plugin=telemetry msg="enabled, setting up runner..."'
75
+
76
+ in_background do
77
+ sleep Puma::Plugin::TelemetryToo.config.initial_delay
78
+ run!
79
+ end
80
+ end
81
+
82
+ def run!
83
+ loop do
84
+ log_writer.debug 'plugin=telemetry msg="publish"'
85
+
86
+ call(Puma::Plugin::TelemetryToo.build(@launcher))
87
+ rescue Errno::EPIPE
88
+ # Occurs when trying to output to STDOUT while puma is shutting down
89
+ rescue StandardError => e
90
+ log_writer.error "plugin=telemetry err=#{e.class} msg=#{e.message.inspect}"
91
+ ensure
92
+ sleep Puma::Plugin::TelemetryToo.config.frequency
93
+ end
94
+ end
95
+
96
+ def call(telemetry)
97
+ Puma::Plugin::TelemetryToo.config.targets.each do |target|
98
+ target.call(telemetry)
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def log_writer
105
+ if Puma::Const::PUMA_VERSION.to_i < 6
106
+ @launcher.events
107
+ else
108
+ @launcher.log_writer
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ Puma::Plugin.create do
117
+ include Puma::Plugin::TelemetryToo::PluginInstanceMethods
118
+ end
@@ -0,0 +1,60 @@
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
+ # TODO: rewrite this line to please Rubocop
41
+ # rubocop:disable Style/SafeNavigationChainLength
42
+ value&.split('Root=')&.last&.split('-')&.fetch(1)&.to_i(16)
43
+ # rubocop:enable Style/SafeNavigationChainLength
44
+ end
45
+
46
+ def request_start
47
+ @process.clock_gettime(Process::CLOCK_REALTIME)
48
+ end
49
+
50
+ def report_queue_time(queue_time)
51
+ return if queue_time.nil?
52
+
53
+ @statsd.timing('queue.time', queue_time)
54
+
55
+ return unless defined?(Datadog) && Datadog.respond_to?(:tracer)
56
+
57
+ span = Datadog.tracer.active_root_span
58
+ span&.set_tag('request.queue_time', queue_time)
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puma-plugin-telemetry_too
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha1
5
+ platform: ruby
6
+ authors:
7
+ - Leszek Zalewski
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: puma
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "<"
17
+ - !ruby/object:Gem::Version
18
+ version: '8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "<"
24
+ - !ruby/object:Gem::Version
25
+ version: '8'
26
+ description: |
27
+ NOTE: This is a fork of puma-plugin-telemetry, modified to:
28
+
29
+ - Support Puma 7
30
+ - Add LogTarget, with custom formatter: and transform: options
31
+ - Warn about socket telemetry on unsupported platforms
32
+
33
+ Puma plugin which should be able to handle all your metric needs regarding your webserver:
34
+
35
+ - ability to publish basic puma statistics (like queue backlog) to both logs and datadog
36
+ - ability to add custom target whenever you need it
37
+ - ability to monitor puma socket listen queue (!)
38
+ - ability to report requests queue time via custom rack middleware - the time request spent between being accepted by Load Balancer and start of its processing by Puma worker
39
+ email:
40
+ - tnt@babbel.com
41
+ executables: []
42
+ extensions: []
43
+ extra_rdoc_files: []
44
+ files:
45
+ - ".rspec"
46
+ - ".rubocop.yml"
47
+ - ".tool-versions"
48
+ - CHANGELOG.md
49
+ - CODE_OF_CONDUCT.md
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - docs/example-datadog_backlog_size.png
54
+ - docs/example-datadog_queue_time.png
55
+ - docs/examples.md
56
+ - lib/puma/plugin/telemetry_too.rb
57
+ - lib/puma/plugin/telemetry_too/config.rb
58
+ - lib/puma/plugin/telemetry_too/data.rb
59
+ - lib/puma/plugin/telemetry_too/formatters/json_formatter.rb
60
+ - lib/puma/plugin/telemetry_too/formatters/logfmt_formatter.rb
61
+ - lib/puma/plugin/telemetry_too/formatters/passthrough_formatter.rb
62
+ - lib/puma/plugin/telemetry_too/targets/base_formatting_target.rb
63
+ - lib/puma/plugin/telemetry_too/targets/datadog_statsd_target.rb
64
+ - lib/puma/plugin/telemetry_too/targets/io_target.rb
65
+ - lib/puma/plugin/telemetry_too/targets/log_target.rb
66
+ - lib/puma/plugin/telemetry_too/transforms/cloud_watch_transform.rb
67
+ - lib/puma/plugin/telemetry_too/transforms/l2met_transform.rb
68
+ - lib/puma/plugin/telemetry_too/transforms/passthrough_transform.rb
69
+ - lib/puma/plugin/telemetry_too/version.rb
70
+ - lib/rack/request_queue_time_middleware.rb
71
+ homepage: https://github.com/stevenharman/puma-plugin-telemetry_too
72
+ licenses:
73
+ - MIT
74
+ metadata:
75
+ homepage_uri: https://github.com/stevenharman/puma-plugin-telemetry_too
76
+ source_code_uri: https://github.com/stevenharman/puma-plugin-telemetry_too
77
+ changelog_uri: https://github.com/stevenharman/puma-plugin-telemetry_too/blob/main/CHANGELOG.md
78
+ github_repo: ssh://github.com/stevenharman/puma-plugin-telemetry_too
79
+ rubygems_mfa_required: 'true'
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: 3.2.0
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubygems_version: 3.6.9
95
+ specification_version: 4
96
+ summary: Puma plugin, adding ability to publish various metrics to your prefered targets.
97
+ test_files: []