http_event_logger 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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 }