class THTP::Server::Instrumentation::Logging

A THTP::Server subscriber for RPC logging and exception recording

Public Class Methods

new(logger, backtrace_lines: 10) click to toggle source
# File lib/thtp/server/instrumentation.rb, line 75
def initialize(logger, backtrace_lines: 10)
  @logger = logger
  @backtrace_lines = backtrace_lines
end

Public Instance Methods

internal_error(request:, error:, time:) click to toggle source

An unknown error occurred @param request [Rack::Request] The inbound HTTP request @param error [Exception] The to-be-serialized exception @param time [Integer] Milliseconds of execution wall time

# File lib/thtp/server/instrumentation.rb, line 138
def internal_error(request:, error:, time:)
  @logger.error :server do
    {
      http: http_request_to_hash(request),
      internal_error: error_to_hash(error),
      elapsed_ms: time,
    }
  end
end
rpc_error(request:, rpc:, args:, error:, time:) click to toggle source

Handler raised an unexpected error @param request [Rack::Request] The inbound HTTP request @param rpc [Symbol] The name of the RPC @param args [Thrift::Struct?] The deserialized thrift args @param error [THTP::ServerError] The to-be-serialized exception @param time [Integer] Milliseconds of execution wall time

# File lib/thtp/server/instrumentation.rb, line 122
def rpc_error(request:, rpc:, args:, error:, time:)
  @logger.error :rpc do
    {
      rpc: rpc,
      http: http_request_to_hash(request),
      request: args_to_hash(args),
      error: error_to_hash(error),
      elapsed_ms: time,
    }
  end
end
rpc_exception(request:, rpc:, args:, exception:, time:) click to toggle source

Handler raised an exception defined in the schema @param request [Rack::Request] The inbound HTTP request @param rpc [Symbol] The name of the RPC @param args [Thrift::Struct] The deserialized thrift args @param exception [Thrift::Struct] The to-be-serialized thrift exception @param time [Integer] Milliseconds of execution wall time

# File lib/thtp/server/instrumentation.rb, line 104
def rpc_exception(request:, rpc:, args:, exception:, time:)
  @logger.info :rpc do
    {
      rpc: rpc,
      http: http_request_to_hash(request),
      request: args_to_hash(args),
      exception: result_to_hash(exception),
      elapsed_ms: time,
    }
  end
end
rpc_success(request:, rpc:, args:, result:, time:) click to toggle source

Everything went according to plan @param request [Rack::Request] The inbound HTTP request @param rpc [Symbol] The name of the RPC @param args [Thrift::Struct] The deserialized thrift args @param result [Thrift::Struct] The to-be-serialized thrift response @param time [Integer] Milliseconds of execution wall time

# File lib/thtp/server/instrumentation.rb, line 86
def rpc_success(request:, rpc:, args:, result:, time:)
  @logger.info :rpc do
    {
      rpc: rpc,
      http: http_request_to_hash(request),
      request: args_to_hash(args),
      result: result_to_hash(result),
      elapsed_ms: time,
    }
  end
end

Private Instance Methods

args_to_hash(rpc_args) click to toggle source
# File lib/thtp/server/instrumentation.rb, line 160
def args_to_hash(rpc_args)
  jsonify(rpc_args)
end
error_to_hash(error, backtrace: true) click to toggle source

converts non-schema errors to an ELK-compatible format (JSON-serialisable hash)

# File lib/thtp/server/instrumentation.rb, line 186
def error_to_hash(error, backtrace: true)
  error_info = { message: error.message, repr: error.inspect }
  error_info[:backtrace] = error.backtrace.first(@backtrace_lines) if backtrace
  { canonical_name(error.class) => error_info }
end
http_request_to_hash(rack_request) click to toggle source
# File lib/thtp/server/instrumentation.rb, line 150
def http_request_to_hash(rack_request)
  {
    user_agent: rack_request.user_agent,
    content_type: rack_request.content_type,
    verb: rack_request.request_method,
    path: rack_request.path_info,
    ssl: rack_request.ssl?,
  }
end
result_to_hash(result) click to toggle source

converts all possible Thrift result types to JSON, inferring types from collections, with the intent of producing ELK-compatible output (i.e., no multiple-type-mapped fields)

# File lib/thtp/server/instrumentation.rb, line 167
def result_to_hash(result)
  case result
  when nil
    nil # nulls are ok in ELK land
  when Array
    { list: { canonical_name(result.first.class) => jsonify(result) } }
  when Set
    { set: { canonical_name(result.first.class) => jsonify(result) } }
  when Hash
    ktype, vtype = result.first.map { |obj| canonical_name(obj.class) }
    { hash: { ktype => { vtype => jsonify(result) } } }
  when StandardError
    error_to_hash(result, backtrace: false)
  else # primitives
    { canonical_name(result.class) => jsonify(result) }
  end
end