pax_global_header00006660000000000000000000000064121526332410014511gustar00rootroot0000000000000052 comment=90b408999413cff9ff06d59b9d542aeeaa2a5044 ruby-samuel-0.3.3/000077500000000000000000000000001215263324100137615ustar00rootroot00000000000000ruby-samuel-0.3.3/.document000066400000000000000000000000741215263324100156010ustar00rootroot00000000000000README.rdoc lib/**/*.rb bin/* features/**/*.feature LICENSE ruby-samuel-0.3.3/.gitignore000066400000000000000000000000631215263324100157500ustar00rootroot00000000000000.DS_Store .yardoc Gemfile.lock /coverage /doc /pkg ruby-samuel-0.3.3/Gemfile000066400000000000000000000001241215263324100152510ustar00rootroot00000000000000source "http://rubygems.org" gemspec platforms :jruby do gem "jruby-openssl" end ruby-samuel-0.3.3/LICENSE000066400000000000000000000020461215263324100147700ustar00rootroot00000000000000Copyright 2009–2011 Chris Kampmeier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-samuel-0.3.3/README.rdoc000066400000000000000000000066621215263324100156010ustar00rootroot00000000000000= Samuel Samuel is a gem for automatic logging of your HTTP requests. It's named for the serial diarist Mr. Pepys, who was known to reliably record events both quotidian and remarkable. Should a Great Plague, Fire, or Whale befall an important external web service you use, you'll be sure to have a tidy record of it. It supports both Net::HTTP and HTTPClient (formerly HTTPAccess2), automatically loading the correct logger for the HTTP client you're using. == Usage If you're using Bundler, just add this to your Gemfile: gem "samuel", "~> 0.3" When Rails is loaded, Samuel configures itself to use Rails's logger and an ActiveRecord-like format automatically, so you don't need to do anything else. For non-Rails projects, you'll have to manually configure logging, like this: require 'samuel' Samuel.logger = Logger.new('http_requests.log') If you don't assign a logger, Samuel will configure a default logger on +STDOUT+. == HTTP Clients When you load Samuel, it automatically detects which HTTP clients you've loaded, then patches them to add logging. If no HTTP drivers are loaded when you load Samuel, it will automatically load Net::HTTP for you. (So, if you're using HTTPClient or a library based on it, make sure to require it before you require Samuel.) == Configuration There are two ways to specify configuration options for Samuel: global and inline. Global configs look like this: Samuel.config[:labels] = {"example.com" => "Example API"} Samuel.config[:filtered_params] = :password You should put global configuration somewhere early-on in your program. If you're using Rails, config/initializers/samuel.rb will do the trick. Alternatively, an inline configuration block temporarily overrides any global configuration for a set of HTTP requests: Samuel.with_config :label => "Twitter API" do Net::HTTP.start("twitter.com") { |http| http.get("/help/test") } end Right now, there are three configuration changes you can make in either style: * :labels -- This is a hash with domain substrings as keys and log labels as values. If a request domain includes one of the domain substrings, the corresponding label will be used for the first part of that log entry. By default this is empty, so that all requests are labeled with a default of "HTTP Request". * :label -- As an alternative to the :labels hash, this is simply a string. If set, it takes precedence over any :labels (by default, it's not set). It gets "Request" appended to it as well -- so if you want your log to always say Twitter API Request instead of the default HTTP Request, you can set this to "Twitter API". I'd recommend using this setting globally if you're only making requests to one service, or inline if you just need to temporarily override the global :labels. * :filtered_params -- This works just like Rails's +filter_parameter_logging+ method. Set it to a symbol, string, or array of them, and Samuel will filter the value of query parameters that have any of these patterns as a substring by replacing the value with [FILTERED] in your logs. By default, no filtering is enabled. Samuel logs successful HTTP requests at the +INFO+ level; Failed requests log at the +WARN+ level. This isn't currently configurable, but it's on the list. == License Copyright 2009–2011 Chris Kampmeier. See +LICENSE+ for details. ruby-samuel-0.3.3/Rakefile000066400000000000000000000015141215263324100154270ustar00rootroot00000000000000require 'rubygems' require 'rake' task :check_dependencies do begin require "bundler" rescue LoadError abort "Samuel uses Bundler to manage development dependencies. Install it with `gem install bundler`." end system("bundle check") || abort end require 'rake/testtask' Rake::TestTask.new(:test) do |test| test.libs << 'lib' << 'test' test.pattern = 'test/**/*_test.rb' test.verbose = false test.warning = true end task :default => [:check_dependencies, :test] begin require 'rcov/rcovtask' Rcov::RcovTask.new do |test| test.libs << 'test' test.pattern = 'test/**/*_test.rb' test.rcov_opts << "--sort coverage" test.rcov_opts << "--exclude gems" test.verbose = false test.warning = true end rescue LoadError end begin require 'yard' YARD::Rake::YardocTask.new rescue LoadError end ruby-samuel-0.3.3/lib/000077500000000000000000000000001215263324100145275ustar00rootroot00000000000000ruby-samuel-0.3.3/lib/samuel.rb000066400000000000000000000022301215263324100163370ustar00rootroot00000000000000require "logger" require "forwardable" require "samuel/loader" require "samuel/diary" require "samuel/driver_patches/http_client" require "samuel/driver_patches/net_http" require "samuel/log_entries/base" require "samuel/log_entries/http_client" require "samuel/log_entries/net_http" module Samuel extend self VERSION = "0.3.3" attr_writer :logger, :config def logger @logger = nil if !defined?(@logger) return @logger if !@logger.nil? if defined?(Rails) && Rails.respond_to?(:logger) @logger = Rails.logger else @logger = Logger.new(STDOUT) end end def config Thread.current[:__samuel_config] ? Thread.current[:__samuel_config] : @config end def with_config(options = {}) original_config = config.dup nested = !Thread.current[:__samuel_config].nil? Thread.current[:__samuel_config] = original_config.merge(options) yield Thread.current[:__samuel_config] = nested ? original_config : nil end def reset_config Thread.current[:__samuel_config] = nil @config = {:label => nil, :labels => {}, :filtered_params => []} end end Samuel.reset_config Samuel::Loader.apply_driver_patches ruby-samuel-0.3.3/lib/samuel/000077500000000000000000000000001215263324100160155ustar00rootroot00000000000000ruby-samuel-0.3.3/lib/samuel/diary.rb000066400000000000000000000017651215263324100174630ustar00rootroot00000000000000module Samuel module Diary extend self def record_request(http, request, time_requested = current_time) @requests ||= [] @requests.push({:request => request, :time_requested => time_requested}) end def record_response(http, request, response, time_responded = current_time) time_requested = @requests.detect { |r| r[:request] == request }[:time_requested] @requests.reject! { |r| r[:request] == request } log_request_and_response(http, request, response, time_requested, time_responded) end def current_time Time.now end private def log_request_and_response(http, request, response, time_started, time_ended) log_entry_class = case http.class.to_s when "Net::HTTP" then LogEntries::NetHttp when "HTTPClient" then LogEntries::HttpClient else raise NotImplementedError end log_entry = log_entry_class.new(http, request, response, time_started, time_ended) log_entry.log! end end end ruby-samuel-0.3.3/lib/samuel/driver_patches/000077500000000000000000000000001215263324100210175ustar00rootroot00000000000000ruby-samuel-0.3.3/lib/samuel/driver_patches/http_client.rb000066400000000000000000000027431215263324100236670ustar00rootroot00000000000000module Samuel module DriverPatches module HTTPClient def self.included(klass) methods_to_wrap = %w(initialize do_get_block do_get_stream) methods_to_wrap.each do |method| klass.send(:alias_method, "#{method}_without_samuel", method) klass.send(:alias_method, method, "#{method}_with_samuel") end end def initialize_with_samuel(*args) initialize_without_samuel(*args) @request_filter << LoggingFilter.new(self) end def do_get_block_with_samuel(req, proxy, conn, &block) begin do_get_block_without_samuel(req, proxy, conn, &block) rescue Exception => e Samuel::Diary.record_response(self, req, e) raise end end def do_get_stream_with_samuel(req, proxy, conn) begin do_get_stream_without_samuel(req, proxy, conn) rescue Exception => e Samuel::Diary.record_response(self, req, e) raise end end class LoggingFilter def initialize(http_client_instance) @http_client_instance = http_client_instance end def filter_request(request) Samuel::Diary.record_request(@http_client_instance, request) end def filter_response(request, response) Samuel::Diary.record_response(@http_client_instance, request, response) nil # this returns command symbols like :retry, etc. end end end end end ruby-samuel-0.3.3/lib/samuel/driver_patches/net_http.rb000066400000000000000000000023361215263324100231750ustar00rootroot00000000000000module Samuel module DriverPatches module NetHTTP def self.included(klass) methods_to_wrap = %w(request connect) methods_to_wrap.each do |method| klass.send(:alias_method, "#{method}_without_samuel", method) klass.send(:alias_method, method, "#{method}_with_samuel") end end def request_with_samuel(request, body = nil, &block) response, exception_raised = nil, false request_time = Samuel::Diary.current_time begin response = request_without_samuel(request, body, &block) rescue Exception => response exception_raised = true end Samuel::Diary.record_request(self, request, request_time) Samuel::Diary.record_response(self, request, response) raise response if exception_raised response end def connect_with_samuel connect_without_samuel rescue Exception => response fake_request = Object.new def fake_request.path; ""; end def fake_request.method; "CONNECT"; end Samuel::Diary.record_request(self, fake_request) Samuel::Diary.record_response(self, fake_request, response) raise end end end end ruby-samuel-0.3.3/lib/samuel/loader.rb000066400000000000000000000007141215263324100176120ustar00rootroot00000000000000module Samuel module Loader extend self def apply_driver_patches loaded = { :net_http => defined?(Net::HTTP), :http_client => defined?(HTTPClient) } Net::HTTP.send(:include, DriverPatches::NetHTTP) if loaded[:net_http] HTTPClient.send(:include, DriverPatches::HTTPClient) if loaded[:http_client] if loaded.values.none? require 'net/http' apply_driver_patches end end end end ruby-samuel-0.3.3/lib/samuel/log_entries/000077500000000000000000000000001215263324100203275ustar00rootroot00000000000000ruby-samuel-0.3.3/lib/samuel/log_entries/base.rb000066400000000000000000000035371215263324100215760ustar00rootroot00000000000000module Samuel module LogEntries class Base def initialize(http, request, response, time_requested, time_responded) @http, @request, @response = http, request, response @seconds = time_responded - time_requested end def log! Samuel.logger.add(log_level, log_message) end protected def log_message bold = "\e[1m" blue = "\e[34m" underline = "\e[4m" reset = "\e[0m" " #{bold}#{blue}#{underline}#{label} request (#{milliseconds}ms) " + "#{response_summary}#{reset} #{method} #{uri}" end def milliseconds (@seconds * 1000).round end def uri "#{scheme}://#{host}#{port_if_not_default}#{path}#{'?' if query}#{filtered_query}" end def label return Samuel.config[:label] if Samuel.config[:label] default = lambda { ["", "HTTP"] } Samuel.config[:labels].detect(default) { |domain, label| host.include?(domain) }[1] end def response_summary if @response.is_a?(Exception) @response.class else "[#{status_code} #{status_message}]" end end def log_level error? ? Logger::WARN : Logger::INFO end def ssl? scheme == 'https' end def filtered_query return "" if query.nil? patterns = [Samuel.config[:filtered_params]].flatten patterns.map { |pattern| pattern_for_regex = Regexp.escape(pattern.to_s) [/([^&]*#{pattern_for_regex}[^&=]*)=(?:[^&]+)/, '\1=[FILTERED]'] }.inject(query) { |filtered, filter| filtered.gsub(*filter) } end def port_if_not_default if (!ssl? && port == 80) || (ssl? && port == 443) "" else ":#{port}" end end end end end ruby-samuel-0.3.3/lib/samuel/log_entries/http_client.rb000066400000000000000000000010131215263324100231640ustar00rootroot00000000000000module Samuel module LogEntries class HttpClient < Base extend Forwardable def_delegators :"@request.header.request_uri", :host, :path, :query, :scheme, :port def method @request.header.request_method end def status_code @response.status end def status_message @response.header.reason_phrase.strip end def error? @response.is_a?(Exception) || @response.status.to_s =~ /^(4|5)/ end end end end ruby-samuel-0.3.3/lib/samuel/log_entries/net_http.rb000066400000000000000000000014201215263324100224760ustar00rootroot00000000000000module Samuel module LogEntries class NetHttp < Base def host @http.address end def path @request.path.split("?")[0] end def query @request.path.split("?")[1] end def scheme @http.use_ssl? ? "https" : "http" end def port @http.port end def method @request.method.to_s.upcase end def status_code @response.code end def status_message @response.message.strip end def error? error_classes = %w(Exception Net::HTTPClientError Net::HTTPServerError) response_ancestors = @response.class.ancestors.map { |a| a.to_s } (error_classes & response_ancestors).any? end end end end ruby-samuel-0.3.3/metadata.yml000066400000000000000000000103301215263324100162610ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: samuel version: !ruby/object:Gem::Version hash: 21 prerelease: segments: - 0 - 3 - 3 version: 0.3.3 platform: ruby authors: - Chris Kampmeier autorequire: bindir: bin cert_chain: [] date: 2011-11-10 00:00:00 -08:00 default_executable: dependencies: - !ruby/object:Gem::Dependency name: shoulda prerelease: false requirement: &id001 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 37 segments: - 2 - 11 - 3 version: 2.11.3 type: :development version_requirements: *id001 - !ruby/object:Gem::Dependency name: mocha prerelease: false requirement: &id002 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 55 segments: - 0 - 10 - 0 version: 0.10.0 type: :development version_requirements: *id002 - !ruby/object:Gem::Dependency name: httpclient prerelease: false requirement: &id003 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 1 segments: - 2 - 2 - 3 version: 2.2.3 type: :development version_requirements: *id003 - !ruby/object:Gem::Dependency name: fakeweb prerelease: false requirement: &id004 !ruby/object:Gem::Requirement none: false requirements: - - ~> - !ruby/object:Gem::Version hash: 9 segments: - 1 - 3 version: "1.3" type: :development version_requirements: *id004 - !ruby/object:Gem::Dependency name: rcov prerelease: false requirement: &id005 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 45 segments: - 0 - 9 - 11 version: 0.9.11 type: :development version_requirements: *id005 - !ruby/object:Gem::Dependency name: yard prerelease: false requirement: &id006 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 5 segments: - 0 - 7 - 3 version: 0.7.3 type: :development version_requirements: *id006 - !ruby/object:Gem::Dependency name: rake prerelease: false requirement: &id007 !ruby/object:Gem::Requirement none: false requirements: - - "=" - !ruby/object:Gem::Version hash: 11 segments: - 0 - 9 - 2 - 2 version: 0.9.2.2 type: :development version_requirements: *id007 description: An automatic logger for HTTP requests in Ruby, supporting the Net::HTTP and HTTPClient client libraries. email: chris@kampers.net executables: [] extensions: [] extra_rdoc_files: - LICENSE - README.rdoc files: - .document - .gitignore - Gemfile - LICENSE - README.rdoc - Rakefile - lib/samuel.rb - lib/samuel/diary.rb - lib/samuel/driver_patches/http_client.rb - lib/samuel/driver_patches/net_http.rb - lib/samuel/loader.rb - lib/samuel/log_entries/base.rb - lib/samuel/log_entries/http_client.rb - lib/samuel/log_entries/net_http.rb - samuel.gemspec - test/http_client_test.rb - test/loader_test.rb - test/net_http_test.rb - test/samuel_test.rb - test/test_helper.rb - test/thread_test.rb has_rdoc: true homepage: http://github.com/chrisk/samuel licenses: [] post_install_message: rdoc_options: - --charset=UTF-8 require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: samuel rubygems_version: 1.6.2 signing_key: specification_version: 3 summary: An automatic logger for HTTP requests in Ruby test_files: - test/http_client_test.rb - test/loader_test.rb - test/net_http_test.rb - test/samuel_test.rb - test/test_helper.rb - test/thread_test.rb ruby-samuel-0.3.3/samuel.gemspec000066400000000000000000000036441215263324100166230ustar00rootroot00000000000000# encoding: utf-8 Gem::Specification.new do |s| s.name = %q{samuel} s.version = "0.3.3" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Chris Kampmeier"] s.date = %q{2011-11-10} s.description = %q{An automatic logger for HTTP requests in Ruby, supporting the Net::HTTP and HTTPClient client libraries.} s.email = %q{chris@kampers.net} s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] s.files = [ ".document", ".gitignore", "Gemfile", "LICENSE", "README.rdoc", "Rakefile", "lib/samuel.rb", "lib/samuel/diary.rb", "lib/samuel/driver_patches/http_client.rb", "lib/samuel/driver_patches/net_http.rb", "lib/samuel/loader.rb", "lib/samuel/log_entries/base.rb", "lib/samuel/log_entries/http_client.rb", "lib/samuel/log_entries/net_http.rb", "samuel.gemspec", "test/http_client_test.rb", "test/loader_test.rb", "test/net_http_test.rb", "test/samuel_test.rb", "test/test_helper.rb", "test/thread_test.rb" ] s.homepage = %q{http://github.com/chrisk/samuel} s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.rubyforge_project = %q{samuel} s.rubygems_version = %q{1.3.5} s.summary = %q{An automatic logger for HTTP requests in Ruby} s.test_files = [ "test/http_client_test.rb", "test/loader_test.rb", "test/net_http_test.rb", "test/samuel_test.rb", "test/test_helper.rb", "test/thread_test.rb" ] s.specification_version = 3 s.add_development_dependency("shoulda", ["2.11.3"]) s.add_development_dependency("mocha", ["0.10.0"]) s.add_development_dependency("httpclient", ["2.2.3"]) s.add_development_dependency("fakeweb", ["~> 1.3"]) s.add_development_dependency("rcov", ["0.9.11"]) s.add_development_dependency("yard", ["0.7.3"]) s.add_development_dependency("rake", ["0.9.2.2"]) end ruby-samuel-0.3.3/test/000077500000000000000000000000001215263324100147405ustar00rootroot00000000000000ruby-samuel-0.3.3/test/http_client_test.rb000066400000000000000000000053741215263324100206520ustar00rootroot00000000000000require 'test_helper' class HttpClientTest < Test::Unit::TestCase context "making an HTTPClient request" do setup { setup_test_logger start_test_server Samuel.reset_config } teardown { teardown_test_logger } context "to GET http://localhost:8000/, responding with a 200 in 53ms" do setup do now = Time.now Samuel::Diary.stubs(:current_time).returns(now, now + 0.053) HTTPClient.get("http://localhost:8000/") end should_log_lines 1 should_log_at_level :info should_log_including "HTTP request" should_log_including "(53ms)" should_log_including "[200 OK]" should_log_including "GET http://localhost:8000/" end context "using PUT" do setup do HTTPClient.put("http://localhost:8000/books/1", "test=true") end should_log_including "PUT http://localhost:8000/books/1" end context "using an asynchronous POST" do setup do body = "title=Infinite%20Jest" client = HTTPClient.new connection = client.post_async("http://localhost:8000/books", body) sleep 0.1 until connection.finished? end should_log_including "POST http://localhost:8000/books" end context "that raises" do setup do begin HTTPClient.get("http://localhost:8001/") rescue Errno::ECONNREFUSED => @exception end end should_log_at_level :warn should_log_including "HTTP request" should_log_including "GET http://localhost:8001/" should_log_including "Errno::ECONNREFUSED" should_log_including %r|\d+ms| should_raise_exception Errno::ECONNREFUSED end context "using an asynchronous GET that raises" do setup do begin client = HTTPClient.new connection = client.get_async("http://localhost:8001/") sleep 0.1 until connection.finished? connection.pop rescue Errno::ECONNREFUSED => @exception end end should_log_at_level :warn should_log_including "HTTP request" should_log_including "GET http://localhost:8001/" should_log_including "Errno::ECONNREFUSED" should_log_including %r|\d+ms| should_raise_exception Errno::ECONNREFUSED end context "that responds with a 400-level code" do setup do HTTPClient.get("http://localhost:8000/test?404") end should_log_at_level :warn should_log_including "[404 Not Found]" end context "that responds with a 500-level code" do setup do HTTPClient.get("http://localhost:8000/test?500") end should_log_at_level :warn should_log_including "[500 Internal Server Error]" end end end ruby-samuel-0.3.3/test/loader_test.rb000066400000000000000000000036001215263324100175710ustar00rootroot00000000000000require 'test_helper' class LoaderTest < Test::Unit::TestCase def capture_output(code = "") requires = @requires.map { |lib| "require '#{lib}';" }.join(' ') samuel_dir = "#{File.dirname(__FILE__)}/../lib" `#{ruby_path} -I#{samuel_dir} -e "#{requires} #{code}" 2>&1` end context "loading Samuel" do setup do start_test_server @requires = ['samuel'] end context "when no HTTP drivers are loaded" do should "automatically load Net::HTTP" do output = capture_output "puts defined?(Net::HTTP)" assert_equal "constant", output.strip end should "successfully log a Net::HTTP request" do output = capture_output "Net::HTTP.get(URI.parse('http://localhost:8000'))" assert_match %r[HTTP request], output end should "not load HTTPClient" do output = capture_output "puts 'good' unless defined?(HTTPClient)" assert_equal "good", output.strip end end context "when Net::HTTP is already loaded" do setup { @requires.unshift('net/http') } should "successfully log a Net::HTTP request" do output = capture_output "Net::HTTP.get(URI.parse('http://localhost:8000'))" assert_match %r[HTTP request], output end should "not load HTTPClient" do output = capture_output "puts 'good' unless defined?(HTTPClient)" assert_match "good", output.strip end end context "when HTTPClient is already loaded" do setup { @requires.unshift('rubygems', 'httpclient') } should "successfully log an HTTPClient request" do output = capture_output "HTTPClient.get('http://localhost:8000')" assert_match %r[HTTP request], output end should "not load Net::HTTP" do output = capture_output "puts 'good' unless defined?(Net::HTTP)" assert_match "good", output.strip end end end end ruby-samuel-0.3.3/test/net_http_test.rb000066400000000000000000000176151215263324100201630ustar00rootroot00000000000000require 'test_helper' class RequestTest < Test::Unit::TestCase context "making an HTTP request" do setup { setup_test_logger FakeWeb.clean_registry Samuel.reset_config } teardown { teardown_test_logger } context "to GET http://example.com/test, responding with a 200 in 53ms" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) now = Time.now Samuel::Diary.stubs(:current_time).returns(now, now + 0.053) open "http://example.com/test" end should_log_lines 1 should_log_at_level :info should_log_including "HTTP request" should_log_including "(53ms)" should_log_including "[200 OK]" should_log_including "GET http://example.com/test" end context "on a non-standard port" do setup do FakeWeb.register_uri(:get, "http://example.com:8080/test", :status => [200, "OK"]) open "http://example.com:8080/test" end should_log_including "GET http://example.com:8080/test" end context "with SSL" do setup do FakeWeb.register_uri(:get, "https://example.com/test", :status => [200, "OK"]) open "https://example.com/test" end should_log_including "HTTP request" should_log_including "GET https://example.com/test" end context "with SSL on a non-standard port" do setup do FakeWeb.register_uri(:get, "https://example.com:80/test", :status => [200, "OK"]) open "https://example.com:80/test" end should_log_including "HTTP request" should_log_including "GET https://example.com:80/test" end context "that raises" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :exception => Errno::ECONNREFUSED) begin Net::HTTP.start("example.com") { |http| http.get("/test") } rescue Exception => @exception end end should_log_at_level :warn should_log_including "HTTP request" should_log_including "GET http://example.com/test" should_log_including "Errno::ECONNREFUSED" should_log_including %r|\d+ms| should_raise_exception Errno::ECONNREFUSED end context "that raises a SocketError when connecting" do setup do FakeWeb.allow_net_connect = true begin http = Net::HTTP.new("example.com") # This is an implementation-dependent hack; it would be more correct # to stub out TCPSocket.open, but I can't get Mocha to make it raise # correctly. Maybe related to TCPSocket being native code? http.stubs(:connect_without_samuel).raises(SocketError) http.start { |h| h.get("/test") } rescue Exception => @exception end end teardown do FakeWeb.allow_net_connect = false end should_log_at_level :warn should_log_including "HTTP request" should_log_including "CONNECT http://example.com" should_log_including "SocketError" should_log_including %r|\d+ms| should_raise_exception SocketError end context "that responds with a 500-level code" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [502, "Bad Gateway"]) Net::HTTP.start("example.com") { |http| http.get("/test") } end should_log_at_level :warn end context "that responds with a 400-level code" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [404, "Not Found"]) Net::HTTP.start("example.com") { |http| http.get("/test") } end should_log_at_level :warn end context "inside a configuration block with :label => 'Example'" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.with_config :label => "Example" do open "http://example.com/test" end end should_log_including "Example request" should_have_config_afterwards_including :labels => {}, :label => nil end context "inside a configuration block with :filter_params" do setup do FakeWeb.register_uri(:get, "http://example.com/test?password=secret&username=chrisk", :status => [200, "OK"]) @uri = "http://example.com/test?password=secret&username=chrisk" end context "=> :password" do setup { Samuel.with_config(:filtered_params => :password) { open @uri } } should_log_including "http://example.com/test?password=[FILTERED]&username=chrisk" end context "=> :as" do setup { Samuel.with_config(:filtered_params => :ass) { open @uri } } should_log_including "http://example.com/test?password=[FILTERED]&username=chrisk" end context "=> ['pass', 'name']" do setup { Samuel.with_config(:filtered_params => %w(pass name)) { open @uri } } should_log_including "http://example.com/test?password=[FILTERED]&username=[FILTERED]" end end context "with a global config including :label => 'Example'" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.config[:label] = "Example" open "http://example.com/test" end should_log_including "Example request" should_have_config_afterwards_including :labels => {}, :label => "Example" end context "with a global config including :label => 'Example' but inside config block that changes it to 'Example 2'" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.config[:label] = "Example" Samuel.with_config(:label => "Example 2") { open "http://example.com/test" } end should_log_including "Example 2 request" should_have_config_afterwards_including :labels => {}, :label => "Example" end context "inside a config block of :label => 'Example 2' nested inside a config block of :label => 'Example'" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.with_config :label => "Example" do Samuel.with_config :label => "Example 2" do open "http://example.com/test" end end end should_log_including "Example 2 request" should_have_config_afterwards_including :labels => {}, :label => nil end context "wth a global config including :labels => {'example.com' => 'Example'} but inside a config block of :label => 'Example 3' nested inside a config block of :label => 'Example 2'" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.config[:labels] = {'example.com' => 'Example'} Samuel.with_config :label => "Example 2" do Samuel.with_config :label => "Example 3" do open "http://example.com/test" end end end should_log_including "Example 3 request" should_have_config_afterwards_including :labels => {'example.com' => 'Example'}, :label => nil end context "with a global config including :labels => {'example.com' => 'Example API'}" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.config[:labels] = {'example.com' => 'Example API'} open "http://example.com/test" end should_log_including "Example API request" end context "with a global config including :labels => {'example.org' => 'Example API'} but making a request to example.com" do setup do FakeWeb.register_uri(:get, "http://example.com/test", :status => [200, "OK"]) Samuel.config[:labels] = {'example.org' => 'Example API'} open "http://example.com/test" end should_log_including "HTTP request" end end end ruby-samuel-0.3.3/test/samuel_test.rb000066400000000000000000000020001215263324100176020ustar00rootroot00000000000000require 'test_helper' class SamuelTest < Test::Unit::TestCase context "logger configuration" do setup do Samuel.logger = nil Object.send(:remove_const, :Rails) if Object.const_defined?(:Rails) end teardown do Samuel.logger = nil end context "when Rails's logger is available" do setup { Object.const_set(:Rails, stub(:logger => :mock_logger)) } should "use the same logger" do assert_equal :mock_logger, Samuel.logger end end context "when Rails's logger is not available" do should "use a new Logger instance pointed to STDOUT" do assert_instance_of Logger, Samuel.logger assert_equal STDOUT, Samuel.logger.instance_variable_get(:"@logdev").dev end end end context ".reset_config" do should "reset the config to default vaules" do Samuel.config = {:foo => "bar"} Samuel.reset_config assert_equal({:label => nil, :labels => {}, :filtered_params => []}, Samuel.config) end end end ruby-samuel-0.3.3/test/test_helper.rb000066400000000000000000000052631215263324100176110ustar00rootroot00000000000000require 'rubygems' require 'bundler' Bundler.setup require 'shoulda' require 'mocha' require 'net/http' require 'httpclient' require 'open-uri' require 'fakeweb' require 'webrick' require 'samuel' FakeWeb.allow_net_connect = false class Test::Unit::TestCase TEST_LOG_PATH = File.join(File.dirname(__FILE__), 'test.log') def self.should_log_lines(expected_count) should "log #{expected_count} line#{'s' unless expected_count == 1}" do lines = File.readlines(TEST_LOG_PATH) assert_equal expected_count, lines.length end end def self.should_log_including(what) should "log a line including #{what.inspect}" do contents = File.read(TEST_LOG_PATH) if what.is_a?(Regexp) assert_match what, contents else assert contents.include?(what), "Expected #{contents.inspect} to include #{what.inspect}" end end end def self.should_log_at_level(level) level = level.to_s.upcase should "log at the #{level} level" do assert File.read(TEST_LOG_PATH).include?(" #{level} -- :") end end def self.should_raise_exception(klass) should "raise an #{klass} exception" do @exception = nil if !defined?(@exception) assert @exception.is_a?(klass) end end def self.should_have_config_afterwards_including(config) config.each_pair do |key, value| should "continue afterwards with Samuel.config[#{key.inspect}] set to #{value.inspect}" do assert_equal value, Samuel.config[key] end end end # The path to the current ruby interpreter. Adapted from Rake's FileUtils. def ruby_path ext = ((RbConfig::CONFIG['ruby_install_name'] =~ /\.(com|cmd|exe|bat|rb|sh)$/) ? "" : RbConfig::CONFIG['EXEEXT']) File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + ext).sub(/.*\s.*/m, '"\&"') end def setup_test_logger FileUtils.rm_rf TEST_LOG_PATH FileUtils.touch TEST_LOG_PATH Samuel.logger = Logger.new(TEST_LOG_PATH) end def teardown_test_logger FileUtils.rm_rf TEST_LOG_PATH end def start_test_server return if defined?(@@server) @@server = WEBrick::HTTPServer.new( :Port => 8000, :AccessLog => [], :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN) ) @@server.mount "/", ResponseCodeServer at_exit { @@server.shutdown } Thread.new { @@server.start } end end class ResponseCodeServer < WEBrick::HTTPServlet::AbstractServlet def do_GET(request, response) response_code = request.query_string.nil? ? 200 : request.query_string.to_i response.status = response_code end alias_method :do_POST, :do_GET alias_method :do_PUT, :do_GET alias_method :do_DELETE, :do_GET end ruby-samuel-0.3.3/test/thread_test.rb000066400000000000000000000015111215263324100175710ustar00rootroot00000000000000require 'test_helper' class ThreadTest < Test::Unit::TestCase context "when logging multiple requests at once" do setup do @log = StringIO.new Samuel.logger = Logger.new(@log) FakeWeb.register_uri(:get, /example\.com/, :status => [200, "OK"]) threads = [] 5.times do |i| threads << Thread.new(i) do |n| Samuel.with_config :label => "Example #{n}" do Thread.pass open "http://example.com/#{n}" end end end threads.each { |t| t.join } @log.rewind end should "not let configuration blocks interfere with eachother" do @log.each_line do |line| matches = %r|Example (\d+).*example\.com/(\d+)|.match(line) assert_not_nil matches assert_equal matches[1], matches[2] end end end end