Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version bump #299

Merged
merged 5 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
activerecord: [60, 61]
ruby: ['3.0', '3.1', '3.2', '3.3']
activerecord: [60, 61, 70]

steps:
- uses: actions/checkout@v2
Expand Down
8 changes: 6 additions & 2 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# frozen_string_literal: true

appraise "ar-60" do
# we are using this version as default in gemspec
# gem "activerecord", "~> 6.0.0"
gem "activerecord", "~> 6.0.0"
end

appraise "ar-61" do
gem "activerecord", "~> 6.1.1"
end

appraise "ar-70" do
# latest version
SamSaffron marked this conversation as resolved.
Show resolved Hide resolved
gem "activerecord", "~> 7.1.2"
end
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
2.1.0 - 2024-08-01

- FEATURE: good_job instrumentation
- PERF: improve performance of histogram
- DEV: use new metric collector pattern so we reuse code between collectors

2.0.8 - 2023-01-20

- FEATURE: attempting to make our first docker release
Expand Down
5 changes: 5 additions & 0 deletions gemfiles/ar_70.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gemspec path: "../"
25 changes: 12 additions & 13 deletions lib/prometheus_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@

require_relative "prometheus_exporter/version"
require "json"
require "thread"

module PrometheusExporter
# per: https://github.com/prometheus/prometheus/wiki/Default-port-allocations
DEFAULT_PORT = 9394
DEFAULT_BIND_ADDRESS = 'localhost'
DEFAULT_PREFIX = 'ruby_'
DEFAULT_BIND_ADDRESS = "localhost"
DEFAULT_PREFIX = "ruby_"
DEFAULT_LABEL = {}
DEFAULT_TIMEOUT = 2
DEFAULT_REALM = 'Prometheus Exporter'
DEFAULT_REALM = "Prometheus Exporter"

class OjCompat
def self.parse(obj)
Oj.compat_load(obj)
end

def self.dump(obj)
Oj.dump(obj, mode: :compat)
end
Expand All @@ -25,7 +25,7 @@ def self.dump(obj)
def self.hostname
@hostname ||=
begin
require 'socket'
require "socket"
Socket.gethostname
rescue => e
STDERR.puts "Unable to lookup hostname #{e}"
Expand All @@ -45,13 +45,12 @@ def self.detect_json_serializer(preferred)
def self.has_oj?
(
@@has_oj ||=
begin
require 'oj'
:true
rescue LoadError
:false
end
) == :true
begin
require "oj"
:T
rescue LoadError
:F
end
) == :T
end

end
53 changes: 34 additions & 19 deletions lib/prometheus_exporter/client.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# frozen_string_literal: true

require 'socket'
require 'thread'
require 'logger'
require "socket"
require "logger"

module PrometheusExporter
class Client
Expand All @@ -25,7 +24,9 @@ def standard_values(value, keys, prometheus_exporter_action = nil)
keys: keys,
value: value
}
values[:prometheus_exporter_action] = prometheus_exporter_action if prometheus_exporter_action
values[
:prometheus_exporter_action
] = prometheus_exporter_action if prometheus_exporter_action
values[:opts] = @opts if @opts
values
end
Expand Down Expand Up @@ -57,8 +58,11 @@ def self.default=(client)
attr_reader :logger

