tracker_hub-request 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f9665a1b80b7c8db64b40ae258eea6b6872c100
4
+ data.tar.gz: 7f645fc08bf692b2f9ea759495401ea9c1db90fa
5
+ SHA512:
6
+ metadata.gz: 0903a3b964a765540d1bbe24069a42ad9327efa9dea128d0db8248b10b63bf8902e98588d4f982d04737c8557e9cc1f9a73aa1167bf94ee67f410f6e8e1adc0b
7
+ data.tar.gz: de367b9ff0845498841c922a5ba62dd3657dcdad4b2e16a6ad01834a5627ee8dabf688aa7c1522424fb196597ebafc98a900bb3dfc7d0d49de412ebac8f3133b
data/.codeclimate.yml ADDED
@@ -0,0 +1,19 @@
1
+ ---
2
+ engines:
3
+ reek:
4
+ enabled: true
5
+ duplication:
6
+ enabled: true
7
+ config:
8
+ languages:
9
+ - ruby
10
+ fixme:
11
+ enabled: true
12
+ rubocop:
13
+ enabled: true
14
+ ratings:
15
+ paths:
16
+ - '**.rb'
17
+ exclude_paths:
18
+ - pkg/
19
+ - spec/
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
11
+ /coverage/
12
+ /rdoc/
13
+ /.rvmrc
data/.hound.yml ADDED
@@ -0,0 +1,2 @@
1
+ ruby:
2
+ config_file: .rubocop.yml
data/.reek ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ TooManyStatements:
3
+ enabled: true
4
+ exclude:
5
+ - initialize
6
+ max_statements: 10
7
+
8
+ Attribute:
9
+ exclude:
10
+ - TrackerHub::Request::Config
11
+
12
+ # Directories below will not be scanned at all
13
+ exclude_paths:
14
+ - spec
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'spec/**/*'
4
+
5
+ Metrics/LineLength:
6
+ Max: 100
7
+ # To make it possible to copy or click on URIs in the code, we allow lines
8
+ # containing a URI to be longer than Max.
9
+ AllowHeredoc: true
10
+ AllowURI: true
11
+ URISchemes:
12
+ - http
13
+ - https
14
+
15
+ Metrics/MethodLength:
16
+ CountComments: false # count full line comments?
17
+ Max: 15
data/.travis.yml ADDED
@@ -0,0 +1,31 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache:
4
+ - bundler
5
+
6
+ rvm:
7
+ - jruby-head
8
+ - rbx-2
9
+ - 2.0.0
10
+ - 2.1.9
11
+ - 2.2.5
12
+ - 2.3.1
13
+ - ruby-head
14
+
15
+ addons:
16
+ code_climate:
17
+ repo_token: 54662312136af39c0814c981c7693a7baf17fbeb09e154806a08b97f467c9392
18
+
19
+ before_install:
20
+ - gem install bundler
21
+
22
+ before_script:
23
+ - bundle update
24
+
25
+ script:
26
+ - bundle exec rake spec
27
+
28
+ matrix:
29
+ allow_failures:
30
+ - rvm: rbx-2
31
+ - rvm: jruby-head
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Change Log
2
+
3
+ ## [1.0.1](https://github.com/SparkHub/gs-tracking-requests/tree/1.0.1) (2016-10-07)
4
+
5
+ **Fixed bugs:**
6
+ - Fix HipChat argument conflicts on #send_message ([mchaisse](https://github.com/mchaisse))
7
+
8
+
9
+ ## [1.0.0](https://github.com/SparkHub/gs-tracking-requests/tree/1.0.0) (2016-10-04)
10
+
11
+ **Implemented enhancements:**
12
+ - Initial version :tada: ([mchaisse](https://github.com/mchaisse))
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at maxime.chaisseleal@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tracker_hub-requests.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'rubocop', require: false
8
+ gem 'brakeman', require: false
9
+ if RUBY_VERSION >= '2.1.0'
10
+ gem 'reek', require: false
11
+ else
12
+ gem 'reek', '~> 3.11', require: false
13
+ end
14
+ end
15
+
16
+ group :test do
17
+ gem 'simplecov', require: false
18
+ gem 'codeclimate-test-reporter', require: false if RUBY_VERSION >= '2.0'
19
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Maxime Chaisse-Leal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ [![Gem Version](https://badge.fury.io/rb/tracker_hub-request.svg)](http://badge.fury.io/rb/tracker_hub-request)
2
+ [![Build Status](https://travis-ci.org/SparkHub/gs-tracking-requests.svg?branch=master)](https://travis-ci.org/SparkHub/gs-tracking-requests)
3
+ [![Code Climate](https://codeclimate.com/github/SparkHub/gs-tracking-requests/badges/gpa.svg)](https://codeclimate.com/github/SparkHub/gs-tracking-requests)
4
+ [![Test Coverage](https://codeclimate.com/github/SparkHub/gs-tracking-requests/badges/coverage.svg)](https://codeclimate.com/github/SparkHub/gs-tracking-requests/coverage)
5
+ [![Dependency Status](https://gemnasium.com/SparkHub/gs-tracking-requests.svg)](https://gemnasium.com/SparkHub/gs-tracking-requests)
6
+ [![Inline docs](http://inch-ci.org/github/SparkHub/gs-tracking-requests.svg)](http://inch-ci.org/github/SparkHub/gs-tracking-requests)
7
+
8
+ # TrackerHub::Request
9
+
10
+ Part of __TrackerHub__, __Request__ is a gem tracking every requests on the backend side. Some keys will be filtered and the request data will finally be saved into a logfile. This logfile will be automatically managed and rotated by the [logging](https://github.com/TwP/logging) gem.
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'tracker_hub-request', git: 'git@github.com:SparkHub/gs-tracking-requests.git'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ ## Configuration _(optional)_
25
+
26
+ In an initializer, you can configure the tracker:
27
+
28
+ ```ruby
29
+ # ./config/initializers/request_tracker.rb
30
+ TrackerHub::Request.setup do |config|
31
+ config.app_version = '1.0'
32
+ config.required_keys = %w(my rack env keys to log)
33
+ config.logger = ActiveSupport::Logger.new('requests.log')
34
+ config.notification = TrackerHub::Request::Notification.new(TrackerHub::Request::Notification::HipChat.new('my_token', 'my_room', 'my_username'))
35
+ end
36
+ ```
37
+
38
+ __Note:__
39
+
40
+ - __logger__
41
+
42
+ Here you can define your own configured logger. The default logger is [logging](https://github.com/TwP/logging). Feel free to add your own, or use another gem!
43
+
44
+ - __notification__
45
+
46
+ If an error occure, it will be catched and a notification will be sent to the service of your choice. The list of available services are defined [here](https://github.com/SparkHub/gs-tracking-requests/tree/master/lib/tracker_hub/request/notification).
47
+
48
+ ## Usage
49
+
50
+ Add the middleware to your environment:
51
+
52
+ ```ruby
53
+ config.middleware.insert_after ActionDispatch::DebugExceptions, TrackerHub::Request::Middleware
54
+ ```
55
+
56
+ Activate the TrackerHub by adding to your environment (or `.env`):
57
+
58
+ $ TRACKER=true
59
+
60
+ Get your request logs:
61
+
62
+ $ tail -f ./log/tracker/requests.log
63
+
64
+ ## Running tests
65
+
66
+ To run tests:
67
+
68
+ $ bundle exec rake spec
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull request are welcome on GitHub at https://github.com/SparkHub/gs-tracking-requests. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
73
+
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'tracking_hub/requests'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require 'pry'
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,45 @@
1
+ require 'logging'
2
+
3
+ require_relative 'request/version'
4
+ require_relative 'request/middleware'
5
+ require_relative 'request/setup'
6
+
7
+ module TrackerHub
8
+ # Format and log an incoming request
9
+ class Request
10
+ extend Setup
11
+ include Format::Logger
12
+
13
+ private
14
+
15
+ # @return [Utils::Env] the rack environment object built from the response object
16
+ # @api private
17
+ attr_accessor :response
18
+
19
+ # @return [Integer] the request status (can be a Fixnum)
20
+ # @api private
21
+ attr_accessor :status
22
+
23
+ # @return [Hash] request header
24
+ # @api private
25
+ attr_accessor :request
26
+
27
+ # Instantiate a request tracker object
28
+ #
29
+ # @param [Utils::Env] env Rack environement object (full response)
30
+ # @param [Integer] status Request status
31
+ # @param [Hash] headers Request header
32
+ #
33
+ # @example
34
+ # > status, headers, body = @app.call(env)
35
+ # > new_env = Utils::Env.new(env)
36
+ # > TrackerHub::Request.new(new_env, status, headers)
37
+ #
38
+ # @api private
39
+ def initialize(env, status, headers)
40
+ self.request = headers
41
+ self.status = status
42
+ self.response = env
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'config/env_keys'
2
+ require_relative 'config/logger'
3
+ require_relative 'format/logger'
4
+ require_relative 'notification'
5
+
6
+ module TrackerHub
7
+ class Request
8
+ # TrackerHub::Request configuration object, can be setup in an initializer
9
+ class Config
10
+ # @return [undefined] logger object to log the request data with
11
+ # @api public
12
+ attr_accessor :logger
13
+
14
+ # @return [Array<String>] rack env keys to log
15
+ # @api public
16
+ attr_accessor :required_keys
17
+
18
+ # @return [TrackerHub::Request::Notification] service to send a
19
+ # notification to if request log process fails
20
+ # @api public
21
+ attr_accessor :notification
22
+
23
+ # @return [String] version of the application logging the request data
24
+ # @api public
25
+ attr_accessor :app_version
26
+
27
+ private
28
+
29
+ # Instanciate a Config object with default values
30
+ #
31
+ # @todo Extract logger logic to be able to store data in another way
32
+ # (ex: database)
33
+ #
34
+ # @return [TrackerHub::Request::Config]
35
+ #
36
+ # @example
37
+ # > TrackerHub::Request::Config.new
38
+ #
39
+ # @api private
40
+ def initialize
41
+ self.app_version = ''
42
+ self.logger = Logger.default_config
43
+ self.required_keys = EnvKeys.default_config
44
+ self.notification = Request::Notification.new
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,71 @@
1
+ module TrackerHub
2
+ class Request
3
+ class Config
4
+ # Rack environment keys to log
5
+ module EnvKeys
6
+ class << self
7
+ # Rack env keys to extract and log
8
+ #
9
+ # @return [Array<String>]
10
+ #
11
+ # @example
12
+ # > TrackerHub::Request::Config::EnvKeys.default_config
13
+ #
14
+ # @api public
15
+ def default_config
16
+ root_keys + rack_keys + action_dispatch_keys
17
+ end
18
+
19
+ private
20
+
21
+ # Request keys in the rack env
22
+ #
23
+ # @return [Array<String>]
24
+ #
25
+ # @api private
26
+ def root_keys
27
+ %w(GATEWAY_INTERFACE PATH_INFO QUERY_STRING REMOTE_ADDR REMOTE_HOST REQUEST_METHOD
28
+ REQUEST_URI SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
29
+ HTTP_HOST HTTP_CONNECTION HTTP_UPGRADE_INSECURE_REQUESTS HTTP_USER_AGENT HTTP_ACCEPT
30
+ HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_COOKIE HTTP_VERSION REQUEST_PATH
31
+ ORIGINAL_FULLPATH ORIGINAL_SCRIPT_NAME)
32
+ end
33
+
34
+ # Rack keys in the rack env
35
+ #
36
+ # @return [Array<String>]
37
+ #
38
+ # @api private
39
+ def rack_keys
40
+ %w(version multithread multiprocess run_once url_scheme hijack? hijack_io
41
+ timestamp request.query_string request.query_hash request.cookie_hash
42
+ request.cookie_string).map do |key|
43
+ "rack.#{key}"
44
+ end
45
+ end
46
+
47
+ # Action dispatch keys in the rack env
48
+ #
49
+ # @return [Array<String>]
50
+ #
51
+ # @api private
52
+ def action_dispatch_keys
53
+ bases = %w(parameter_filter redirect_filter secret_token secret_key_base show_exceptions
54
+ show_detailed_exceptions http_auth_salt signed_cookie_salt
55
+ encrypted_cookie_salt encrypted_signed_cookie_salt cookies_serializer
56
+ cookies_digest request_id).map do |key|
57
+ "action_dispatch.#{key}"
58
+ end
59
+
60
+ requests = %w(path_parameters content_type request_parameters query_parameters
61
+ parameters formats unsigned_session_cookie).map do |key|
62
+ "action_dispatch.request.#{key}"
63
+ end
64
+
65
+ bases + requests
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,103 @@
1
+ module TrackerHub
2
+ class Request
3
+ class Config
4
+ # Default customizable configurations for normal/rolling logger
5
+ module Logger
6
+ ROLLING_PATTERN = '%Y-%m-%d-%H-%M-%S'.freeze
7
+ ROLLING_AGE = 'daily'.freeze
8
+ ROLLING_SIZE = 1.gigabyte.freeze
9
+ LOGGER_NAME = 'requests'.freeze
10
+ LOGFILE_NAME = "#{LOGGER_NAME}.log".freeze
11
+
12
+ class << self
13
+ # Template for the rolling logger configuration
14
+ # Note: for additional options, please refer to:
15
+ # http://www.rubydoc.info/gems/logging/Logging/Appenders/RollingFile:initialize
16
+ #
17
+ # @option args [String] :log_path ('/log') Path of the log file
18
+ # @option args [String] :logger_name ('requests') Name of the log file
19
+ # @option args [String] :logfile_pattern ('requests.log.%Y-%m-%d-%H-%M-%S')
20
+ # Pattern to roll log files
21
+ # @option args [String|Integer] :age ('daily') The maximum age (in seconds)
22
+ # of a log file before it is rolled.
23
+ # @option args [String] :size (1073741824) The maximum allowed size (in bytes)
24
+ # of a log file before it is rolled.
25
+ # @return [Logging::Logger]
26
+ #
27
+ # @example
28
+ # > options = { logger_name: 'my_logger' }
29
+ # > logger = TrackerHub::Request::Config::Logger.rolling_logger(options)
30
+ # > logger.info('log text')
31
+ #
32
+ # @api public
33
+ def rolling_logger(args = {})
34
+ options = extract_rolling_options(args)
35
+
36
+ build_rolling_logger(options)
37
+ end
38
+
39
+ # Default configuration for the logger
40
+ #
41
+ # @return [ActiveSupport::Logger]
42
+ #
43
+ # @example
44
+ # > logger = TrackerHub::Request::Config::Logger.default_config
45
+ # > logger.info('log text')
46
+ #
47
+ # @api public
48
+ def default_config
49
+ ::ActiveSupport::Logger.new(File.join(log_path, LOGFILE_NAME))
50
+ end
51
+
52
+ private
53
+
54
+ # Extract the given rolling logger arguments and merge with defaults
55
+ #
56
+ # @param [Hash] args Options to configure the rolling logger
57
+ # @return [Hash]
58
+ #
59
+ # @api private
60
+ def extract_rolling_options(args)
61
+ {
62
+ log_path: log_path,
63
+ logger_name: LOGGER_NAME,
64
+ logfile_pattern: "#{LOGFILE_NAME}{{.#{ROLLING_PATTERN}}}",
65
+
66
+ age: ROLLING_AGE,
67
+ size: ROLLING_SIZE
68
+ }.merge(args)
69
+ end
70
+
71
+ # Build a rolling logger based on the given options
72
+ #
73
+ # @param [Hash] options Options to configure the rolling logger
74
+ # @return [Logging]
75
+ #
76
+ # @api private
77
+ def build_rolling_logger(options)
78
+ logfile_path = File.join(options.delete(:log_path),
79
+ options.delete(:logfile_pattern))
80
+
81
+ logger = Logging.logger.new(options.delete(:logger_name))
82
+ rolling = Logging::Appenders::RollingFile.new(logfile_path, options)
83
+ logger.add_appenders(rolling)
84
+
85
+ logger
86
+ end
87
+
88
+ # Get the current rails log path
89
+ #
90
+ # @return [String]
91
+ #
92
+ # @api private
93
+ #
94
+ # :nocov:
95
+ def log_path
96
+ Pathname.new(Rails.application.config.paths['log'].first).parent.to_s
97
+ end
98
+ # :nocov:
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,66 @@
1
+ module TrackerHub
2
+ class Request
3
+ module Format
4
+ # Logger module formatting and rendering the request object
5
+ module Logger
6
+ # Format the response to the logger
7
+ #
8
+ # @return [JSON]
9
+ #
10
+ # @example
11
+ # > status, headers, body = @app.call(env)
12
+ # > track = TrackerHub::Request.new(env, status, headers)
13
+ # > track.to_logger
14
+ #
15
+ # @api public
16
+ def to_logger
17
+ {
18
+ status: status,
19
+ request: request,
20
+ response: cleaned_env,
21
+ app_version: Request.config.app_version,
22
+ tracker_version: Request::VERSION
23
+ }.to_json
24
+ end
25
+
26
+ # Extract the rack env keys (see TrackerHub::Request::Config#required_keys)
27
+ # and convert them
28
+ #
29
+ # @return [Utils::Env] cleaned rack env object
30
+ #
31
+ # @example
32
+ # > status, headers, body = @app.call(env)
33
+ # > track = TrackerHub::Request.new(env, status, headers)
34
+ # > track.cleaned_env
35
+ #
36
+ # @api public
37
+ def cleaned_env
38
+ response.slice(*Request.config.required_keys).merge(serialized_env)
39
+ end
40
+
41
+ # Serialize objects before jsonifying them
42
+ #
43
+ # @return [Hash]
44
+ #
45
+ # @example
46
+ # > status, headers, body = @app.call(env)
47
+ # > track = TrackerHub::Request.new(env, status, headers)
48
+ # > track.serialized_env
49
+ #
50
+ # @api public
51
+ #
52
+ # rubocop:disable AbcSize
53
+ def serialized_env
54
+ {
55
+ 'action_dispatch.logger' => response['action_dispatch.logger'].formatter.session_info,
56
+ 'action_dispatch.remote_ip' => response['action_dispatch.remote_ip'].to_s,
57
+ 'rack.session' => response['rack.session'].try(:to_hash),
58
+ 'rack.session.options' => response['rack.session.options'].try(:to_hash),
59
+ 'http_accept_language.parser' => response['http_accept_language.parser'].header
60
+ }
61
+ end
62
+ # rubocop:enable AbcSize
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'utils/env'
3
+ require_relative 'utils/exception'
4
+
5
+ module TrackerHub
6
+ class Request
7
+ # Middleware to include in a Rails stack to log every incoming requests
8
+ class Middleware
9
+ # Method called by the middleware stack to run the request tracker
10
+ #
11
+ # @param [Hash] env Rack env
12
+ # @return [Array]
13
+ #
14
+ # @api public
15
+ def call(env)
16
+ # execute application to get more data (injected into env)
17
+ status, headers, body = @app.call(env)
18
+ # save logs from env
19
+ track(env, status, headers)
20
+ # release the process to other middlewares
21
+ [status, headers, body]
22
+ end
23
+
24
+ private
25
+
26
+ # Instanciate the request tracker middleware
27
+ #
28
+ # @param [undefined] app Previous middelware
29
+ # @return [undefined]
30
+ #
31
+ # @api private
32
+ def initialize(app)
33
+ @logger = ::TrackerHub::Request.config.logger
34
+ @app = app
35
+ end
36
+
37
+ # Initiate the request tracker to store formated requests in a log file.
38
+ # If an internal error occurs while logging the request, and if a
39
+ # notification has been configured (see TrackerHub::Request::Notification),
40
+ # then a notification is sent to the configured service
41
+ #
42
+ # @param [Hash] env Rack env
43
+ # @param [Integer] status HTTP request status
44
+ # @param [Hash] headers Header of the HTTP request
45
+ # @return [undefined]
46
+ #
47
+ # @example
48
+ # > status, headers, body = @app.call(env)
49
+ # > track(env, status, headers)
50
+ #
51
+ # @api private
52
+ def track(env, status, headers)
53
+ new_env = Utils::Env.new(env)
54
+ return unless new_env.trackable?
55
+
56
+ @logger.info ::TrackerHub::Request.new(new_env, status, headers).to_logger
57
+ rescue StandardError => exception
58
+ notifier = ::TrackerHub::Request.config.notification
59
+ Utils::Exception.new(exception).report(notifier)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,95 @@
1
+ require_relative 'notification/hip_chat'
2
+
3
+ module TrackerHub
4
+ class Request
5
+ # Notify a configured service when an error occured during the request log process
6
+ class Notification
7
+ # Key to cache the timelapse between 2 notifications
8
+ KEY_CACHE = 'trackinghub_request_notification'.freeze
9
+
10
+ # @return [Object] the notification adapter
11
+ # @api public
12
+ attr_reader :notifier
13
+
14
+ # @return [Integer|ActiveSupport::Duration] timelapse between 2 notifications
15
+ # @api public
16
+ attr_reader :timelapse
17
+
18
+ # @return [Hash] notification and notifier options
19
+ # @api public
20
+ attr_reader :options
21
+
22
+ # Trigger a notification paused by a timelapse if specified
23
+ #
24
+ # @param [String] message Notification's message
25
+ # @param [Hash] args Notifier adapter's options (if there is any)
26
+ # @return [undefined]
27
+ #
28
+ # @example
29
+ # > notifier = TrackerHub::Request::Notification::HipChat.new(token, 'room', 'username')
30
+ # > options = { timelapse: 10.minutes }
31
+ # > notification = TrackerHub::Request::Notification.new(notifier, options)
32
+ # > notification.notify('my message')
33
+ #
34
+ # @api public
35
+ def notify(message, args = {})
36
+ timelapser do
37
+ notifier.send_message(message, options.merge(args))
38
+ end
39
+ rescue
40
+ false
41
+ end
42
+
43
+ private
44
+
45
+ # @return [Object] the notification adapter
46
+ # @api private
47
+ attr_writer :notifier
48
+
49
+ # @return [Integer|ActiveSupport::Duration] timelapse between 2 notifications
50
+ # @api private
51
+ attr_writer :timelapse
52
+
53
+ # @return [Hash] notification and notifier options
54
+ # @api private
55
+ attr_writer :options
56
+
57
+ # Instantiate a notification object to add the ability to send a notification
58
+ # to any service adapter (see in request/notification). If a timelapse is
59
+ # given in parameter, there will be a pause between 2 notifications.
60
+ # Please checkout the available adapters for more :args: options
61
+ #
62
+ # @param [Object] notifier The notification adapter
63
+ # @option args [Integer] :timelapse Time between 2 notifications
64
+ #
65
+ # @example
66
+ # > notifier = TrackerHub::Request::Notification::HipChat.new(token, 'room', 'username')
67
+ # > options = { timelapse: 10.minutes }
68
+ # > TrackerHub::Request::Notification.new(notifier, options)
69
+ #
70
+ # @api private
71
+ def initialize(notifier = nil, args = {})
72
+ defaults = {
73
+ notify: true
74
+ }
75
+
76
+ self.notifier = notifier
77
+ self.timelapse = args.delete(:timelapse)
78
+ self.options = defaults.merge(args)
79
+ end
80
+
81
+ # Execute an action in a timelapse
82
+ #
83
+ # @return [Boolean]
84
+ #
85
+ # @api private
86
+ def timelapser
87
+ return yield unless timelapse
88
+
89
+ Rails.cache.fetch(KEY_CACHE, expires_in: timelapse) do
90
+ yield
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,68 @@
1
+ module TrackerHub
2
+ class Request
3
+ class Notification
4
+ # Service to send notifications to when an error occured during the log process
5
+ class HipChat
6
+ # HipChat API version to call by default
7
+ API_VERSION = 'v2'.freeze
8
+
9
+ # Send a notification message
10
+ #
11
+ # @param [String] msg Message to send
12
+ # @param [Hash] args See more options at
13
+ # https://www.hipchat.com/docs/apiv2/method/send_room_notification
14
+ # @return [Boolean]
15
+ #
16
+ # @example
17
+ # > notifier = TrackerHub::Request::Notification::HipChat.new(token, 'room', 'username')
18
+ # > notifier.send_message('my message')
19
+ #
20
+ # @api public
21
+ def send_message(msg, args = {})
22
+ client[room].send(username, msg, options.merge(args))
23
+ end
24
+
25
+ private
26
+
27
+ # @return [HipChat::Client] the hipchat client
28
+ # @api private
29
+ attr_accessor :client
30
+
31
+ # @return [String] user's name sending the message
32
+ # @api private
33
+ attr_accessor :username
34
+
35
+ # @return [String] room's name to send the message to
36
+ # @api private
37
+ attr_accessor :room
38
+
39
+ # @return [Hash] hipchat send method options
40
+ # @api private
41
+ attr_accessor :options
42
+
43
+ # Instantiate a HipChat notifier. For more options, please checkout
44
+ # https://www.hipchat.com/docs/apiv2/method/send_room_notification
45
+ #
46
+ # @param [String] token API key to talk with hipchat services
47
+ # @param [String] room Room's name to send the message
48
+ # @param [String] username User's name sending the message
49
+ # @option options [String] :api_version Version of the hipchat API
50
+ # @return [TrackerHub::Request::Notification::HipChat]
51
+ #
52
+ # @example
53
+ # > TrackerHub::Request::Notification::HipChat.new(token, 'room', 'username')
54
+ #
55
+ # @api private
56
+ def initialize(token, room, username, options = {})
57
+ api_version = options.delete(:api_version) || API_VERSION
58
+
59
+ self.room = room
60
+ self.username = username
61
+ self.options = options
62
+
63
+ self.client = ::HipChat::Client.new(token, api_version: api_version)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,47 @@
1
+ require_relative 'config'
2
+
3
+ module TrackerHub
4
+ class Request
5
+ # Configure the request tracker
6
+ module Setup
7
+ # Get the current configuration, instantiate a new object if nil
8
+ # Note: each attribute can also be written through the config object
9
+ #
10
+ # @return [TrackerHub::Request::Config] configuration object
11
+ #
12
+ # @example
13
+ # > # get the current configuration
14
+ # > TrackerHub::Request.config
15
+ # > => #<TrackerHub::Request::Config:0x007fa574ad7390 ...>
16
+ # >
17
+ # > # set a specific value in the config
18
+ # > TrackerHub::Request.config.app_version = '4.2'
19
+ #
20
+ # @api public
21
+ def config
22
+ @config ||= self::Config.new
23
+ end
24
+
25
+ # Setup configuration in block
26
+ #
27
+ # @yield (see TrackerHub::Request::Config#initialize)
28
+ # @return [TrackerHub::Request::Config]
29
+ #
30
+ # @example
31
+ # > TrackerHub::Request.setup do |config|
32
+ # > config.app_version = '4.2'
33
+ # > end
34
+ #
35
+ # @api public
36
+ def setup
37
+ yield(config)
38
+ end
39
+
40
+ private
41
+
42
+ # @return [TrackerHub::Request::Config] the tracking request configuration
43
+ # @api private
44
+ attr_writer :config
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module TrackerHub
3
+ class Request
4
+ module Utils
5
+ # SimpleDelegator wrapping class for rack environment
6
+ class Env < SimpleDelegator
7
+ # Should the request tracker log the current request
8
+ #
9
+ # @return [Boolean] true if should track the current request,
10
+ # false if should not track the current request
11
+ #
12
+ # @example
13
+ # > new_env = Utils::Env.new(env)
14
+ # > new_env.trackable?
15
+ #
16
+ # @api public
17
+ def trackable?
18
+ '/assets' != self['SCRIPT_NAME']
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module TrackerHub
3
+ class Request
4
+ module Utils
5
+ # SimpleDelegator wrapping class for exceptions
6
+ class Exception < SimpleDelegator
7
+ # Send a report of the exception with a given notification
8
+ #
9
+ # @param [TrackerHub::Request::Notification] notification See in request/notification
10
+ # for a full list of available notifiers
11
+ # @param [undefined] framework Used framework to retreive the rack environment
12
+ # @return [Boolean]
13
+ #
14
+ # @example
15
+ # > notifier = TrackerHub::Request::Notification::HipChat.new(token, 'room', 'username')
16
+ # > notification = TrackerHub::Request::Notification.new(notifier)
17
+ # > new_exception = Utils::Exception.new(exception)
18
+ # > new_exception.report(notification)
19
+ #
20
+ # @api public
21
+ def report(notification, framework = Rails)
22
+ formatted_backtrace = backtrace.join("\n")
23
+ msg = "[#{framework.env}]\n#{message}\n#{formatted_backtrace}"
24
+ notification.notify(msg)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ module TrackerHub
2
+ class Request
3
+ VERSION = '1.0.1'.freeze
4
+ end
5
+ end
@@ -0,0 +1,42 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tracker_hub/request/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'tracker_hub-request'
8
+ spec.version = TrackerHub::Request::VERSION
9
+ spec.authors = ['Maxime Chaisse-Leal']
10
+ spec.email = ['maxime.chaisseleal@gmail.com']
11
+
12
+ spec.summary = 'Track all the incoming requests and store them in log files.'
13
+ spec.description = 'Track all the incoming requests and store them in log files.'
14
+ spec.homepage = 'https://github.com/SparkHub/gs-tracking-request'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_dependency 'logging', '~> 2.1'
25
+ spec.add_dependency 'hipchat', '~> 1.5'
26
+
27
+ if RUBY_VERSION >= '2.2.2'
28
+ spec.add_dependency 'activesupport'
29
+ else
30
+ spec.add_dependency 'activesupport', '< 5'
31
+ end
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.13'
34
+ spec.add_development_dependency 'rake', '~> 11.3'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+
37
+ if RUBY_VERSION >= '2.2.2'
38
+ spec.add_development_dependency 'rack'
39
+ else
40
+ spec.add_development_dependency 'rack', '~> 1.6'
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,170 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tracker_hub-request
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Maxime Chaisse-Leal
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logging
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hipchat
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '11.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '11.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Track all the incoming requests and store them in log files.
112
+ email:
113
+ - maxime.chaisseleal@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".codeclimate.yml"
119
+ - ".gitignore"
120
+ - ".hound.yml"
121
+ - ".reek"
122
+ - ".rspec"
123
+ - ".rubocop.yml"
124
+ - ".travis.yml"
125
+ - CHANGELOG.md
126
+ - CODE_OF_CONDUCT.md
127
+ - Gemfile
128
+ - LICENSE.txt
129
+ - README.md
130
+ - Rakefile
131
+ - bin/console
132
+ - bin/setup
133
+ - lib/tracker_hub/request.rb
134
+ - lib/tracker_hub/request/config.rb
135
+ - lib/tracker_hub/request/config/env_keys.rb
136
+ - lib/tracker_hub/request/config/logger.rb
137
+ - lib/tracker_hub/request/format/logger.rb
138
+ - lib/tracker_hub/request/middleware.rb
139
+ - lib/tracker_hub/request/notification.rb
140
+ - lib/tracker_hub/request/notification/hip_chat.rb
141
+ - lib/tracker_hub/request/setup.rb
142
+ - lib/tracker_hub/request/utils/env.rb
143
+ - lib/tracker_hub/request/utils/exception.rb
144
+ - lib/tracker_hub/request/version.rb
145
+ - tracker_hub-request.gemspec
146
+ homepage: https://github.com/SparkHub/gs-tracking-request
147
+ licenses:
148
+ - MIT
149
+ metadata: {}
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 2.5.1
167
+ signing_key:
168
+ specification_version: 4
169
+ summary: Track all the incoming requests and store them in log files.
170
+ test_files: []