rack-logjam 0.1.0 → 0.2.0

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: 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