api_hammer 0.6.3 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 002d518d717450a30af8921565ffe152625133aa
4
- data.tar.gz: 9bae35c5c3917d005ec60b662923d3bcf69317e8
3
+ metadata.gz: 2efd3cea2d6c262bff720e983354d944f0de16da
4
+ data.tar.gz: 9611db21a814571891d2d17ed017c82d2048b52e
5
5
  SHA512:
6
- metadata.gz: 166eefca239f0809baf6a54eb4d0de4b43ae5f2944ac57dcdab980df6be1be21572868575d3af3a15b07380ef367811b4e005c047e491cceab6c8c66f5a291f4
7
- data.tar.gz: e60133dfd3a348b375e34ac6075435ce7e4784d8d8dae5ff68428398f2340ba78653f168eea849b82d9c3db086f4e3f2cfaef43fa2a2c7a9d8c901606bf38d4f
6
+ metadata.gz: 8b0dbb63040d700c2c137e24c4d84eb4804d2a234a68f3b02fdc001a78ff9364cb04975466e571bac801436be222174cd7fb7def879766c1af15afadddc1a093
7
+ data.tar.gz: 01d61498c3db5d7ef844b6c4c8bd4524a7f98c5d2b8bd738b3a8d5ac42e952c11eab9e85c48d794e9e9d1c8fdbdbff5f2317b78d95b8f4e3af954b35729228e9
@@ -1,3 +1,8 @@
1
+ # v0.7.1
2
+ - logstash filters for sidekiq, activesupport tags, and of course ApiHammer's request logging
3
+ - use i18n for errors and add error_message to response
4
+ - hc assumes http if no protocol specified
5
+
1
6
  # v0.6.3
2
7
  - add request role to the request logging
3
8
 
data/bin/hc CHANGED
@@ -94,6 +94,10 @@ end
94
94
 
95
95
  httpmethod, url, body = *ARGV
96
96
 
97
+ unless url['://']
98
+ url = 'http://' + url
99
+ end
100
+
97
101
  unless Faraday::Connection::METHODS.map{|m| m.to_s.downcase }.include?(httpmethod.downcase)
98
102
  abort "Unrecognized HTTP method given: #{httpmethod}\n\n" + opt_parser.help
99
103
  end
@@ -29,7 +29,7 @@ module ApiHammer::Rails
29
29
  key = parents.join('#')
30
30
  add_error = proc { |message| errors[key] << message unless errors[key].include?(message) }
31
31
  if subparams.nil?
32
- add_error.call("is required but was not provided")
32
+ add_error.call(I18n.t(:"errors.required_params.not_provided", :default => "%{key} is required but was not provided", :key => key))
33
33
  elsif check
34
34
  case check
35
35
  when Array
@@ -40,17 +40,17 @@ module ApiHammer::Rails
40
40
  check_required_params_helper(subcheck, subparams[key], errors, parents + [key])
41
41
  end
42
42
  else
43
- add_error.call("must be a Hash")
43
+ add_error.call(I18n.t(:"errors.required_params.must_be_hash", :default => "%{key} must be a Hash", :key => key))
44
44
  end
45
45
  when Class
46
46
  unless subparams.is_a?(check)
47
- add_error.call("must be a #{check.name}")
47
+ add_error.call(I18n.t(:"errors.required_params.must_be_type", :default => "%{key} must be a %{type}", :key => key, :type => check.name))
48
48
  end
49
49
  else
50
50
  if subparams.is_a?(Hash)
51
51
  check_required_params_helper(nil, subparams[check], errors, parents + [check])
52
52
  else
53
- add_error.call("must be a Hash")
53
+ add_error.call(I18n.t(:"errors.required_params.must_be_hash", :default => "%{key} must be a Hash", :key => key))
54
54
  end
55
55
  end
56
56
  end
@@ -44,7 +44,7 @@ module ApiHammer::Rails
44
44
  end
45
45
 
46
46
  # halt and render the given errors in the body on the 'errors' key
47
- def halt_error(status, errors, render_options = {})
47
+ def halt_error(status, errors, options = {})
48
48
  errors_as_json = errors.respond_to?(:as_json) ? errors.as_json : errors
49
49
  unless errors_as_json.is_a?(Hash)
50
50
  raise ArgumentError, "errors be an object representable in JSON as a Hash; got errors = #{errors.inspect}"
@@ -55,7 +55,19 @@ module ApiHammer::Rails
55
55
  unless errors_as_json.values.all? { |v| v.is_a?(Array) && v.all? { |e| e.is_a?(String) } }
56
56
  raise ArgumentError, "errors values must all be arrays of strings; got errors = #{errors.inspect}"
57
57
  end
