diff --git a/lib/timber/events.rb b/lib/timber/events.rb index 0e2a817f..d20e2adf 100644 --- a/lib/timber/events.rb +++ b/lib/timber/events.rb @@ -1,6 +1,8 @@ require "timber/events/controller_call" require "timber/events/custom" require "timber/events/exception" +require "timber/events/http_client_request" +require "timber/events/http_client_response" require "timber/events/http_server_request" require "timber/events/http_server_response" require "timber/events/sql_query" diff --git a/lib/timber/events/http_client_request.rb b/lib/timber/events/http_client_request.rb new file mode 100644 index 00000000..dbe8a07f --- /dev/null +++ b/lib/timber/events/http_client_request.rb @@ -0,0 +1,60 @@ +module Timber + module Events + # The HTTP client request event tracks *outgoing* HTTP requests giving you structured insight + # into communication with external services. + # + # @note This event should be installed automatically through probes, + # such as the {Probes::NetHTTP} probe. + class HTTPClientRequest < Timber::Event + attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id, + :scheme, :service_name + + def initialize(attributes) + @body = Util::HTTPEvent.normalize_body(attributes[:body]) + @headers = Util::HTTPEvent.normalize_headers(attributes[:headers]) + @host = attributes[:host] || raise(ArgumentError.new(":host is required")) + @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required")) + @path = attributes[:path] || raise(ArgumentError.new(":path is required")) + @port = attributes[:port] + @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string]) + @request_id = attributes[:request_id] + @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required")) + @service_name = attributes[:service_name] + end + + def to_hash + {body: body, headers: headers, host: host, method: method, path: path, port: port, + query_string: query_string, request_id: request_id, scheme: scheme, + service_name: service_name} + end + alias to_h to_hash + + def as_json(_options = {}) + {:server_side_app => {:http_client_request => to_hash}} + end + + def message + message = 'Outgoing HTTP request to ' + + if service_name + mesage << " #{service_name} [#{method}] #{full_path}" + else + message << " [#{method}] #{full_url}" + end + end + + def status_description + Rack::Utils::HTTP_STATUS_CODES[status] + end + + private + def full_path + Util::HTTPEvent.full_path(path, query_string) + end + + def full_url + "#{scheme}#{host}#{full_path}" + end + end + end +end \ No newline at end of file diff --git a/lib/timber/events/http_client_response.rb b/lib/timber/events/http_client_response.rb new file mode 100644 index 00000000..58e5e991 --- /dev/null +++ b/lib/timber/events/http_client_response.rb @@ -0,0 +1,46 @@ +module Timber + module Events + # The HTTP client response event tracks responses for *outgoing* HTTP *requests*. + # This gives you structured insight into communication with external services. + # + # @note This event should be installed automatically through probes, + # such as the {Probes::NetHTTP} probe. + class HTTPClientResponse < Timber::Event + attr_reader :body, :headers, :request_id, :service_name, :status, :time_ms + + def initialize(attributes) + @body = Util::HTTPEvent.normalize_body(attributes[:body]) + @headers = Util::HTTPEvent.normalize_headers(attributes[:headers]) + @request_id = attributes[:request_id] + @service_name = attributes[:service_name] + @status = attributes[:status] || raise(ArgumentError.new(":status is required")) + @time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required")) + @time_ms = @time_ms.round(6) + end + + def to_hash + {body: body, headers: headers, request_id: request_id, service_name: service_name, + status: status, time_ms: time_ms} + end + alias to_h to_hash + + def as_json(_options = {}) + {:server_side_app => {:http_client_response => to_hash}} + end + + def message + message = "Outgoing HTTP response" + + if service_name + message << " from #{service_name}" + end + + message << " #{status_description} in #{time_ms}ms" + end + + def status_description + Rack::Utils::HTTP_STATUS_CODES[status] + end + end + end +end \ No newline at end of file diff --git a/lib/timber/events/http_server_request.rb b/lib/timber/events/http_server_request.rb index cca0a8e0..c9363c12 100644 --- a/lib/timber/events/http_server_request.rb +++ b/lib/timber/events/http_server_request.rb @@ -1,31 +1,29 @@ module Timber module Events - # The HTTP request event tracks incoming HTTP requests. + # The HTTP server request event tracks incoming HTTP requests to your HTTP server. + # Such as unicorn, webrick, puma, etc. # # @note This event should be installed automatically through probes, # such as the {Probes::ActionControllerLogSubscriber} probe. class HTTPServerRequest < Timber::Event - attr_reader :host, :method, :path, :port, :query_string, :content_type, - :remote_addr, :referrer, :request_id, :scheme, :user_agent + attr_reader :body, :headers, :host, :method, :path, :port, :query_string, :request_id, + :scheme def initialize(attributes) + @body = Util::HTTPEvent.normalize_body(attributes[:body]) + @headers = Util::HTTPEvent.normalize_headers(attributes[:headers]) @host = attributes[:host] || raise(ArgumentError.new(":host is required")) - @method = attributes[:method] || raise(ArgumentError.new(":method is required")) + @method = Util::HTTPEvent.normalize_method(attributes[:method]) || raise(ArgumentError.new(":method is required")) @path = attributes[:path] || raise(ArgumentError.new(":path is required")) @port = attributes[:port] - @query_string = attributes[:query_string] - @content_type = attributes[:content_type] - @remote_addr = attributes[:remote_addr] - @referrer = attributes[:referrer] - @request_id = attributes[:request_id] + @query_string = Util::HTTPEvent.normalize_query_string(attributes[:query_string]) @scheme = attributes[:scheme] || raise(ArgumentError.new(":scheme is required")) - @user_agent = attributes[:user_agent] + @request_id = attributes[:request_id] end def to_hash - {host: host, method: method, path: path, port: port, query_string: query_string, - headers: {content_type: content_type, remote_addr: remote_addr, referrer: referrer, - request_id: request_id, scheme: scheme, user_agent: user_agent}} + {body: body, headers: headers, host: host, method: method, path: path, port: port, + query_string: query_string, request_id: request_id, scheme: scheme} end alias to_h to_hash @@ -43,6 +41,15 @@ def message def status_description Rack::Utils::HTTP_STATUS_CODES[status] end + + private + def truncate_body(body) + if body.is_a?(String) && body.length > 2000 + body.truncate(2000) + else + body + end + end end end end \ No newline at end of file diff --git a/lib/timber/events/http_server_response.rb b/lib/timber/events/http_server_response.rb index 495d8c4d..625134c9 100644 --- a/lib/timber/events/http_server_response.rb +++ b/lib/timber/events/http_server_response.rb @@ -1,13 +1,17 @@ module Timber module Events - # The HTTP response event tracks outgoing HTTP request responses. + # The HTTP server response event tracks outgoing HTTP responses that you send + # to clients. # # @note This event should be installed automatically through probes, # such as the {Probes::ActionControllerLogSubscriber} probe. class HTTPServerResponse < Timber::Event - attr_reader :status, :time_ms, :additions + attr_reader :body, :headers, :request_id, :status, :time_ms, :additions def initialize(attributes) + @body = Util::HTTPEvent.normalize_body(attributes[:body]) + @headers = Util::HTTPEvent.normalize_headers(attributes[:headers]) + @request_id = attributes[:request_id] @status = attributes[:status] || raise(ArgumentError.new(":status is required")) @time_ms = attributes[:time_ms] || raise(ArgumentError.new(":time_ms is required")) @time_ms = @time_ms.round(6) @@ -15,7 +19,7 @@ def initialize(attributes) end def to_hash - {status: status, time_ms: time_ms} + {body: body, headers: headers, request_id: request_id, status: status, time_ms: time_ms} end alias to_h to_hash diff --git a/lib/timber/util/http_event.rb b/lib/timber/util/http_event.rb new file mode 100644 index 00000000..927223fe --- /dev/null +++ b/lib/timber/util/http_event.rb @@ -0,0 +1,25 @@ +module Timber + module Util + module HTTPEvent + def self.full_path(path, query_string) + if query_string + "#{path}?#{query_string}" + else + path + end + end + + def self.normalize_body(body) + if body.is_a?(String) && body.length > 2000 + body.truncate(2000) + else + body + end + end + + def normalize_headers(headers) + + end + end + end +end \ No newline at end of file