class Rollout::Logging::Logger

Constants

CONTEXT_THREAD_KEY
WITHOUT_THREAD_KEY

Public Class Methods

new(storage: nil, history_length: 50, global: false) click to toggle source
# File lib/rollout/logging.rb, line 55
def initialize(storage: nil, history_length: 50, global: false)
  @history_length = history_length
  @storage = storage
  @global = global
end

Public Instance Methods

current_context() click to toggle source
# File lib/rollout/logging.rb, line 168
def current_context
  Thread.current[CONTEXT_THREAD_KEY] || {}
end
delete(feature_name) click to toggle source
# File lib/rollout/logging.rb, line 88
def delete(feature_name)
  storage_key = events_storage_key(feature_name)
  @storage.del(storage_key)
end
events(feature_name) click to toggle source
# File lib/rollout/logging.rb, line 73
def events(feature_name)
  storage_key = events_storage_key(feature_name)
  @storage
    .zrange(storage_key, 0, -1, with_scores: true)
    .map { |v| Event.from_raw(*v) }
    .reverse
end
global_events() click to toggle source
# File lib/rollout/logging.rb, line 81
def global_events
  @storage
    .zrange(global_events_storage_key, 0, -1, with_scores: true)
    .map { |v| Event.from_raw(*v) }
    .reverse
end
last_event(feature_name) click to toggle source
# File lib/rollout/logging.rb, line 67
def last_event(feature_name)
  storage_key = events_storage_key(feature_name)
  value = @storage.zrange(storage_key, 0, 0, with_scores: true).first
  Event.from_raw(*value) if value
end
log(event, *args) click to toggle source
# File lib/rollout/logging.rb, line 137
def log(event, *args)
  return unless logging_enabled?

  unless respond_to?(event)
    raise ArgumentError, "Invalid log event: #{event}"
  end

  expected_arity = method(event).arity
  unless args.count == expected_arity
    raise(
      ArgumentError,
      "Invalid number of arguments for event '#{event}': expected #{expected_arity} but got #{args.count}",
    )
  end

  public_send(event, *args)
end
logging_enabled?() click to toggle source
# File lib/rollout/logging.rb, line 179
def logging_enabled?
  !Thread.current[WITHOUT_THREAD_KEY]
end
update(before, after) click to toggle source
# File lib/rollout/logging.rb, line 93
def update(before, after)
  before_hash = before.to_hash
  before_hash.delete(:data).each do |k, v|
    before_hash["data.#{k}"] = v
  end
  after_hash = after.to_hash
  after_hash.delete(:data).each do |k, v|
    after_hash["data.#{k}"] = v
  end

  keys = before_hash.keys | after_hash.keys
  change = { before: {}, after: {} }
  changed_count = 0

  keys.each do |key|
    next if before_hash[key] == after_hash[key]

    change[:before][key] = before_hash[key]
    change[:after][key] = after_hash[key]

    changed_count += 1
  end

  return if changed_count == 0

  event = Event.new(
    feature: after.name,
    name: :update,
    data: change,
    context: current_context,
    created_at: Time.now,
  )

  storage_key = events_storage_key(after.name)

  @storage.zadd(storage_key, -event.timestamp, event.serialize)
  @storage.zremrangebyrank(storage_key, @history_length, -1)

  if @global
    @storage.zadd(global_events_storage_key, -event.timestamp, event.serialize)
    @storage.zremrangebyrank(global_events_storage_key, @history_length, -1)
  end
end
updated_at(feature_name) click to toggle source
# File lib/rollout/logging.rb, line 61
def updated_at(feature_name)
  storage_key = events_storage_key(feature_name)
  _, score = @storage.zrange(storage_key, 0, 0, with_scores: true).first
  Time.at(-score.to_f / 1_000_000) if score
end
with_context(context) { || ... } click to toggle source
# File lib/rollout/logging.rb, line 158
def with_context(context)
  raise ArgumentError, "context must be a Hash" unless context.is_a?(Hash)
  raise ArgumentError, "block is required" unless block_given?

  Thread.current[CONTEXT_THREAD_KEY] = context
  yield
ensure
  Thread.current[CONTEXT_THREAD_KEY] = nil
end
without() { || ... } click to toggle source
# File lib/rollout/logging.rb, line 172
def without
  Thread.current[WITHOUT_THREAD_KEY] = true
  yield
ensure
  Thread.current[WITHOUT_THREAD_KEY] = nil
end

Private Instance Methods

current_timestamp() click to toggle source
# File lib/rollout/logging.rb, line 193
def current_timestamp
  (Time.now.to_f * 1_000_000).to_i
end
events_storage_key(feature_name) click to toggle source
# File lib/rollout/logging.rb, line 189
def events_storage_key(feature_name)
  "feature:#{feature_name}:logging:events"
end
global_events_storage_key() click to toggle source
# File lib/rollout/logging.rb, line 185
def global_events_storage_key
  "feature:_global_:logging:events"
end