58
- halt(status, {'errors' => errors}, render_options)
58
+ render_options = options.dup.with_indifferent_access
59
+ body = {'errors' => errors}
60
+ error_message = render_options.delete('error_message') || begin
61
+ error_values = errors.values.inject([], &:+)
62
+ if error_values.size <= 1
63
+ error_values.first
64
+ else
65
+ # sentencify with periods
66
+ error_values.map { |v| v =~ /\.\s*\z/ ? v : v + '.' }.join(' ')
67
+ end
68
+ end
69
+ body['error_message'] = error_message if error_message
70
+ halt(status, body, render_options)
59
71
  end
60
72
 
61
73
  # attempts to find and return the given model (an ActiveRecord::Base subclass) with the given attributes.
@@ -73,7 +85,11 @@ module ApiHammer::Rails
73
85
  attributes = find_attrs.map{|attr, val| "#{attr}: #{val}" }.join(", ")
74
86
  model_name = model.table_name
75
87
  model_name = model_name.singularize if model_name.respond_to?(:singularize)
76
- halt_error(options[:status], {model_name => ["Unknown #{model_name}! #{attributes}"]})
88
+ message = I18n.t(:"errors.unknown_record_with_attributes", :default => "Unknown %{model_name}! %{attributes}",
89
+ :model_name => model_name,
90
+ :attributes => attributes
91
+ )
92
+ halt_error(options[:status], {model_name => [message]})
77
93
  end
78
94
  record
79
95
  end
@@ -1,3 +1,3 @@
1
1
  module ApiHammer
