rack-logjam 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b41ef65c7eded4ea8dea271c8b87d7efd09a9dd
4
- data.tar.gz: 9a9a132674d17a97b9def761ca94b43cc26568c5
3
+ metadata.gz: 100c480a6bdd47bb50ebbacbde5f69019c7c386c
4
+ data.tar.gz: 4e737fc841d55db1bdd4c06b2968890e746ca230
5
5
  SHA512:
6
- metadata.gz: 2200c484906b60998f5ea1f704fa99a9643bc3ca7be94c4e8eded4ed2501ac09263357337554efb72770f79fde9656aa1512f6ac78d7dfd88b97d44522ccb3bb
7
- data.tar.gz: 553ef5c793e5d16dfe725c90cf350085d11b377d07f905e47b581f8de8583b05e8b935e0b914ae448cbb117d67115b2c3b78b9a245b5dbceff7f4a784cc7330a
6
+ metadata.gz: 7d2b976933f0ad9827c1fb5521813b79b0d8b852db8becfb9a03d87d839e8c0cc74406ab1d8263832ec78909d5b5a5ac5109522b4385b594666d071ea182b15e
7
+ data.tar.gz: d5422d0782d60ea1181119862976e92488244bfdeac02ad3129821e137cf2dd52ff5f1ee5b391c2b966840f15f5bb9d5739ef78112db30589f5be5224c2606ae
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ rack-logjam
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.0
data/README.markdown CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  Logs helpful HTTP information on Rack requests.
4
4
 
5
+
5
6
  ## Installation
6
7
 
7
8
  Add this line to your application's Gemfile:
@@ -16,14 +17,148 @@ Or install it yourself as:
16
17
 
17
18
  $ gem install rack-logjam
18
19
 
20
+
19
21
  ## Usage
20
22
 