def initialize(
host: ENV.fetch('PROMETHEUS_EXPORTER_HOST', 'localhost'),
port: ENV.fetch('PROMETHEUS_EXPORTER_PORT', PrometheusExporter::DEFAULT_PORT),
host: ENV.fetch("PROMETHEUS_EXPORTER_HOST", "localhost"),
port: ENV.fetch(
"PROMETHEUS_EXPORTER_PORT",
PrometheusExporter::DEFAULT_PORT
),
max_queue_size: nil,
thread_sleep: 0.5,
json_serializer: nil,
Expand Down Expand Up @@ -90,7 +94,8 @@ def initialize(
@mutex = Mutex.new
@thread_sleep = thread_sleep

@json_serializer = json_serializer == :oj ? PrometheusExporter::OjCompat : JSON
@json_serializer =
json_serializer == :oj ? PrometheusExporter::OjCompat : JSON

@custom_labels = custom_labels
end
Expand All @@ -100,7 +105,14 @@ def custom_labels=(custom_labels)
end

def register(type, name, help, opts = nil)
metric = RemoteMetric.new(type: type, name: name, help: help, client: self, opts: opts)
metric =
RemoteMetric.new(
type: type,
name: name,
help: help,
client: self,
opts: opts
)
@metrics << metric
metric
end
Expand Down Expand Up @@ -161,9 +173,7 @@ def stop(wait_timeout_seconds: 0)
@mutex.synchronize do
wait_for_empty_queue_with_timeout(wait_timeout_seconds)
@worker_thread&.kill
while @worker_thread&.alive?
sleep 0.001
end
sleep 0.001 while @worker_thread&.alive?
@worker_thread = nil
close_socket!
end
Expand All @@ -183,12 +193,13 @@ def ensure_worker_thread!
@mutex.synchronize do
return if @worker_thread&.alive?

@worker_thread = Thread.new do
while true
worker_loop
sleep @thread_sleep
@worker_thread =
Thread.new do
while true
worker_loop
sleep @thread_sleep
end
end
end
end
end
rescue ThreadError => e
Expand All @@ -212,7 +223,8 @@ def close_socket!
end

def close_socket_if_old!
if @socket_pid == Process.pid && @socket && @socket_started && ((@socket_started + MAX_SOCKET_AGE) < Time.now.to_f)
if @socket_pid == Process.pid && @socket && @socket_started &&
((@socket_started + MAX_SOCKET_AGE) < Time.now.to_f)
close_socket!
end
end
Expand Down Expand Up @@ -240,7 +252,7 @@ def ensure_socket!
end

nil
rescue
rescue StandardError
@socket = nil
@socket_started = nil
@socket_pid = nil
Expand All @@ -250,7 +262,10 @@ def ensure_socket!
def wait_for_empty_queue_with_timeout(timeout_seconds)
start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
while @queue.length > 0
break if start_time + timeout_seconds < ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
if start_time + timeout_seconds <
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
break
end
sleep(0.05)
end
end
Expand Down
30 changes: 16 additions & 14 deletions lib/prometheus_exporter/instrumentation/sidekiq.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
# frozen_string_literal: true

require 'yaml'
require "yaml"

module PrometheusExporter::Instrumentation
JOB_WRAPPER_CLASS_NAME = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'
DELAYED_CLASS_NAMES = [
'Sidekiq::Extensions::DelayedClass',
'Sidekiq::Extensions::DelayedModel',
'Sidekiq::Extensions::DelayedMailer',
JOB_WRAPPER_CLASS_NAME =
"ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
DELAYED_CLASS_NAMES = %w[
Sidekiq::Extensions::DelayedClass
Sidekiq::Extensions::DelayedModel
Sidekiq::Extensions::DelayedMailer
]

class Sidekiq
def self.death_handler
-> (job, ex) do
->(job, ex) do
job_is_fire_and_forget = job["retry"] == false

worker_class = Object.const_get(job["class"])
Expand Down Expand Up @@ -43,7 +44,8 @@ def self.get_worker_custom_labels(worker_class, msg)
end

def initialize(options = { client: nil })
@client = options.fetch(:client, nil) || PrometheusExporter::Client.default
@client =
options.fetch(:client, nil) || PrometheusExporter::Client.default
end

def call(worker, msg, queue)
Expand Down Expand Up @@ -82,33 +84,33 @@ def self.get_name(class_name, msg)
end

def self.get_job_wrapper_name(msg)
msg['wrapped']
msg["wrapped"]
end

def self.get_delayed_name(msg, class_name)
begin
# fallback to class_name since we're relying on the internal implementation
# of the delayed extensions
# https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/extensions/class_methods.rb
(target, method_name, _args) = YAML.load(msg['args'].first) # rubocop:disable Security/YAMLLoad
target, method_name, _args = YAML.load(msg["args"].first)
if target.class == Class
"#{target.name}##{method_name}"
else
"#{target.class.name}##{method_name}"
end
rescue Psych::DisallowedClass, ArgumentError
parsed = Psych.parse(msg['args'].first)
parsed = Psych.parse(msg["args"].first)
children = parsed.root.children
target = (children[0].value || children[0].tag).sub('!', '')
method_name = (children[1].value || children[1].tag).sub(':', '')
target = (children[0].value || children[0].tag).sub("!", "")
method_name = (children[1].value || children[1].tag).sub(":", "")

if target && method_name
"#{target}##{method_name}"
else
class_name
end
end
rescue
rescue StandardError
class_name
end
end
Expand Down
Loading
Loading