2
- VERSION = "0.6.3"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -0,0 +1,27 @@
1
+ require "logstash/filters/base"
2
+ require "logstash/namespace"
3
+
4
+ class LogStash::Filters::ActiveSupportTags < LogStash::Filters::Base
5
+ config_name "active_support_tags"
6
+ milestone 1
7
+
8
+ config :consume, :validate => :boolean, :default => true
9
+ config :source, :validate => :string, :default => 'message'
10
+
11
+ public
12
+ def register
13
+ end
14
+
15
+ public
16
+ def filter(event)
17
+ as_tags = []
18
+ message = event[@source]
19
+ while message =~ /\A\[([^\]]+?)\]\s+/
20
+ as_tags << $1
21
+ message = $'
22
+ end
23
+ event['as_tags'] = as_tags if as_tags.any?
24
+
25
+ event[@source] = message if @consume
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require "logstash/filters/base"
2
+ require "logstash/namespace"
3
+
4
+ class LogStash::Filters::ApiHammerRequest < LogStash::Filters::Base
5
+ config_name "api_hammer_request"
6
+ milestone 1
7
+
8
+ config :consume, :validate => :boolean, :default => true
9
+ config :source, :validate => :string, :default => 'message'
10
+
11
+ public
12
+ def register
13
+ end
14
+
15
+ public
16
+ def filter(event)
17
+ # discard the request status line for humans - always followed by json which we'll parse
18
+ col = /[\e\[\dm]*/.source
19
+ human_request = [/\A/, /[<>]/, /\s/, /\d+/, / : /, /\w+/, / /, /[^\e]+/, / @ /, /[^\e]+/, /\z/].map(&:source).join(col)
20
+ event.cancel if event[@source] =~ /#{human_request}/
21
+
22
+ begin
23
+ parsed_message = JSON.parse(event[@source])
24
+ if @consume
25
+ # replace the source with a brief human-readable message
26
+ role = parsed_message['request_role']
27
+ dir = role == 'server' ? '<' : role == 'client' ? '>' : '*'
28
+ status = parsed_message['response'] && parsed_message['response']['status']
29
+ request_method = parsed_message['request'] && parsed_message['request']['method']
30
+ request_uri = parsed_message['request'] && parsed_message['request']['uri']
31
+ now_s = Time.now.strftime('%Y-%m-%d %H:%M:%S %Z')
32
+ event[@source] = "#{dir} #{status} : #{request_method} #{request_uri} @ #{now_s}"
33
+ end
34
+ rescue JSON::ParserError
35
+ nil
36
+ end
37
+
38
+ if parsed_message
39
+ if parsed_message.is_a?(Hash)
40
+ event.to_hash.update(parsed_message)
41
+ if parsed_message['processing'].is_a?(Hash) && parsed_message['processing']['began_at'].is_a?(Integer)
42
+ event['@timestamp'] = Time.at(parsed_message['processing']['began_at']).utc
43
+ end
44
+ else
45
+ event['parsed_message'] = parsed_message
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ require "logstash/filters/base"
2
+ require "logstash/namespace"
3
+ require 'rack'
4
+ require 'cgi'
5
+ require 'json'
6
+ class LogStash::Filters::RequestBodiesParsed < LogStash::Filters::Base
7
+ config_name "request_bodies_parsed"
8
+ milestone 1
9
+
10
+ public
11
+ def register
12
+ end
13
+
14
+ public
15
+ def filter(event)
16
+ %w(request response).each do |re|
17
+ if event[re].is_a?(Hash) && event[re]['body'].is_a?(String)
18
+ _, content_type = event[re].detect { |(k,_)| k =~ /\Acontent.type\z/i }
19
+ if event[re]['headers'].is_a?(Hash) && !content_type
20
+ _, content_type = event[re]['headers'].detect { |(k,_)| k =~ /\Acontent.type\z/i }
21
+ end
22
+ media_type = ::Rack::Request.new({'CONTENT_TYPE' => content_type}).media_type
23
+ body_parsed = begin
24
+ if media_type == 'application/json'
25
+ JSON.parse(event[re]['body']) rescue nil
26
+ elsif media_type == 'application/x-www-form-urlencoded'
27
+ CGI.parse(event[re]['body']).map { |k, vs| {k => vs.last} }.inject({}, &:update)
28
+ end
29
+ end
30
+ event[re]['body_parsed'] = body_parsed if body_parsed
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ require "logstash/filters/base"
2
+ require "logstash/namespace"
3
+
4
+ class LogStash::Filters::RubyLogger < LogStash::Filters::Base
5
+ config_name "ruby_logger"
6
+ milestone 1
7
+
8
+ config :consume, :validate => :boolean, :default => true
9
+ config :source, :validate => :string, :default => 'message'
10
+
11
+ public
12
+ def register
13
+ end
14
+
15
+ public
16
+ def filter(event)
17
+ ruby_logged = /\A(?<severity_letter>\w), +\[(?<time>[\d\-T.:]+) +#(?<pid>\d+)\] +(?<severity>(?i:DEBUG|INFO|WARN|ERROR|FATAL|UNKNOWN|ANY)) +-- +(?<progname>.*?): /
18
+ if ruby_log_match = event[@source].match(ruby_logged)
19
+ uninteresting_names = %w(severity_letter time)
20
+ interesting_names = ruby_log_match.names - uninteresting_names
21
+ event.to_hash.update(interesting_names.map { |name| {name => ruby_log_match[name]} }.inject({}, &:update))
22
+
23
+ event['@timestamp'] = Time.parse(ruby_log_match['time']).utc
24
+
25
+ event[@source] = ruby_log_match.post_match if @consume
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ require "logstash/filters/base"
2
+ require "logstash/namespace"
3
+
4
+ class LogStash::Filters::Sidekiq < LogStash::Filters::Base
5
+ config_name "sidekiq"
6
+ milestone 1
7
+
8
+ config :consume, :validate => :boolean, :default => true
9
+ config :source, :validate => :string, :default => 'message'
10
+
11
+ public
12
+ def register
13
+ end
14
+
15
+ public
16
+ def filter(event)
17
+ sidekiq_logged = /\A(?<time>[\d\-]+T[\d:]+Z) (?<pid>\d+) TID-(?<tid>\w+)(?<context>.*?) (?<severity>(?i:DEBUG|INFO|WARN|ERROR|FATAL|UNKNOWN|ANY)): /
18
+ if sidekiq_match = event[@source].match(sidekiq_logged)
19
+ event['sidekiq'] ||= {}
20
+ event['sidekiq'].update(sidekiq_match.names.map { |name| {name => sidekiq_match[name]} }.inject({}, &:update))
21
+
22
+ event['@timestamp'] = Time.parse(sidekiq_match['time']).utc
23
+
24
+ # extract more info from context
25
+ job_context = /\A\s*(?<job_name>.+) JID-(?<jid>\w+)\z/
26
+ if context_match = sidekiq_match['context'].match(job_context)
27
+ event['sidekiq']['context'] = context_match.names.map { |name| {name => context_match[name]} }.inject({}, &:update)
28
+ end
29
+
30
+ event[@source] = sidekiq_match.post_match if @consume
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_hammer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ethan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-22 00:00:00.000000000 Z
11
+ date: 2014-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -255,6 +255,11 @@ files:
255
255
  - lib/api_hammer/unmunged_request_params.rb
256
256
  - lib/api_hammer/version.rb
257
257
  - lib/api_hammer/weblink.rb
258
+ - lib/logstash/filters/active_support_tags.rb
259
+ - lib/logstash/filters/api_hammer_request.rb
260
+ - lib/logstash/filters/request_bodies_parsed.rb
261
+ - lib/logstash/filters/ruby_logger.rb
262
+ - lib/logstash/filters/sidekiq.rb
258
263
  - test/active_record_cache_find_by_test.rb
259
264
  - test/check_required_params_test.rb
260
265
  - test/faraday_request_logger_test.rb