21
- TODO: Write usage instructions here
23
+ ### Including the Middleware
24
+
25
+ #### Grape
26
+
27
+ Include the middleware in your API classes with the `use` statement
28
+
29
+ module AwesomeApp
30
+ class ApiV1 < Grape::API
31
+ use Rack::Logjam::Grape::Middleware
32
+ end
33
+ end
34
+
35
+
36
+ #### Rails
37
+
38
+ Require the middleware in `config/application.rb`
39
+
40
+ config.middleware.use( 'Rack::Logjam::Middleware' )
41
+
42
+ ### Configuration
43
+
44
+ At a minimum one must specify the logger that rack-logjam will use.
45
+
46
+ Rack::Logjam.configure |c|
47
+ c.logger = Rails.logger
48
+ end
49
+
50
+ #### Formatters
51
+
52
+ Rack::Logjam includes several default formatters that are registered by default: `Array, FormUrlencoded, Json, TextPlain, and Xml`. Formatters
53
+ are registered and selected by mime-types. If a the mime-type of a response does not have a regostered formatter, it will output a message to
54
+ the log in place of the body specifying the lack of formatter and mime-type.
55
+
56
+ Additionally, you can configure custom mime-types with formatters in the configuration file.
57
+
58
+ Rack::Logjam.configure |c|
59
+ # register custom mime-types with inlcuded formatters
60
+ c.register_formatter 'application/vnd.awesome-app-v1+x-www-form-urlencoded', :FormUrlencoded
61
+ c.register_formatter 'application/vnd.awesome-app-v1+json', :Json
62
+ c.register_formatter 'application/vnd.awesome-app-v1+text', :TextPlain
63
+ c.register_formatter 'application/vnd.awesome-app-v1+xml', :Xml
64
+
65
+ # register custom mime-type with custom formatter
66
+ c.register_formatter 'application/vnd.awesome-app-v1+csv', AwesomeApp::LogFormatter::Csv
67
+ end
68
+
69
+ #### Filters
70
+
71
+ Filters can fully fully redact or truncate data within data structures. Filters are registered and selected by mime-types. No filters are
72
+ registered by default. However, Rack::Logjam includes a `Json` filter. The Json filter uses [JsonPath](https://github.com/joshbuddy/jsonpath)
73
+ to specify which attributes are filtered.
74
+
75
+ Additionally, you can configure custom mime-types with filters in the configuration file.
76
+
77
+ Rack::Logjam.configure |c|
78
+ # configure a mime-type with the built in Json filter
79
+ # this filter will truncate all image_as_base64 attributes
80
+ # at any level of nesting and redact the subject/date_of_birth
81
+ c.register_filter 'application/vnd.ncite-vetting-v1+json', :Json, [
82
+ ['$..image_as_base64', :truncate, 10],
83
+ ['$.subject_attributes.date_of_birth', :redact]
84
+ ]
85
+ end
86
+
87
+ #### Custom Formatters and Filters
88
+
89
+ ##### Writing a Custom Formatter
90
+
91
+ A formatter is a simply a class that accepts content in an initializer and implements a #render method with no parameters. Folowing
92
+ is an implementation of a CSV formatter.
93
+
94
+ module AwesomeApp
95
+ module Formatter
96
+ class Csv
97
+ def initialize( content )
98
+ @content = content
99
+ end
100
+
101
+ def render
102
+ content
103
+ end
104
+
105
+ protected
106
+
107
+ attr_reader :content
108
+ end
109
+ end
110
+ end
111
+
112
+ The formatter must also be registered with a mime-type.
113
+
114
+ c.register_formatter 'application/vnd.awesome-app-v1+csv', AwesomeApp::Formatter::Csv
115
+
116
+ ##### Writing a Custom Filter
117
+
118
+ A filter is a simply a class that accepts content and filters in an initializer and implements a #render method with no parameters. Folowing
119
+ is a naive implementation of a CSV filter.
120
+
121
+ require 'csv'
122
+
123
+ module AwesomeApp
124
+ module Filters
125
+ class Csv
126
+ def initialize( content, filters )
127
+ @content = content
128
+ @filters = filters
129
+ end
130
+
131
+ def render
132
+ CSV.parse("some long bit of text",data,123-45-6789,data") do |row|
133
+ filters.each do |column, action, length|
134
+ row[column] = send( action, row[column], length )
135
+ end
136
+ end
137
+ end
138
+
139
+ protected
140
+
141
+ def redact( val, *args )
142
+ (val.nil? || val.empty?) ?
143
+ val :
144
+ "[REDACTED]"
145
+ end
146
+
147
+ def truncate( val, length )
148
+ (val.nil? || val.empty?) ?
149
+ val :
150
+ "#{val[0..length]}...[TRUNCATED]..."
151
+ end
152
+
153
+ attr_reader :content,
154
+ :filters
155
+ end
156
+ end
157
+ end
22
158
 
23
- ## Contributing
159
+ The filter must also be registered with a mime-type.
24
160
 
25
- 1. Fork it ( http://github.com/ninja-loss/rack-logjam/fork )
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Add some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
161
+ c.register_filter 'application/vnd.awesome-app-v1+xml', AwesomeApp::Filters::Csv, [
162
+ [0, :truncate, 10],
163
+ [2, :redact]
164
+ ]
@@ -1,7 +1,5 @@
1
1
  module Rack
2
-
3
2
  module Logjam
4
-
5
3
  class ANSI
6
4
 
7
5
  def self.resolve_text( color, &block )
@@ -38,7 +36,5 @@ module Rack
38
36
  end
39
37
 
40
38
  end
41
-
42
39
  end
43
-
44
40
  end
@@ -0,0 +1,19 @@
1
+ module Rack
2
+ module Logjam
3
+ class Configuration
4
+
5
+ def logger=( logger )
6
+ Rack::Logjam::logger = logger
7
+ end
8
+
9
+ def register_filter( mime_type, filter_symbol_or_constant, filters )
10
+ Rack::Logjam::Filters.register( mime_type, filter_symbol_or_constant, filters )
11
+ end
12
+
13
+ def register_formatter( mime_type, formatter_symbol_or_constant )
14
+ Rack::Logjam::Formatters.register( mime_type, formatter_symbol_or_constant )
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Rack
2
+ module Logjam
3
+ module Filters
4
+ class Base
5
+
6
+ def initialize( content, filters )
7
+ @content = content
8
+ @filters = filters
9
+ end
10
+
11
+ def render
12
+ raise NotImplementedError
13
+ end
14
+
15
+ protected
16
+
17
+ attr_reader :content,
18
+ :filters
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,55 @@
1
+ require 'jsonpath'
2
+
3
+ module Rack
4
+ module Logjam
5
+ module Filters
6
+ class Json < Rack::Logjam::Filters::Base
7
+
8
+ def render
9
+ apply_filters
10
+ json_path.to_json
11
+ end
12
+
13
+ protected
14
+
15
+ def apply_filters
16
+ filters.each do |j_path, action, length|
17
+ json_path.gsub!( j_path ) do |val|
18
+ method_name = "#{action}_#{val.class.name.downcase}"
19
+ if respond_to?( method_name, true )
20
+ send( method_name, val, length )
21
+ else
22
+ "[#{action.upcase}ED: #{val.class.name}]"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def redact_nilclass( val, *args )
29
+ nil
30
+ end
31
+
32
+ def truncate_nilclass( val, length )
33
+ nil
34
+ end
35
+
36
+ def redact_string( val, *args )
37
+ (val.nil? || val.empty?) ?
38
+ val :
39
+ "[REDACTED]"
40
+ end
41
+
42
+ def truncate_string( val, length )
43
+ (val.nil? || val.empty?) ?
44
+ val :
45
+ "#{val[0..length]}...[TRUNCATED]..."
46
+ end
47
+
48
+ def json_path
49
+ @json_path ||= ::JsonPath.for( content )
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Filters
4
+ class Nil < Rack::Logjam::Filters::Base
5
+
6
+ def render
7
+ content
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Rack
2
+ module Logjam
3
+ module Filters
4
+
5
+ autoload :Base, 'rack/logjam/filters/base'
6
+ autoload :Json, 'rack/logjam/filters/json'
7
+ autoload :Nil, 'rack/logjam/filters/nil'
8
+
9
+ @registry = {}
10
+
11
+ def self.registry
12
+ @registry
13
+ end
14
+
15
+ def self.register( mime_type, formatter_klass_name, filters )
16
+ registry.merge!( mime_type => [formatter_klass_name, filters] )
17
+ end
18
+
19
+ def self.get( mime_type )
20
+ const_name, filters = registry.fetch( mime_type, [:Nil, []] )
21
+ return self::const_get( const_name ), filters
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class Array < ::Rack::Logjam::Formatters::Base
5
+
6
+ def render
7
+ Json.new( content, format ).render
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class Base
5
+
6
+ def initialize( content, format=nil )
7
+ @content = content
8
+ @format = format
9
+ end
10
+
11
+ def render
12
+ "You must implement #{self.class.name}#render in order to render a #{format} response"
13
+ end
14
+
15
+ protected
16
+
17
+ attr_reader :content,
18
+ :format
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class Empty < ::Rack::Logjam::Formatters::Base
5
+
6
+ def render
7
+ "[EMPTY]"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class FormUrlencoded < ::Rack::Logjam::Formatters::Base
5
+
6
+ def render
7
+ URI.unescape( content )
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'multi_json'
2
+
3
+ module Rack
4
+ module Logjam
5
+ module Formatters
6
+ class Json < ::Rack::Logjam::Formatters::Base
7
+
8
+ def render
9
+ hash = MultiJson.load( content )
10
+ MultiJson.dump( hash, pretty: true )
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class Nil < ::Rack::Logjam::Formatters::Base
5
+
6
+ def render
7
+ "No formatter defined for mime-type #{format}"
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+ class TextPlain < ::Rack::Logjam::Formatters::Base
5
+
6
+ def render
7
+ content
8
+ end
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ require 'nokogiri'
2
+
3
+ module Rack
4
+ module Logjam
5
+ module Formatters
6
+ class Xml < ::Rack::Logjam::Formatters::Base
7
+
8
+ def render
9
+ ::Nokogiri.XML( content ) do |config|
10
+ config.default_xml.noblanks
11
+ end.to_xml( indent: 2 )
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module Rack
2
+ module Logjam
3
+ module Formatters
4
+
5
+ autoload :Array, 'rack/logjam/formatters/array'
6
+ autoload :Base, 'rack/logjam/formatters/base'
7
+ autoload :Empty, 'rack/logjam/formatters/empty'
8
+ autoload :FormUrlencoded, 'rack/logjam/formatters/form_urlencoded'
9
+ autoload :Json, 'rack/logjam/formatters/json'
10
+ autoload :Nil, 'rack/logjam/formatters/nil'
11
+ autoload :TextPlain, 'rack/logjam/formatters/text_plain'
12
+ autoload :Xml, 'rack/logjam/formatters/xml'
13
+
14
+ @registry = {}
15
+
16
+ def self.registry
17
+ @registry
18
+ end
19
+
20
+ def self.register( mime_type, formatter_klass_name )
21
+ registry.merge!( mime_type => formatter_klass_name )
22
+ end
23
+
24
+ def self.get( mime_type )
25
+ const_name = registry.fetch( mime_type, :Nil )
26
+ const_name.is_a?( Class ) ?
27
+ const_name :
28
+ self::const_get( const_name )
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,88 @@
1
+ #require 'json'
2
+ require 'grape/middleware/base'
3
+
4
+ module Rack
5
+ module Logjam
6
+ module Grape
7
+ class Middleware < ::Grape::Middleware::Base
8
+
9
+ def before
10
+ return unless api_request?( env )
11
+
12
+ logger.log_request( env )
13
+ end
14
+
15
+ def after
16
+ return unless api_request?( env )
17
+
18
+ status = @app_response.first
19
+ headers = @app_response[1]
20
+ body = @app_response.last.body.last
21
+
22
+ #logger.log_response( env, status, headers, response )
23
+ logger.log_response( env, status, headers, body )
24
+
25
+ @app_response
26
+ end
27
+
28
+ protected
29
+
30
+ def api_request?( env )
31
+ true
32
+ #path_info( env ) =~ /^\/api\//
33
+ end
34
+
35
+ def path_info( env )
36
+ env['PATH_INFO']
37
+ end
38
+
39
+ def logger
40
+ Rack::Logjam::Logger.new
41
+ end
42
+
43
+ def request_log_data
44
+ request_data = {
45
+ auth_token: env['HTTP_X_NCITE_AUTH_TOKEN'],
46
+ content_type: content_type,
47
+ content_length: env['CONTENT_LENGTH'],
48
+ accept: env['HTTP_ACCEPT'],
49
+ accept_version: env['HTTP_ACCEPT_VERSION'],
50
+ method: env['REQUEST_METHOD'],
51
+ path: env['PATH_INFO'],
52
+ query: env['QUERY_STRING']
53
+ }
54
+ #request_data[:user_id] = current_user.id if current_user
55
+ request_data
56
+ end
57
+
58
+ def response_log_data
59
+ {
60
+ description: env['api.endpoint'].options[:route_options][:description],
61
+ source_file: env['api.endpoint'].block.source_location[0][(::Rails.root.to_s.length+1)..-1],
62
+ source_line: env['api.endpoint'].block.source_location[1]
63
+ }
64
+ end
65
+
66
+ def content_type
67
+ env['CONTENT_TYPE']
68
+ end
69
+
70
+ def formatted_request_body
71
+ body = env['rack.input'].read
72
+ return ANSI.yellow { 'EMPTY' } if body.blank?
73
+
74
+ if content_type == Mime::JSON.to_s
75
+ return ANSI.cyan { JSON.pretty_generate( JSON.parse( body )) }
76
+ end
77
+
78
+ nil
79
+ end
80
+
81
+ def formatted_response_body
82
+ ANSI.cyan { JSON.pretty_generate(JSON.parse( @app_response.last.body.last )) }
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,9 @@
1
+ module Rack
2
+ module Logjam
3
+ module Grape
4
+
5
+ autoload :Middleware, 'rack/logjam/grape/middleware'
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,94 @@
1
+ require 'jsonpath'
2
+ require 'mime/types'
3
+
4
+ module Rack
5
+ module Logjam
6
+ class Logger
7
+
8
+ def log_request( env )
9
+ _logger.info <<-end_info
10
+ [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Request Env ---' }}
11
+ #{ANSI.magenta { JSON.pretty_generate( request_log_data( env )) }}
12
+ [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Request Body ---' }}
13
+ #{ANSI.cyan { formatted_request_body( env ) }}
14
+ end_info
15
+ end
16
+
17
+ def log_response( env, status, headers, response )
18
+ _logger.info <<-end_info
19
+ [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Response ---' }}
20
+ Status: #{status}
21
+ Headers: #{headers.inspect}
22
+ Body:
23
+ #{ANSI.cyan { format_body( (response.body rescue response), accept( env ), env ) }}
24
+ end_info
25
+ end
26
+
27
+ protected
28
+
29
+ def request_log_data( env )
30
+ request_data = {
31
+ content_type: content_type( env ),
32
+ content_length: env['CONTENT_LENGTH'],
33
+ accept: accept( env ),
34
+ accept_version: env['HTTP_ACCEPT_VERSION'],
35
+ method: env['REQUEST_METHOD'],
36
+ path: path_info( env ),
37
+ query: query( env )
38
+ }
39
+ #request_data[:user_id] = current_user.id if current_user
40
+ request_data
41
+ end
42
+
43
+ def content_type( env )
44
+ env['CONTENT_TYPE']
45
+ end
46
+
47
+ def accept( env )
48
+ env['HTTP_ACCEPT']
49
+ end
50
+
51
+ def path_info( env )
52
+ env['PATH_INFO']
53
+ end
54
+
55
+ def query( env )
56
+ URI.unescape( env['QUERY_STRING'] )
57
+ end
58
+
59
+ def formatted_request_body( env )
60
+ format_body( rack_input_content( env ), content_type( env ), env )
61
+ end
62
+
63
+ def format_body( body, format, env )
64
+ filter = fetch_filter( format, body )
65
+ filtered = filter.render
66
+
67
+ formatter = fetch_formatter( format, body ).new( filtered, format )
68
+ formatter.render
69
+ end
70
+
71
+ def fetch_filter( format, body )
72
+ klass, filters = Rack::Logjam::Filters.get( format )
73
+ klass.new( body, filters )
74
+ end
75
+
76
+ def fetch_formatter( format, body )
77
+ return Rack::Logjam::Array if body.is_a?( Array )
78
+ return Rack::Logjam::Empty if body.strip.nil? || body.strip.empty?
79
+ Rack::Logjam::Formatters.get( format )
80
+ end
81
+
82
+ def rack_input_content( env )
83
+ ( rack_input = env['rack.input'] ).read.tap do |content|
84
+ rack_input.rewind
85
+ end
86
+ end
87
+
88
+ def _logger
89
+ ::Rack::Logjam::logger
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,52 @@
1
+ require 'action_dispatch/http/mime_type'
2
+ require 'jsonpath'
3
+
4
+ module Rack
5
+ module Logjam
6
+ module Rails
7
+ class Middleware
8
+
9
+ def initialize( app )
10
+ @app = app
11
+ end
12
+
13
+ def call( env )
14
+ before env
15
+
16
+ app.call( env ).tap do |rack_response|
17
+ after env, *rack_response
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ attr_reader :app
24
+
25
+ def before( env )
26
+ return unless api_request?( env )
27
+
28
+ logger.log_request( env )
29
+ end
30
+
31
+ def after( env, status, headers, response )
32
+ return unless api_request?( env )
33
+
34
+ logger.log_response( env, status, headers, response )
35
+ end
36
+
37
+ def api_request?( env )
38
+ path_info( env ) =~ /^\/api\//
39
+ end
40
+
41
+ def path_info( env )
42
+ env['PATH_INFO']
43
+ end
44
+
45
+ def logger
46
+ Rack::Logjam::Logger.new
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,9 @@
1
+ module Rack
2
+ module Logjam
3
+ module Rails
4
+
5
+ autoload :Middleware, 'rack/logjam/rails/middleware'
6
+
7
+ end
8
+ end
9
+ end
@@ -2,7 +2,7 @@ module Rack
2
2
 
3
3
  module Logjam
4
4
 
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
 
7
7
  end
8
8
 
data/lib/rack/logjam.rb CHANGED
@@ -1,11 +1,35 @@
1
1
  module Rack
2
-
3
2
  module Logjam
4
3
 
5
- autoload :VERSION, 'rack/logjam/version'
6
- autoload :ANSI, 'rack/logjam/ansi'
7
- autoload :Middleware, 'rack/logjam/middleware'
4
+ autoload :ANSI, 'rack/logjam/ansi'
5
+ autoload :Configuration, 'rack/logjam/configuration'
6
+ autoload :Filters, 'rack/logjam/filters'
7
+ autoload :Formatters, 'rack/logjam/formatters'
8
+ autoload :Grape, 'rack/logjam/grape'
9
+ autoload :Logger, 'rack/logjam/logger'
10
+ autoload :Rails, 'rack/logjam/rails'
11
+ autoload :VERSION, 'rack/logjam/version'
8
12
 
9
- end
13
+ def self.configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ def self.configuration=( configuration )
18
+ @configuration = configuration
19
+ end
20
+
21
+ def self.configure
22
+ yield( configuration ) if block_given?
23
+ end
10
24
 
25
+ class << self
26
+ attr_accessor :logger
27
+ end
28
+
29
+ end
11
30
  end
31
+
32
+ Rack::Logjam::Formatters.register 'application/x-www-form-urlencoded', :FormUrlencoded
33
+ Rack::Logjam::Formatters.register 'application/json', :Json
34
+ Rack::Logjam::Formatters.register 'text/plain', :TextPlain
35
+ Rack::Logjam::Formatters.register 'application/xml', :Xml
data/rack-logjam.gemspec CHANGED
@@ -20,9 +20,9 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'actionpack', '~> 3.2'
24
- spec.add_dependency 'jsonpath', '~> 0.5'
25
- spec.add_dependency 'nokogiri', '~> 1.6'
23
+ spec.add_dependency 'jsonpath', '~> 0.5'
24
+ spec.add_dependency 'multi_json'
25
+ spec.add_dependency 'nokogiri'
26
26
 
27
27
  spec.add_development_dependency 'bundler', '~> 1.5'
28
28
  spec.add_development_dependency 'rake'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-logjam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Harrelson
@@ -9,50 +9,50 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-06 00:00:00.000000000 Z
12
+ date: 2014-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: actionpack
15
+ name: jsonpath
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '3.2'
20
+ version: '0.5'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: '3.2'
27
+ version: '0.5'
28
28
  - !ruby/object:Gem::Dependency
29
- name: jsonpath
29
+ name: multi_json
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - "~>"
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: '0.5'
34
+ version: '0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - "~>"
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: '0.5'
41
+ version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: nokogiri
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: '1.6'
48
+ version: '0'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - "~>"
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
- version: '1.6'
55
+ version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: bundler
58
58
  requirement: !ruby/object:Gem::Requirement
@@ -89,6 +89,8 @@ extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
91
  - ".gitignore"
92
+ - ".ruby-gemset"
93
+ - ".ruby-version"
92
94
  - Gemfile
93
95
  - License.markdown
94
96
  - README.markdown
@@ -96,7 +98,25 @@ files:
96
98
  - lib/rack-logjam.rb
97
99
  - lib/rack/logjam.rb
98
100
  - lib/rack/logjam/ansi.rb
99
- - lib/rack/logjam/middleware.rb
101
+ - lib/rack/logjam/configuration.rb
102
+ - lib/rack/logjam/filters.rb
103
+ - lib/rack/logjam/filters/base.rb
104
+ - lib/rack/logjam/filters/json.rb
105
+ - lib/rack/logjam/filters/nil.rb
106
+ - lib/rack/logjam/formatters.rb
107
+ - lib/rack/logjam/formatters/array.rb
108
+ - lib/rack/logjam/formatters/base.rb
109
+ - lib/rack/logjam/formatters/empty.rb
110
+ - lib/rack/logjam/formatters/form_urlencoded.rb
111
+ - lib/rack/logjam/formatters/json.rb
112
+ - lib/rack/logjam/formatters/nil.rb
113
+ - lib/rack/logjam/formatters/text_plain.rb
114
+ - lib/rack/logjam/formatters/xml.rb
115
+ - lib/rack/logjam/grape.rb
116
+ - lib/rack/logjam/grape/middleware.rb
117
+ - lib/rack/logjam/logger.rb
118
+ - lib/rack/logjam/rails.rb
119
+ - lib/rack/logjam/rails/middleware.rb
100
120
  - lib/rack/logjam/version.rb
101
121
  - rack-logjam.gemspec
102
122
  homepage: ''
@@ -1,149 +0,0 @@
1
- require 'action_dispatch/http/mime_type'
2
- require 'jsonpath'
3
-
4
- module Rack
5
-
6
- module Logjam
7
-
8
- class Middleware
9
-
10
- def initialize( app )
11
- @app = app
12
- end
13
-
14
- def call( env )
15
- before env
16
- app.call( env ).tap do |rack_response|
17
- after env, *rack_response
18
- end
19
- end
20
-
21
- protected
22
-
23
- attr_reader :app
24
-
25
- def before( env )
26
- return unless api_request?( env )
27
-
28
- Rails.logger.info <<-end_info
29
- [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Request Env ---' }}
30
- #{ANSI.magenta { JSON.pretty_generate( request_log_data( env )) }}
31
- [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Request Body ---' }}
32
- #{ANSI.cyan { formatted_request_body( env ) }}
33
- end_info
34
- end
35
-
36
- def after( env, status, headers, response )
37
- return unless api_request?( env )
38
-
39
- Rails.logger.info <<-end_info
40
- [#{ANSI.green { 'api' }}] #{ANSI.cyan { '--- Response ---' }}
41
- Status: #{status}
42
- Headers: #{headers.inspect}
43
- Body:
44
- #{ANSI.cyan { format_body( (response.body rescue response), accept( env ), env ) }}
45
- end_info
46
- end
47
-
48
- def request_log_data( env )
49
- request_data = {
50
- auth_token: env['HTTP_X_NCITE_AUTH_TOKEN'],
51
- content_type: content_type( env ),
52
- content_length: env['CONTENT_LENGTH'],
53
- accept: accept( env ),
54
- accept_version: env['HTTP_ACCEPT_VERSION'],
55
- method: env['REQUEST_METHOD'],
56
- path: path_info( env ),
57
- query: query( env )
58
- }
59
- #request_data[:user_id] = current_user.id if current_user
60
- request_data
61
- end
62
-
63
- def api_request?( env )
64
- path_info( env ) =~ /^\/api\//
65
- end
66
-
67
- def content_type( env )
68
- env['CONTENT_TYPE']
69
- end
70
-
71
- def accept( env )
72
- env['HTTP_ACCEPT']
73
- end
74
-
75
- def path_info( env )
76
- env['PATH_INFO']
77
- end
78
-
79
- def query( env )
80
- URI.unescape( env['QUERY_STRING'] )
81
- end
82
-
83
- def formatted_request_body( env )
84
- format_body( rack_input_content( env ), content_type( env ), env )
85
- end
86
-
87
- def format_body( body, format, env )
88
- return body.inspect if body.is_a?( Array )
89
- return body if body.strip.nil? || body.strip.empty?
90
-
91
- if format == ::Mime::JSON.to_s
92
- hash = truncate_json_attributes( body )
93
- return ::JSON.pretty_generate( hash )
94
- elsif format == ::Mime::XML.to_s
95
- return ::Nokogiri.XML( body ) do |config|
96
- config.default_xml.noblanks
97
- end.to_xml( indent: 2 )
98
- elsif format == ::Mime::URL_ENCODED_FORM.to_s
99
- return URI.unescape( body )
100
- elsif format == ::Mime::OCTET_STREAM.to_s
101
- return "no body b/c content type is #{::Mime::OCTET_STREAM}"
102
- end
103
-
104
- body
105
- end
106
-
107
- def rack_input_content( env )
108
- ( rack_input = env['rack.input'] ).read.tap do |content|
109
- rack_input.rewind
110
- end
111
- end
112
-
113
- # Can currently use the following xpath expressions that will translate to json_path:
114
- # /some/path -> $.some.path (search from root)
115
- # some/path -> some.path (search for any sub-path that matches this, not bound to root)
116
- # //path -> $..path (recursive search)
117
- #
118
- def truncated_attributes_as_xpath
119
- %w(
120
- //image_as_base64
121
- //fingerprint_image_as_base64
122
- )
123
- end
124
-
125
- def truncate_json_attributes( json )
126
- json_path = ::JsonPath.for( json )
127
- truncated_attributes_as_json_paths.each do |j_path|
128
- json_path.gsub!( j_path ) { |val| (val.nil? || val.empty?) ? val : "#{val[0..25]} ... [TRUNCATED] ..." }
129
- end
130
- json_path.to_hash
131
- end
132
-
133
- def truncated_attributes_as_json_paths
134
- truncated_attributes_as_xpath.map do |xpath|
135
- if xpath.start_with?( '//' )
136
- xpath.gsub( '//', '$..' )
137
- else
138
- parts = xpath.split( '/' )
139
- parts[0] = '$' if parts[0] == ''
140
- parts.join( '.' )
141
- end
142
- end
143
- end
144
-
145
- end
146
-
147
- end
148
-
149
- end