http_event_logger 0.1.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1c6e13a02bf58df078b6db91d89d34e04a3ab46
4
+ data.tar.gz: 8743f10e8bc7672991d0447cf8dad126d84d7e7f
5
+ SHA512:
6
+ metadata.gz: 5827ef887a7b2a13a9d9d29a684eb1b75963047ed30c7a4c9717c5346a2268ad94521dd363f8c57b84311306242e52d4e732f091f0aa9882ea0fccfeea9f169c
7
+ data.tar.gz: 27e747db5d476fbc7fc9db0adf5a759ca1ea5d750c4b70ddf8219a60956b7bf8bea9cdda18aeabbd0c0cc3bc9f1f95f3d0fd8ebc56021954dfcf966512809079
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Thilo Rusche
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ module Ethon
2
+
3
+ class Easy
4
+
5
+ module Http
6
+
7
+ def http_request_with_logging(url, action_name, options={})
8
+ create_request_event(url, action_name, options)
9
+ http_request_without_logging(url, action_name, options)
10
+ end
11
+
12
+ alias_method_chain :http_request, :logging
13
+
14
+ private
15
+
16
+ def create_request_event(url, action_name, options)
17
+ @request_event = HttpEventLogger::Event::Request.new(
18
+ method: action_name,
19
+ uri: url,
20
+ headers: options[:headers],
21
+ body: options[:body]
22
+ )
23
+ end
24
+
25
+ end
26
+
27
+ module Operations
28
+
29
+ def perform_with_logging
30
+ result = nil
31
+ time_taken_in_seconds = ::Benchmark.realtime do
32
+ result = perform_without_logging
33
+ end
34
+ create_response_event(time_taken_in_seconds)
35
+ result
36
+ end
37
+
38
+ alias_method_chain :perform, :logging
39
+
40
+ private
41
+
42
+ def create_response_event(time_taken_in_seconds)
43
+ HttpEventLogger::Event::Response.new(
44
+ request: @request_event,
45
+ time_taken_in_seconds: time_taken_in_seconds,
46
+ status: response_code,
47
+ headers: response_headers,
48
+ body: response_body
49
+ )
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ end if defined?(Ethon)
@@ -0,0 +1,64 @@
1
+ module Excon
2
+
3
+ class Socket
4
+
5
+ def connect_with_logging
6
+ host = @data[:proxy] ? @data[:proxy][:host] : @data[:host]
7
+ port = @data[:proxy] ? @data[:proxy][:port] : @data[:port]
8
+ HttpEventLogger::Event::Connection.new(host, port)
9
+ connect_without_logging
10
+ end
11
+
12
+ alias_method_chain :connect, :logging
13
+
14
+ end
15
+
16
+ class Connection
17
+
18
+ def request_with_logging(params, &block)
19
+ request_datum = @data.merge(params)
20
+ request_datum[:headers] = @data[:headers].merge(request_datum[:headers] || {})
21
+ request_event = create_request_event(request_datum)
22
+ result = nil
23
+ time_taken_in_seconds = ::Benchmark.realtime do
24
+ result = request_without_logging(params, &block)
25
+ end
26
+ response = result.is_a?(Excon::Response) ? result : Excon::Response.new(response(result)[:response])
27
+ create_response_event(request_event, time_taken_in_seconds, response)
28
+ result
29
+ end
30
+
31
+ alias_method_chain :request, :logging
32
+
33
+ private
34
+
35
+ def create_request_event(datum)
36
+ HttpEventLogger::Event::Request.new(
37
+ method: method_from(datum),
38
+ uri: absolute_url_from(datum),
39
+ headers: datum[:headers],
40
+ body: datum[:body]
41
+ )
42
+ end
43
+
44
+ def method_from(datum)
45
+ datum[:method] ? datum[:method].to_s.upcase : "UNKNOWN"
46
+ end
47
+
48
+ def absolute_url_from(datum)
49
+ "#{datum[:scheme]}://#{datum[:host]}:#{datum[:port]}#{datum[:path]}#{datum[:query]}"
50
+ end
51
+
52
+ def create_response_event(request_event, time_taken_in_seconds, response)
53
+ HttpEventLogger::Event::Response.new(
54
+ request: request_event,
55
+ time_taken_in_seconds: time_taken_in_seconds,
56
+ status: response.status,
57
+ headers: response.headers,
58
+ body: response.body
59
+ )
60
+ end
61
+
62
+ end
63
+
64
+ end if defined?(Excon)
@@ -0,0 +1,60 @@
1
+ class HTTPClient
2
+
3
+ private
4
+
5
+ class Session
6
+
7
+ def create_socket_with_logging(site)
8
+ HttpEventLogger::Event::Connection.new(site.host, site.port)
9
+ create_socket_without_logging(site)
10
+ end
11
+
12
+ alias_method_chain :create_socket, :logging
13
+
14
+ end
15
+
16
+ def do_get_block_with_logging(req, proxy, conn, &block)
17
+ request_event = create_request_event(req)
18
+ retryable_response_error = nil
19
+ time_taken_in_seconds = ::Benchmark.realtime do
20
+ begin
21
+ do_get_block_without_logging(req, proxy, conn, &block)
22
+ rescue RetryableResponse => exc
23
+ retryable_response_error = exc
24
+ end
25
+ end
26
+ res = conn.pop
27
+ create_response_event(request_event, res, time_taken_in_seconds)
28
+ conn.push(res)
29
+ raise retryable_response_error unless retryable_response_error.nil?
30
+ end
31
+
32
+ alias_method_chain :do_get_block, :logging
33
+
34
+ def create_request_event(req)
35
+ HttpEventLogger::Event::Request.new(
36
+ method: req.header.request_method,
37
+ uri: req.header.request_uri,
38
+ headers: req.headers,
39
+ body: req.body
40
+ )
41
+ end
42
+
43
+ def create_response_event(request_event, res, time_taken_in_seconds)
44
+ HttpEventLogger::Event::Response.new(
45
+ request: request_event,
46
+ time_taken_in_seconds: time_taken_in_seconds,
47
+ status: res.status_code,
48
+ headers: response_headers_from(res),
49
+ body: res.body
50
+ )
51
+ end
52
+
53
+ def response_headers_from(res)
54
+ res.header.all.reduce({}) do |result, header|
55
+ result[header[0]] = header[1]
56
+ result
57
+ end
58
+ end
59
+
60
+ end if defined?(::HTTPClient)
@@ -0,0 +1,65 @@
1
+ module Net
2
+
3
+ class HTTP
4
+
5
+ private
6
+
7
+ HIDDEN_PORTS = [ 80, 443 ].freeze
8
+
9
+ public
10
+
11
+ def connect_with_logging
12
+ HttpEventLogger::Event::Connection.new(@address, @port) unless started?
13
+ connect_without_logging
14
+ end
15
+
16
+ alias_method_chain :connect, :logging
17
+
18
+ def request_with_logging(req, body = nil, &block)
19
+ create_request_event(req, body) if started?
20
+ response = nil
21
+ time_taken_in_seconds = ::Benchmark.realtime do
22
+ response = request_without_logging(req, body, &block)
23
+ end
24
+ create_response_event(time_taken_in_seconds, response) if started?
25
+ response
26
+ end
27
+
28
+ alias_method_chain :request, :logging
29
+
30
+ private
31
+
32
+ def create_request_event(req, body)
33
+ @request_event = HttpEventLogger::Event::Request.new(
34
+ method: req.method,
35
+ uri: absolute_url_from(req),
36
+ headers: req.each_header.collect,
37
+ body: request_body_from(req, body)
38
+ )
39
+ end
40
+
41
+ def absolute_url_from(req)
42
+ protocol = @use_ssl ? "https" : "http"
43
+ port_indicator = HIDDEN_PORTS.include?(@port) ? "" : ":#{@port}"
44
+ hostname = "#{@address}#{port_indicator}"
45
+ "#{protocol}://#{hostname}#{req.path}"
46
+ end
47
+
48
+ # A bit convoluted because post_form uses form_data= to assign the data, so in that case req.body will be empty
49
+ def request_body_from(req, body)
50
+ req.body.nil? || req.body.size == 0 ? body : req.body
51
+ end
52
+
53
+ def create_response_event(time_taken_in_seconds, response)
54
+ HttpEventLogger::Event::Response.new(
55
+ request: @request_event,
56
+ time_taken_in_seconds: time_taken_in_seconds,
57
+ status: response.code,
58
+ headers: response.each_header.collect,
59
+ body: response.body
60
+ )
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,40 @@
1
+ module Patron
2
+
3
+ class Session
4
+
5
+ def request_with_logging(action_name, url, headers, options={})
6
+ create_request_event(action_name, headers, options, url)
7
+ response = nil
8
+ time_taken_in_seconds = ::Benchmark.realtime do
9
+ response = request_without_logging(action_name, url, headers, options)
10
+ end
11
+ create_response_event(response, time_taken_in_seconds)
12
+ response
13
+ end
14
+
15
+ alias_method_chain :request, :logging
16
+
17
+ private
18
+
19
+ def create_response_event(response, time_taken_in_seconds)
20
+ HttpEventLogger::Event::Response.new(
21
+ request: @request_event,
22
+ time_taken_in_seconds: time_taken_in_seconds,
23
+ status: response.status,
24
+ headers: response.headers,
25
+ body: response.body
26
+ )
27
+ end
28
+
29
+ def create_request_event(action_name, headers, options, url)
30
+ @request_event = HttpEventLogger::Event::Request.new(
31
+ method: action_name,
32
+ uri: url,
33
+ headers: headers,
34
+ body: options[:data]
35
+ )
36
+ end
37
+
38
+ end
39
+
40
+ end if defined?(Patron)
@@ -0,0 +1,9 @@
1
+ module HttpEventLogger
2
+
3
+ class Configuration
4
+
5
+ attr_accessor :logger
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,21 @@
1
+ module HttpEventLogger
2
+ module Event
3
+
4
+ class Connection
5
+
6
+ attr_reader :host, :port
7
+
8
+ def initialize(host, port)
9
+ @host = host
10
+ @port = port ? port.to_s : nil
11
+ HttpEventLogger::Event::Observer.observe(:connected, self)
12
+ end
13
+
14
+ def uri
15
+ @port.present? ? "#{@host}:#{@port}" : @host
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module HttpEventLogger
2
+ module Event
3
+
4
+ class Headers
5
+
6
+ def initialize(headers)
7
+ @headers = headers || ""
8
+ end
9
+
10
+ def [](name)
11
+ @headers.is_a?(Hash) ? @headers[name] : nil
12
+ end
13
+
14
+ def to_s
15
+ @headers.is_a?(String) ? @headers : @headers.map { |key, value| "#{key}: #{value}" }.join(", ")
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module HttpEventLogger
2
+ module Event
3
+
4
+ class Observer
5
+
6
+ def self.observe(event_name, event_object)
7
+ HttpEventLogger.logger.send(event_name, event_object) if HttpEventLogger.logger.logs?(event_object.uri)
8
+ end
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module HttpEventLogger
2
+ module Event
3
+
4
+ class Request
5
+
6
+ attr_reader :method, :uri, :headers, :body
7
+
8
+ def initialize(args)
9
+ @method = args[:method].to_s.upcase
10
+ @uri = args[:uri].to_s
11
+ @headers = HttpEventLogger::Event::Headers.new(args[:headers])
12
+ @body = args[:body]
13
+ HttpEventLogger::Event::Observer.observe(:sent, self)
14
+ end
15
+
16
+ def base_uri
17
+ matcher = @uri.match(/\/\/([^\/\?]*)/)
18
+ matcher ? matcher[1] : ""
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ module HttpEventLogger
2
+ module Event
3
+
4
+ class Response
5
+
6
+ attr_reader :request, :time_taken_in_seconds, :status, :headers
7
+
8
+ delegate :base_uri, :uri, to: :request
9
+
10
+ def initialize(args)
11
+ @request = args[:request]
12
+ @time_taken_in_seconds = args[:time_taken_in_seconds]
13
+ @status = args[:status].to_s
14
+ @headers = HttpEventLogger::Event::Headers.new(args[:headers])
15
+ @raw_body = args[:body]
16
+ HttpEventLogger::Event::Observer.observe(:received, self)
17
+ end
18
+
19
+ def body_with_formatting
20
+ body.include?("\n") ? "\n#{body}" : body
21
+ end
22
+
23
+ private
24
+
25
+ def body
26
+ if body_read_deferred?
27
+ "[not available]"
28
+ elsif body_gzip_encoded?
29
+ Zlib::GzipReader.new(StringIO.new(@raw_body.to_s)).read
30
+ else
31
+ @raw_body.to_s
32
+ end
33
+ end
34
+
35
+ # open-uri wraps the response in a Net::ReadAdapter, so the response body is not available yet
36
+ def body_read_deferred?
37
+ @raw_body.is_a?(Net::ReadAdapter)
38
+ end
39
+
40
+ def body_gzip_encoded?
41
+ @headers["Content-Encoding"] =~ /gzip/
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ module HttpEventLogger
2
+
3
+ class Logger
4
+
5
+ private
6
+
7
+ DEFAULT_LOGGER = ::Logger.new($stdout).freeze
8
+ DEFAULT_SEVERITY = ::Logger::Severity::DEBUG.freeze
9
+
10
+ public
11
+
12
+ def initialize(options={})
13
+ @logger = options[:logger] || DEFAULT_LOGGER
14
+ @severity = options[:severity] || DEFAULT_SEVERITY
15
+ @blacklist_pattern = options[:blacklist_pattern] || nil
16
+ end
17
+
18
+ def logs?(uri)
19
+ @blacklist_pattern.nil? || @blacklist_pattern !~ uri
20
+ end
21
+
22
+ def connected(connection)
23
+ log "[#{connection.uri}] Connected"
24
+ end
25
+
26
+ def sent(request)
27
+ log "[#{request.base_uri}] Sent - #{request.method} #{request.uri}"
28
+ log "[#{request.base_uri}] Sent - Headers: #{request.headers}"
29
+ log "[#{request.base_uri}] Sent - Body: #{request.body}" if request.body.present?
30
+ end
31
+
32
+ def received(response)
33
+ log "[#{response.base_uri}] Benchmark: #{response.time_taken_in_seconds} seconds"
34
+ log "[#{response.base_uri}] Received - Status: #{response.status}"
35
+ log "[#{response.base_uri}] Received - Headers: #{response.headers}"
36
+ log "[#{response.base_uri}] Received - Body:#{response.body_with_formatting}"
37
+ end
38
+
39
+ private
40
+
41
+ def log(message)
42
+ @logger.add(@severity) { message }
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,3 @@
1
+ module HttpEventLogger
2
+ VERSION = "0.1.0.rc1".freeze
3
+ end
@@ -0,0 +1,43 @@
1
+ require 'net/http'
2
+ require 'logger'
3
+ require 'benchmark'
4
+
5
+ require 'active_support/core_ext/object/blank'
6
+ require 'active_support/core_ext/module/aliasing'
7
+ require 'active_support/core_ext/module/delegation'
8
+ require 'active_support/core_ext/string/inflections'
9
+
10
+ require_relative 'http_event_logger/configuration'
11
+ require_relative 'http_event_logger/event/connection'
12
+ require_relative 'http_event_logger/event/headers'
13
+ require_relative 'http_event_logger/event/request'
14
+ require_relative 'http_event_logger/event/response'
15
+ require_relative 'http_event_logger/logger'
16
+ require_relative 'http_event_logger/event/observer'
17
+ require_relative 'http_event_logger/adapter/net_http'
18
+ require_relative 'http_event_logger/adapter/httpclient'
19
+ require_relative 'http_event_logger/adapter/excon'
20
+ require_relative 'http_event_logger/adapter/ethon'
21
+ require_relative 'http_event_logger/adapter/patron'
22
+
23
+ module HttpEventLogger
24
+
25
+ class << self
26
+
27
+ attr_reader :configuration
28
+
29
+ def configure(&block)
30
+ @configuration = HttpEventLogger::Configuration.new
31
+ block.call @configuration
32
+ end
33
+
34
+ def logger
35
+ @configuration.logger
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ # Establish default configuration
43
+ HttpEventLogger.configure { |config| config.logger = HttpEventLogger::Logger.new }