zuora_connectD 1.7.09

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +38 -0
  4. data/app/assets/javascripts/zuora_connect/api/v1/app_instance.js +2 -0
  5. data/app/assets/javascripts/zuora_connect/application.js +13 -0
  6. data/app/assets/stylesheets/zuora_connect/api/v1/app_instance.css +4 -0
  7. data/app/assets/stylesheets/zuora_connect/application.css +15 -0
  8. data/app/controllers/zuora_connect/admin/tenant_controller.rb +11 -0
  9. data/app/controllers/zuora_connect/api/v1/app_instance_controller.rb +45 -0
  10. data/app/controllers/zuora_connect/application_controller.rb +8 -0
  11. data/app/controllers/zuora_connect/static_controller.rb +32 -0
  12. data/app/helpers/zuora_connect/api/v1/app_instance_helper.rb +4 -0
  13. data/app/helpers/zuora_connect/application_helper.rb +5 -0
  14. data/app/models/zuora_connect/app_instance.rb +5 -0
  15. data/app/models/zuora_connect/app_instance_base.rb +811 -0
  16. data/app/models/zuora_connect/login.rb +36 -0
  17. data/app/models/zuora_connect/telegraf.rb +88 -0
  18. data/app/views/layouts/zuora_connect/application.html.erb +14 -0
  19. data/app/views/sql/refresh_aggregate_table.txt +84 -0
  20. data/app/views/zuora_connect/static/invalid_app_instance_error.html.erb +65 -0
  21. data/app/views/zuora_connect/static/session_error.html.erb +63 -0
  22. data/config/initializers/apartment.rb +95 -0
  23. data/config/initializers/object_method_hooks.rb +27 -0
  24. data/config/initializers/postgresql_adapter.rb +32 -0
  25. data/config/initializers/prometheus.rb +41 -0
  26. data/config/initializers/redis.rb +10 -0
  27. data/config/initializers/resque.rb +6 -0
  28. data/config/initializers/to_bool.rb +24 -0
  29. data/config/initializers/unicorn.rb +9 -0
  30. data/config/routes.rb +13 -0
  31. data/db/migrate/20100718151733_create_connect_app_instances.rb +9 -0
  32. data/db/migrate/20101024162319_add_tokens_to_app_instance.rb +6 -0
  33. data/db/migrate/20101024220705_add_token_to_app_instance.rb +5 -0
  34. data/db/migrate/20110131211919_add_sessions_table.rb +13 -0
  35. data/db/migrate/20110411200303_add_expiration_to_app_instance.rb +5 -0
  36. data/db/migrate/20110413191512_add_new_api_token.rb +5 -0
  37. data/db/migrate/20110503003602_add_catalog_data_to_app_instance.rb +6 -0
  38. data/db/migrate/20110503003603_add_catalog_mappings_to_app_instance.rb +5 -0
  39. data/db/migrate/20110503003604_catalog_default.rb +5 -0
  40. data/db/migrate/20180301052853_add_catalog_attempted_at.rb +5 -0
  41. data/lib/metrics/influx/point_value.rb +79 -0
  42. data/lib/metrics/net.rb +218 -0
  43. data/lib/middleware/metrics_middleware.rb +110 -0
  44. data/lib/resque/additions.rb +53 -0
  45. data/lib/resque/dynamic_queues.rb +142 -0
  46. data/lib/resque/self_lookup.rb +19 -0
  47. data/lib/resque/silence_done.rb +71 -0
  48. data/lib/tasks/zuora_connect_tasks.rake +24 -0
  49. data/lib/zuora_connectD.rb +41 -0
  50. data/lib/zuora_connectD/configuration.rb +52 -0
  51. data/lib/zuora_connectD/controllers/helpers.rb +165 -0
  52. data/lib/zuora_connectD/engine.rb +30 -0
  53. data/lib/zuora_connectD/exceptions.rb +67 -0
  54. data/lib/zuora_connectD/railtie.rb +59 -0
  55. data/lib/zuora_connectD/version.rb +3 -0
  56. data/lib/zuora_connectD/views/helpers.rb +9 -0
  57. data/test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb +13 -0
  58. data/test/dummy/README.rdoc +28 -0
  59. data/test/dummy/Rakefile +6 -0
  60. data/test/dummy/app/assets/javascripts/application.js +13 -0
  61. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  62. data/test/dummy/app/controllers/application_controller.rb +5 -0
  63. data/test/dummy/app/helpers/application_helper.rb +2 -0
  64. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  65. data/test/dummy/bin/bundle +3 -0
  66. data/test/dummy/bin/rails +4 -0
  67. data/test/dummy/bin/rake +4 -0
  68. data/test/dummy/bin/setup +29 -0
  69. data/test/dummy/config.ru +4 -0
  70. data/test/dummy/config/application.rb +26 -0
  71. data/test/dummy/config/boot.rb +5 -0
  72. data/test/dummy/config/database.yml +25 -0
  73. data/test/dummy/config/environment.rb +5 -0
  74. data/test/dummy/config/environments/development.rb +41 -0
  75. data/test/dummy/config/environments/production.rb +79 -0
  76. data/test/dummy/config/environments/test.rb +42 -0
  77. data/test/dummy/config/initializers/assets.rb +11 -0
  78. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  80. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  81. data/test/dummy/config/initializers/inflections.rb +16 -0
  82. data/test/dummy/config/initializers/mime_types.rb +4 -0
  83. data/test/dummy/config/initializers/session_store.rb +3 -0
  84. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  85. data/test/dummy/config/locales/en.yml +23 -0
  86. data/test/dummy/config/routes.rb +4 -0
  87. data/test/dummy/config/secrets.yml +22 -0
  88. data/test/dummy/db/development.sqlite3 +0 -0
  89. data/test/dummy/db/test.sqlite3 +0 -0
  90. data/test/dummy/log/development.log +2 -0
  91. data/test/dummy/log/test.log +0 -0
  92. data/test/dummy/public/404.html +67 -0
  93. data/test/dummy/public/422.html +67 -0
  94. data/test/dummy/public/500.html +66 -0
  95. data/test/dummy/public/favicon.ico +0 -0
  96. data/test/fixtures/zuora_connect/app_instances.yml +11 -0
  97. data/test/integration/navigation_test.rb +8 -0
  98. data/test/lib/generators/zuora_connect/datatable_generator_test.rb +16 -0
  99. data/test/models/zuora_connect/app_instance_test.rb +9 -0
  100. data/test/test_helper.rb +21 -0
  101. data/test/zuora_connect_test.rb +7 -0
  102. metadata +416 -0
