class Sentry::Lambda::CaptureExceptions

Constants

TIMEOUT_WARNING_BUFFER

Public Class Methods

new(aws_event:, aws_context: NullContext.new, capture_timeout_warning: false) click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 6
def initialize(aws_event:, aws_context: NullContext.new, capture_timeout_warning: false)
  @aws_event = aws_event
  @aws_context = aws_context || NullContext.new
  @capture_timeout_warning = capture_timeout_warning
end

Public Instance Methods

_get_cloudwatch_logs_url(log_group_name, log_stream_name, start_time) click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 108
def _get_cloudwatch_logs_url(log_group_name, log_stream_name, start_time)
  formatstring = "%Y-%m-%dT%H:%M:%SZ"
  region = ENV['AWS_REGION']

  "https://console.aws.amazon.com/cloudwatch/home?region=#{region}" \
  "#logEventViewer:group=#{log_group_name};stream=#{log_stream_name}" \
  ";start=#{start_time.strftime(formatstring)};end=#{(Time.now.utc + 2).strftime(formatstring)}"
end
call() { || ... } click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 12
def call(&block)
  return yield unless Sentry.initialized?

  if @capture_timeout_warning && (@aws_context.get_remaining_time_in_millis > TIMEOUT_WARNING_BUFFER)
    Thread.new do
      configured_timeout_seconds = @aws_context.get_remaining_time_in_millis / 1000.0
      sleep_timeout_seconds = ((@aws_context.get_remaining_time_in_millis - TIMEOUT_WARNING_BUFFER) / 1000.0)

      timeout_message = "WARNING : Function is expected to get timed out. "\
                        "Configured timeout duration = #{configured_timeout_seconds.round} seconds."

      sleep(sleep_timeout_seconds)
      Sentry.capture_message(timeout_message)
    end
  end

  # make sure the current thread has a clean hub
  Sentry.clone_hub_to_current_thread

  Sentry.with_scope do |scope|
    start_time = Time.now.utc
    initial_remaining_time_in_millis = @aws_context.get_remaining_time_in_millis
    execution_expiration_time = Time.now.utc + ((initial_remaining_time_in_millis || 0)/1000.0)

    scope.clear_breadcrumbs
    scope.set_transaction_name(@aws_context.function_name)

    scope.add_event_processor do |event, hint|
      event_time = event.timestamp.is_a?(Float) ? Time.at(event.timestamp) : Time.parse(event.timestamp)
      remaining_time_in_millis = ((execution_expiration_time - event_time) * 1000).round
      execution_duration_in_millis = ((event_time - start_time) * 1000).round
      event.extra = event.extra.merge(
        lambda: {
          function_name: @aws_context.function_name,
          function_version: @aws_context.function_version,
          invoked_function_arn: @aws_context.invoked_function_arn,
          aws_request_id: @aws_context.aws_request_id,
          execution_duration_in_millis: execution_duration_in_millis,
          remaining_time_in_millis: remaining_time_in_millis
        }
      )

      event.extra = event.extra.merge(
        "cloudwatch logs": {
          url: _get_cloudwatch_logs_url(
            @aws_context.log_group_name,
            @aws_context.log_stream_name,
            start_time
          ),
          log_group: @aws_context.log_group_name,
          log_stream: @aws_context.log_stream_name
        }
      )

      event
    end

    transaction = start_transaction(@aws_event, @aws_context, scope.transaction_name)
    scope.set_span(transaction) if transaction

    begin
      response = yield
    rescue Sentry::Error
      finish_transaction(transaction, 500)
      raise # Don't capture Sentry errors
    rescue Exception => e
      capture_exception(e)
      finish_transaction(transaction, 500)
      raise
    end

    status_code = response.respond_to?(:dig) && (response&.dig(:statusCode) || response&.dig('statusCode')) || nil
    finish_transaction(transaction, status_code)

    response
  end
end
capture_exception(exception) click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 104
def capture_exception(exception)
  Sentry.capture_exception(exception)
end
finish_transaction(transaction, status_code) click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 97
def finish_transaction(transaction, status_code)
  return unless transaction

  transaction.set_http_status(status_code)
  transaction.finish
end
start_transaction(event, context, transaction_name) click to toggle source
# File lib/sentry/lambda/capture_exceptions.rb, line 90
def start_transaction(event, context, transaction_name)
  sentry_trace = event["HTTP_SENTRY_TRACE"]
  options = { name: transaction_name, op: 'serverless.function' }
  transaction = Sentry::Transaction.from_sentry_trace(sentry_trace, **options) if sentry_trace
  Sentry.start_transaction(transaction: transaction, **options)
end