# frozen_string_literal: true require 'date' module Aws module Plugins class ClientMetricsSendPlugin < Seahorse::Client::Plugin def add_handlers(handlers, config) if config.client_side_monitoring && config.client_side_monitoring_port # AttemptHandler comes just before we would retry an error. # Or before we would follow redirects. handlers.add(AttemptHandler, step: :sign, priority: 39) # ErrorHandler comes after we have parsed an error. handlers.add(ErrorHandler, step: :sign, priority: 95) # LatencyHandler is as close to sending as possible. handlers.add(LatencyHandler, step: :sign, priority: 0) end end class LatencyHandler < Seahorse::Client::Handler def call(context) start_time = Aws::Util.monotonic_milliseconds resp = @handler.call(context) end_time = Aws::Util.monotonic_milliseconds latency = end_time - start_time context.metadata[:current_call_attempt].request_latency = latency resp end end class AttemptHandler < Seahorse::Client::Handler def call(context) request_metrics = context.metadata[:client_metrics] attempt_opts = { timestamp: DateTime.now.strftime('%Q').to_i, fqdn: context.http_request.endpoint.host, region: context.config.region, user_agent: context.http_request.headers["user-agent"], } # It will generally cause an error, but it is semantically valid for # credentials to not exist. if context.config.credentials attempt_opts[:access_key] = context.config.credentials.credentials.access_key_id attempt_opts[:session_token] = context.config.credentials.credentials.session_token end call_attempt = request_metrics.build_call_attempt(attempt_opts) context.metadata[:current_call_attempt] = call_attempt resp = @handler.call(context) if context.metadata[:redirect_region] call_attempt.region = context.metadata[:redirect_region] end headers = context.http_response.headers if headers.include?("x-amz-id-2") call_attempt.x_amz_id_2 = headers["x-amz-id-2"] end if headers.include?("x-amz-request-id") call_attempt.x_amz_request_id = headers["x-amz-request-id"] end if headers.include?("x-amzn-request-id") call_attempt.x_amzn_request_id = headers["x-amzn-request-id"] end call_attempt.http_status_code = context.http_response.status_code context.metadata[:current_call_attempt] = call_attempt request_metrics.add_call_attempt(call_attempt) resp end end class ErrorHandler < Seahorse::Client::Handler def call(context) resp = @handler.call(context) call_attempt = context.metadata[:current_call_attempt] if (e = resp.error) e_name = _extract_error_name(e) e_msg = e.message call_attempt.aws_exception = "#{e_name}" call_attempt.aws_exception_msg = "#{e_msg}" end resp end private def _extract_error_name(error) if error.is_a?(Aws::Errors::ServiceError) error.class.code else error.class.name.to_s end end end end end end