class Gruf::Error

Represents a error that can be transformed into a gRPC error and have metadata attached to the trailing headers. This layer acts as an middle layer that can have metadata injection, tracing support, and other functionality not present in the gRPC core.

Constants

MAX_METADATA_SIZE

Default limit on trailing metadata is 8KB. We need to be careful not to overflow this limit, or the response message will never be sent. Instead, resource_exhausted will be thrown.

METADATA_SIZE_EXCEEDED_CODE
METADATA_SIZE_EXCEEDED_MSG
TYPES

@return [Hash<GRPC::BadStatus>] A hash mapping of gRPC BadStatus codes to error symbols

Attributes

app_code[RW]

@return [Symbol] An arbitrary application code that can be used for logical processing of the error by the client

code[RW]

@return [Symbol] The given internal gRPC code for the error

debug_info[RW]

@return [Errors::DebugInfo] A object containing debugging information, such as a stack trace and exception name, that can be used to debug an given error response. This is sent by the server over the trailing metadata.

field_errors[RW]

@return [Array] An array of field errors that can be returned by the server

grpc_error[W]

@return [GRPC::BadStatus] The gRPC BadStatus error object that was generated

message[RW]

@return [String] The error message returned by the server

metadata[R]

@return [Hash] The trailing metadata that was attached to the error

Public Class Methods

new(args = {}) click to toggle source

Initialize the error, setting default values

@param [Hash] args (Optional) An optional hash of arguments that will set fields on the error object

# File lib/gruf/error.rb, line 83
def initialize(args = {})
  @field_errors = []
  @metadata = {}
  args.each do |k, v|
    send("#{k}=", v) if respond_to?(k)
  end
end

Public Instance Methods

add_field_error(field_name, error_code, message = '') click to toggle source

Add a field error to this error package

@param [Symbol] field_name The field name for the error @param [Symbol] error_code The application error code for the error; e.g. :job_not_found @param [String] message The application error message for the error; e.g. “Job not found with ID 123”

# File lib/gruf/error.rb, line 98
def add_field_error(field_name, error_code, message = '')
  @field_errors << Errors::Field.new(field_name, error_code, message)
end
attach_to_call(active_call) click to toggle source

Update the trailing metadata on the given gRPC call, including the error payload if configured to do so.

@param [GRPC::ActiveCall] active_call The marshalled gRPC call @return [Error] Return the error itself after updating metadata on the given gRPC call.

In the case of a metadata overflow error, we replace the current error with
a new one that won't cause a low-level http2 error.
# File lib/gruf/error.rb, line 151
def attach_to_call(active_call)
  metadata[Gruf.error_metadata_key.to_sym] = serialize if Gruf.append_server_errors_to_trailing_metadata
  return self if metadata.empty? || !active_call || !active_call.respond_to?(:output_metadata)

  # Check if we've overflown the maximum size of output metadata. If so,
  # log a warning and replace the metadata with something smaller to avoid
  # resource exhausted errors.
  if metadata.inspect.size > MAX_METADATA_SIZE
    code = METADATA_SIZE_EXCEEDED_CODE
    msg = METADATA_SIZE_EXCEEDED_MSG
    logger.warn "#{code}: #{msg} Original error: #{to_h.inspect}"
    err = Gruf::Error.new(code: :internal, app_code: code, message: msg)
    return err.attach_to_call(active_call)
  end

  active_call.output_metadata.update(metadata)
  self
end
fail!(active_call) click to toggle source

Fail the current gRPC call with the given error, properly attaching it to the call and raising the appropriate gRPC BadStatus code.

@param [GRPC::ActiveCall] active_call The marshalled gRPC call @return [GRPC::BadStatus] The gRPC BadStatus code this error is mapped to

# File lib/gruf/error.rb, line 177
def fail!(active_call)
  raise attach_to_call(active_call).grpc_error
end
grpc_error() click to toggle source

Return the appropriately mapped GRPC::BadStatus error object for this error

@return [GRPC::BadStatus]

# File lib/gruf/error.rb, line 201
def grpc_error
  md = @metadata || {}
  @grpc_error = grpc_class.new(message, **md)
end
has_field_errors?() click to toggle source

Return true if there are any present field errors

@return [Boolean] True if the service has any field errors

# File lib/gruf/error.rb, line 107
def has_field_errors?
  @field_errors.any?
end
metadata=(metadata) click to toggle source

Ensure all metadata values are strings as HTTP/2 requires string values for transport

@param [Hash] metadata The existing metadata hash @return [Hash] The newly set metadata

# File lib/gruf/error.rb, line 128
def metadata=(metadata)
  @metadata = metadata.transform_values(&:to_s)
end
serialize() click to toggle source

Serialize the error for transport

@return [String] The serialized error message

# File lib/gruf/error.rb, line 137
def serialize
  serializer = serializer_class.new(self)
  serializer.serialize.to_s
end
set_debug_info(detail, stack_trace = []) click to toggle source

Set the debugging information for the error message

@param [String] detail The detailed message generated by the exception @param [Array<String>] stack_trace An array of strings that represents the exception backtrace generated by the service

# File lib/gruf/error.rb, line 118
def set_debug_info(detail, stack_trace = [])
  @debug_info = Errors::DebugInfo.new(detail, stack_trace)
end
to_h() click to toggle source

Return the error represented in Hash form

@return [Hash] The error as a hash

# File lib/gruf/error.rb, line 186
def to_h
  {
    code: code,
    app_code: app_code,
    message: message,
    field_errors: field_errors.map(&:to_h),
    debug_info: debug_info.to_h
  }
end

Private Instance Methods

grpc_class() click to toggle source

Return the appropriate gRPC class for the given error code

@return [Class] The gRPC error class

# File lib/gruf/error.rb, line 226
def grpc_class
  TYPES[code]
end
serializer_class() click to toggle source

Return the error serializer being used for gruf

@return [Gruf::Serializers::Errors::Base]

# File lib/gruf/error.rb, line 213
def serializer_class
  if Gruf.error_serializer
    Gruf.error_serializer.is_a?(Class) ? Gruf.error_serializer : Gruf.error_serializer.to_s.constantize
  else
    Gruf::Serializers::Errors::Json
  end
end