class Labkit::Context

A context can be used to provide structured information on what resources GitLab is working on within a service. The currently supported keys are defined in the `KNOWN_KEYS` constant.

Values can be provided by passing a hash. If one of the values is a Proc the proc will only be called when the value is actually needed.

Multiple contexts can be nested, the nested context will inherit the values from the closest outer one. All contexts will have the same correlation id.

Usage:

Labkit::Context.with_context(user: 'username', root_namespace: -> { get_root_namespace } do |context|
  logger.info(context.to_h)
end

Constants

CORRELATION_ID_KEY
HEADER_PREFIX
KNOWN_KEYS
LOG_KEY
RAW_KEYS

Attributes

data[R]

Public Class Methods

correlation_id() click to toggle source
# File lib/labkit/context.rb, line 57
def correlation_id
  contexts.last&.correlation_id
end
current() click to toggle source
# File lib/labkit/context.rb, line 61
def current
  contexts.last
end
header_name(name) click to toggle source
# File lib/labkit/context.rb, line 77
def header_name(name)
  HEADER_PREFIX + log_key(name).titlecase(keep_id_suffix: true).gsub(/\W/, "-")
end
known_log_keys() click to toggle source
# File lib/labkit/context.rb, line 73
def known_log_keys
  @known_log_keys ||= (KNOWN_KEYS.map(&method(:log_key)) + RAW_KEYS).freeze
end
log_key(key) click to toggle source
# File lib/labkit/context.rb, line 65
def log_key(key)
  key = key.to_s
  return key if RAW_KEYS.include?(key)
  return key if key.starts_with?("#{LOG_KEY}.")

  "#{LOG_KEY}.#{key}"
end
new(values = {}) click to toggle source
# File lib/labkit/context.rb, line 88
def initialize(values = {})
  @data = {}

  assign_attributes(values)
end
pop(context) click to toggle source
# File lib/labkit/context.rb, line 53
def pop(context)
  contexts.pop while contexts.include?(context)
end
push(new_attributes = {}) click to toggle source
# File lib/labkit/context.rb, line 45
def push(new_attributes = {})
  new_context = current&.merge(new_attributes) || new(new_attributes)

  contexts.push(new_context)

  new_context
end
with_context(attributes = {}) { |context| ... } click to toggle source
# File lib/labkit/context.rb, line 35
def with_context(attributes = {})
  context = push(attributes)

  begin
    yield(context)
  ensure
    pop(context)
  end
end

Private Class Methods

contexts() click to toggle source
# File lib/labkit/context.rb, line 83
def contexts
  Thread.current[:labkit_contexts] ||= []
end

Public Instance Methods

correlation_id() click to toggle source
# File lib/labkit/context.rb, line 105
def correlation_id
  data[CORRELATION_ID_KEY]
end
get_attribute(attribute) click to toggle source
# File lib/labkit/context.rb, line 115
def get_attribute(attribute)
  raw = call_or_value(data[log_key(attribute)])

  call_or_value(raw)
end
merge(new_attributes) click to toggle source
# File lib/labkit/context.rb, line 94
def merge(new_attributes)
  new_context = self.class.new(data.dup)
  new_context.assign_attributes(new_attributes)

  new_context
end
to_h() click to toggle source
# File lib/labkit/context.rb, line 101
def to_h
  expand_data
end
to_headers() click to toggle source
# File lib/labkit/context.rb, line 109
def to_headers
  to_h.except(CORRELATION_ID_KEY).transform_keys do |key|
    self.class.header_name(key)
  end
end

Protected Instance Methods

assign_attributes(attributes) click to toggle source
# File lib/labkit/context.rb, line 123
def assign_attributes(attributes)
  attributes = attributes.transform_keys(&method(:log_key))
  attributes = attributes.slice(*known_log_keys)

  data.merge!(attributes)

  # Remove keys that had their values set to `nil` in the new attributes
  data.keep_if { |_, value| value.present? }

  # Assign a correlation if it was missing in the first context or when
  # explicitly removed
  data[CORRELATION_ID_KEY] ||= new_id

  data
end

Private Instance Methods

call_or_value(value) click to toggle source
# File lib/labkit/context.rb, line 145
def call_or_value(value)
  value.respond_to?(:call) ? value.call : value
end
expand_data() click to toggle source
# File lib/labkit/context.rb, line 149
def expand_data
  data.transform_values do |value|
    value = call_or_value(value)

    value.presence
  end.compact
end
new_id() click to toggle source
# File lib/labkit/context.rb, line 157
def new_id
  SecureRandom.hex
end