@@ -0,0 +1,27 @@
1
+ class Object
2
+ def self.method_hook(*args)
3
+ options = args.extract_options!
4
+ return unless (options[:before].present? or options[:after].present?)
5
+ args.each do |method_name|
6
+ old_method = instance_method(method_name) rescue next
7
+
8
+ define_method(method_name) do |*args|
9
+ # invoke before callback
10
+ if options[:before].present?
11
+ options[:before].is_a?(Proc) ? options[:before].call(method_name, self):
12
+ send(options[:before], method_name)
13
+ end
14
+
15
+ # you can modify the code to call after callback
16
+ # only when the old method returns true etc..
17
+ old_method.bind(self).call(*args)
18
+
19
+ # invoke after callback
20
+ if options[:after].present?
21
+ options[:after].is_a?(Proc) ? options[:after].call(method_name, self):
22
+ send(options[:after], method_name)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter < AbstractAdapter
4
+ private
5
+ def load_additional_types(type_map, oids = nil)
6
+ initializer = OID::TypeMapInitializer.new(type_map)
7
+ if supports_ranges?
8
+ query = <<-SQL
9
+ SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
10
+ FROM pg_type as t
11
+ LEFT JOIN pg_range as r ON oid = rngtypid
12
+ SQL
13
+ else
14
+ query = <<-SQL
15
+ SELECT DISTINCT on (t.typname) t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
16
+ FROM pg_type as t
17
+ SQL
18
+ end
19
+
20
+ if oids
21
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
22
+ else
23
+ query += initializer.query_conditions_for_initial_load(type_map)
24
+ end
25
+
26
+ execute_and_clear(query, "SCHEMA", []) do |records|
27
+ initializer.run(records)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,41 @@
1
+ if defined? Prometheus
2
+ module Prometheus
3
+ require "zuora_connectD/version"
4
+ require "zuora_api/version"
5
+
6
+ app_name = ENV['DEIS_APP'].present? ? "#{ENV['DEIS_APP']}" : "#{Rails.application.class.parent_name}"
7
+ # Create a default Prometheus registry for our metrics.
8
+ prometheus = Prometheus::Client.registry
9
+
10
+ # Create your metrics.
11
+ ZUORA_VERSION = Prometheus::Client::Gauge.new(:zuora_version, 'The current Zuora Gem version.')
12
+ CONNECT_VERSION = Prometheus::Client::Gauge.new(:gem_version, 'The current Connect Gem version.')
13
+ RAILS_VERSION = Prometheus::Client::Gauge.new(:rails_version, 'The current Rails version.')
14
+ RUBY_V = Prometheus::Client::Gauge.new(:ruby_version, 'The current Ruby version.')
15
+
16
+ # Register your metrics with the registry we previously created.
17
+ prometheus.register(ZUORA_VERSION);ZUORA_VERSION.set({version:ZuoraAPI::VERSION,name:app_name},0)
18
+ prometheus.register(CONNECT_VERSION);CONNECT_VERSION.set({version:ZuoraConnect::VERSION,name:app_name},0)
19
+ prometheus.register(RAILS_VERSION);RAILS_VERSION.set({version:Rails.version,name:app_name},0)
20
+ prometheus.register(RUBY_V);RUBY_V.set({version:RUBY_VERSION,name:app_name},0)
21
+
22
+ # Do they have resque jobs?
23
+ if defined? Resque.redis
24
+ REDIS_CONNECTION = Prometheus::Client::Gauge.new(:redis_connection, 'The status of the redis connection, 0 or 1')
25
+ FINISHED_JOBS = Prometheus::Client::Gauge.new(:finished_jobs, 'Done resque jobs')
26
+ WORKERS = Prometheus::Client::Gauge.new(:workers, 'Total resque workers')
27
+ ACTIVE_WORKERS = Prometheus::Client::Gauge.new(:active_workers, 'Active resque workers')
28
+ FAILED_JOBS = Prometheus::Client::Gauge.new(:failed_jobs, 'Failed resque jobs')
29
+ PENDING_JOBS = Prometheus::Client::Gauge.new(:pending_jobs, 'Pending resque jobs')
30
+
31
+ prometheus.register(REDIS_CONNECTION)
32
+ prometheus.register(FINISHED_JOBS)
33
+ prometheus.register(ACTIVE_WORKERS)
34
+ prometheus.register(WORKERS)
35
+ prometheus.register(FAILED_JOBS)
36
+ prometheus.register(PENDING_JOBS)
37
+
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ redis_url = ENV["REDIS_URL"].present? ? ENV["REDIS_URL"] : defined?(Rails.application.secrets.redis) ? Rails.application.secrets.redis : 'redis://localhost:6379/1'
2
+ if defined?(Redis.current)
3
+ Redis.current = Redis.new(:url => redis_url, :timeout => 10, :reconnect_attempts => 2)
4
+ if defined?(Resque.redis)
5
+ Resque.redis = Redis.current
6
+ end
7
+ end
8
+ if defined?(RedisBrowser)
9
+ RedisBrowser.configure("connections" => { "default" => { "url" => redis_url } })
10
+ end
@@ -0,0 +1,6 @@
1
+ if defined?(Resque::Worker)
2
+ Resque.send(:extend, Resque::Additions)
3
+ Resque::Worker.send(:include, Resque::DynamicQueues)
4
+ Resque::Worker.send(:include, Resque::SilenceDone) if ZuoraConnect.configuration.silencer_resque_finish == true
5
+ Resque::Job.send(:include, Resque::SelfLookup)
6
+ end
@@ -0,0 +1,24 @@
1
+ class String
2
+ def to_bool
3
+ return self if (self.class == TrueClass || self.class == FalseClass)
4
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
5
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
6
+ return false
7
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
8
+ end
9
+ end
10
+ class TrueClass
11
+ def to_bool
12
+ return self
13
+ end
14
+ end
15
+ class FalseClass
16
+ def to_bool
17
+ return self
18
+ end
19
+ end
20
+ class NilClass
21
+ def to_bool
22
+ return false
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ if defined?(Unicorn::WorkerKiller)
2
+ Unicorn::WorkerKiller.module_eval do
3
+ self.singleton_class.send(:alias_method, :kill_self_old, :kill_self)
4
+ def self.kill_self(logger, start_time)
5
+ self.kill_self_old(logger, start_time)
6
+ ZuoraConnect::AppInstance.write_to_telegraf(direction: 'Unicorn-Killer', tags: {app_instance: 0}, values: {kill: 1})
7
+ end
8
+ end
9
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ ZuoraConnect::Engine.routes.draw do
2
+ get '/health' => 'static#health'
3
+ get '/internal/data' => 'static#metrics'
4
+ get '/invalid_session' => 'static#session_error', :as => :invalid_session
5
+ get '/invalid_instance' => "static#invalid_app_instance_error", :as => :invalid_instance
6
+ namespace :api do
7
+ namespace :v1 do
8
+ resources :app_instance, :only => [:index], defaults: {format: :json} do
9
+ match "drop", via: [:get, :post], on: :collection
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ class CreateConnectAppInstances < ActiveRecord::Migration
2
+ def change
3
+ if !ActiveRecord::Base.connection.table_exists?('zuora_connect_app_instances')
4
+ create_table :zuora_connect_app_instances do |t|
5
+ t.timestamps null: false
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ class AddTokensToAppInstance < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :access_token, :string unless column_exists? :zuora_connect_app_instances, :access_token
4
+ add_column :zuora_connect_app_instances, :refresh_token, :string unless column_exists? :zuora_connect_app_instances, :refresh_token
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddTokenToAppInstance < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :token, :string unless column_exists? :zuora_connect_app_instances, :token
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ class AddSessionsTable < ActiveRecord::Migration
2
+ def change
3
+ if !ActiveRecord::Base.connection.table_exists?('sessions')
4
+ create_table :sessions do |t|
5
+ t.string :session_id, :null => false
6
+ t.text :data
7
+ t.timestamps
8
+ end
9
+ add_index :sessions, :session_id, :unique => true
10
+ add_index :sessions, :updated_at
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddExpirationToAppInstance < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :oauth_expires_at, :datetime unless column_exists? :zuora_connect_app_instances, :oauth_expires_at
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddNewApiToken < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :api_token, :string unless column_exists? :zuora_connect_app_instances, :api_token
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddCatalogDataToAppInstance < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :catalog_updated_at, :datetime unless column_exists? :zuora_connect_app_instances, :catalog_updated_at
4
+ add_column :zuora_connect_app_instances, :catalog, :jsonb, default: {} unless column_exists? :zuora_connect_app_instances, :catalog
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddCatalogMappingsToAppInstance < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :catalog_mapping, :jsonb, default: {} unless column_exists? :zuora_connect_app_instances, :catalog_mapping
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class CatalogDefault < ActiveRecord::Migration
2
+ def change
3
+ change_column :zuora_connect_app_instances, :catalog, :jsonb, default: {}
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddCatalogAttemptedAt < ActiveRecord::Migration
2
+ def change
3
+ add_column :zuora_connect_app_instances, :catalog_update_attempt_at, :datetime unless column_exists? :zuora_connect_app_instances, :catalog_update_attempt_at
4
+ end
5
+ end
@@ -0,0 +1,79 @@
1
+ module InfluxDB
2
+ # Convert data point to string using Line protocol
3
+ class PointValue
4
+ attr_reader :series, :values, :tags, :timestamp
5
+
6
+ def initialize(data)
7
+ @series = escape data[:series], :measurement
8
+ @values = escape_values data[:values]
9
+ @tags = escape_tags data[:tags]
10
+ @timestamp = data[:timestamp]
11
+ end
12
+
13
+ def dump
14
+ dump = @series.dup
15
+ dump << ",#{@tags}" if @tags
16
+ dump << " #{@values}"
17
+ dump << " #{@timestamp}" if @timestamp
18
+ dump
19
+ end
20
+
21
+ private
22
+
23
+ ESCAPES = {
24
+ measurement: [' '.freeze, ','.freeze],
25
+ tag_key: ['='.freeze, ' '.freeze, ','.freeze],
26
+ tag_value: ['='.freeze, ' '.freeze, ','.freeze],
27
+ field_key: ['='.freeze, ' '.freeze, ','.freeze, '"'.freeze],
28
+ field_value: ["\\".freeze, '"'.freeze],
29
+ }.freeze
30
+
31
+ private_constant :ESCAPES
32
+
33
+ def escape(str, type)
34
+ # rubocop:disable Layout/AlignParameters
35
+ str = str.encode "UTF-8".freeze, "UTF-8".freeze,
36
+ invalid: :replace,
37
+ undef: :replace,
38
+ replace: "".freeze
39
+ # rubocop:enable Layout/AlignParameters
40
+
41
+ ESCAPES[type].each do |ch|
42
+ str = str.gsub(ch) { "\\#{ch}" }
43
+ end
44
+ str
45
+ end
46
+
47
+ def escape_values(values)
48
+ return if values.nil?
49
+ values.map do |k, v|
50
+ key = escape(k.to_s, :field_key)
51
+ val = escape_value(v)
52
+ "#{key}=#{val}"
53
+ end.join(",".freeze)
54
+ end
55
+
56
+ def escape_value(value)
57
+ if value.is_a?(String)
58
+ '"'.freeze + escape(value, :field_value) + '"'.freeze
59
+ elsif value.is_a?(Integer)
60
+ "#{value}i"
61
+ else
62
+ value.to_s
63
+ end
64
+ end
65
+
66
+ def escape_tags(tags)
67
+ return if tags.nil?
68
+
69
+ tags = tags.map do |k, v|
70
+ key = escape(k.to_s, :tag_key)
71
+ val = escape(v.to_s, :tag_value)
72
+
73
+ "#{key}=#{val}" unless key == "".freeze || val == "".freeze
74
+ end.compact
75
+
76
+ tags.join(",") unless tags.empty?
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,218 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'set'
4
+
5
+ # Usage:
6
+ #
7
+ # require 'http_logger'
8
+ #
9
+ # == Setup logger
10
+ #
11
+ # HttpLogger.logger = Logger.new('/tmp/all.log')
12
+ # HttpLogger.log_headers = true
13
+ #
14
+ # == Do request
15
+ #
16
+ # res = Net::HTTP.start(url.host, url.port) { |http|
17
+ # http.request(req)
18
+ # }
19
+ # ...
20
+ #
21
+ # == View the log
22
+ #
23
+ # cat /tmp/all.log
24
+ class HttpLogger
25
+ class << self
26
+ attr_accessor :collapse_body_limit
27
+ attr_accessor :log_headers
28
+ attr_accessor :log_request_body
29
+ attr_accessor :log_response_body
30
+ attr_accessor :logger
31
+ attr_accessor :colorize
32
+ attr_accessor :ignore
33
+ attr_accessor :level
34
+ end
35
+
36
+ self.log_headers = false
37
+ self.log_request_body = true
38
+ self.log_response_body = true
39
+ self.colorize = true
40
+ self.collapse_body_limit = 5000
41
+ self.ignore = []
42
+ self.level = :debug
43
+
44
+ def self.perform(*args, &block)
45
+ instance.perform(*args, &block)
46
+ end
47
+
48
+ def self.instance
49
+ @instance ||= HttpLogger.new
50
+ end
51
+
52
+ def self.deprecate_config(option)
53
+ warn "Net::HTTP.#{option} is deprecated. Use HttpLogger.#{option} instead."
54
+ end
55
+
56
+ def perform(http, request, request_body)
57
+ start_time = Time.now
58
+ tags = {}
59
+ response = yield
60
+ rescue => ex
61
+ tags = tags.merge({error_type: ex.class})
62
+ raise
63
+ ensure
64
+ values = {response_time: ((Time.now - start_time)*1000).round(2)}
65
+ if require_logging?(http, request)
66
+ tags = tags.merge({endpoint: http.address, method: request.method})
67
+ log_request_url(http, request, start_time)
68
+ log_request_body(request)
69
+ log_request_headers(request)
70
+ if defined?(response) && response
71
+ tags = tags.merge({status: response.code.to_i})
72
+ log_response_code(response)
73
+ log_response_headers(response)
74
+ log_response_body(response.body)
75
+ end
76
+ ZuoraConnect::AppInstance.write_to_telegraf(direction: :outbound, tags: tags, values: values)
77
+ end
78
+ end
79
+
80
+ protected
81
+
82
+ def log_request_url(http, request, start_time)
83
+ ofset = Time.now - start_time
84
+ log("HTTP #{request.method} (%0.2fms)" % (ofset * 1000), request_url(http, request))
85
+ end
86
+
87
+ def request_url(http, request)
88
+ URI.decode("http#{"s" if http.use_ssl?}://#{http.address}:#{http.port}#{request.path}")
89
+ end
90
+
91
+ def log_request_headers(request)
92
+ if self.class.log_headers
93
+ request.each_capitalized { |k,v| log("HTTP request header", "#{k}: #{v}") }
94
+ end
95
+ end
96
+
97
+ HTTP_METHODS_WITH_BODY = Set.new(%w(POST PUT GET PATCH))
98
+
99
+ def log_request_body(request)
100
+ if self.class.log_request_body
101
+ if HTTP_METHODS_WITH_BODY.include?(request.method)
102
+ if (body = request.body) && !body.empty?
103
+ log("Request body", truncate_body(body))
104
+ end
105
+ end
106
+ end
107
+ end
108
+
109
+ def log_response_code(response)
110
+ log("Response status", "#{response.class} (#{response.code})")
111
+ end
112
+
113
+ def log_response_headers(response)
114
+ if self.class.log_headers
115
+ response.each_capitalized { |k,v| log("HTTP response header", "#{k}: #{v}") }
116
+ end
117
+ end
118
+
119
+ def log_response_body(body)
120
+ if self.class.log_response_body
121
+ if body.is_a?(Net::ReadAdapter)
122
+ log("Response body", "<impossible to log>")
123
+ else
124
+ if body && !body.empty?
125
+ log("Response body", truncate_body(body))
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def require_logging?(http, request)
132
+ self.logger && !ignored?(http, request) && (http.started? || fakeweb?(http, request))
133
+ end
134
+
135
+ def ignored?(http, request)
136
+ url = request_url(http, request)
137
+ self.class.ignore.any? do |pattern|
138
+ url =~ pattern
139
+ end
140
+ end
141
+
142
+ def fakeweb?(http, request)
143
+ return false unless defined?(::FakeWeb)
144
+ uri = ::FakeWeb::Utility.request_uri_as_string(http, request)
145
+ method = request.method.downcase.to_sym
146
+ ::FakeWeb.registered_uri?(method, uri)
147
+ end
148
+
149
+ def truncate_body(body)
150
+ if collapse_body_limit && collapse_body_limit > 0 && body && body.size >= collapse_body_limit
151
+ body_piece_size = collapse_body_limit / 2
152
+ body[0..body_piece_size] +
153
+ "\n\n<some data truncated>\n\n" +
154
+ body[(body.size - body_piece_size)..body.size]
155
+ else
156
+ body
157
+ end
158
+ end
159
+
160
+ def log(message, dump)
161
+ self.logger.send(self.class.level, format_log_entry(message, dump)) if Rails.env.to_sym == :development
162
+ end
163
+
164
+ def format_log_entry(message, dump = nil)
165
+ if self.class.colorize
166
+ message_color, dump_color = "4;32;1", "0;1"
167
+ log_entry = " \e[#{message_color}m#{message}\e[0m "
168
+ log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump
169
+ log_entry
170
+ else
171
+ "%s %s" % [message, dump]
172
+ end
173
+ end
174
+
175
+ def logger
176
+ self.class.logger
177
+ end
178
+
179
+ def collapse_body_limit
180
+ self.class.collapse_body_limit
181
+ end
182
+ end
183
+
184
+ class Net::HTTP
185
+
186
+ def self.log_headers=(value)
187
+ HttpLogger.deprecate_config("log_headers")
188
+ HttpLogger.log_headers = value
189
+ end
190
+
191
+ def self.colorize=(value)
192
+ HttpLogger.deprecate_config("colorize")
193
+ HttpLogger.colorize = value
194
+ end
195
+
196
+ def self.logger=(value)
197
+ HttpLogger.deprecate_config("logger")
198
+ HttpLogger.logger = value
199
+ end
200
+
201
+
202
+ alias_method :request_without_logging, :request
203
+
204
+ def request(request, body = nil, &block)
205
+ HttpLogger.perform(self, request, body) do
206
+ request_without_logging(request, body, &block)
207
+ end
208
+ end
209
+ end
210
+
211
+ if defined?(Rails)
212
+ if defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
213
+ # Rails3
214
+ ActiveSupport.on_load(:after_initialize) do
215
+ HttpLogger.logger = Rails.logger unless HttpLogger.logger
216
+ end
217
+ end
218
+ end