peastash 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +129 -0
  6. data/README.md +183 -0
  7. data/Rakefile +17 -0
  8. data/lib/peastash/middleware.rb +37 -0
  9. data/lib/peastash/outputs/io.rb +21 -0
  10. data/lib/peastash/rails_ext/railtie.rb +30 -0
  11. data/lib/peastash/rails_ext/watch.rb +15 -0
  12. data/lib/peastash/rails_ext.rb +2 -0
  13. data/lib/peastash/version.rb +3 -0
  14. data/lib/peastash.rb +120 -0
  15. data/peastash.gemspec +31 -0
  16. data/spec/dummy/README.rdoc +28 -0
  17. data/spec/dummy/Rakefile +6 -0
  18. data/spec/dummy/app/assets/images/.keep +0 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  20. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  21. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  22. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  23. data/spec/dummy/app/controllers/welcome_controller.rb +5 -0
  24. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  25. data/spec/dummy/app/mailers/.keep +0 -0
  26. data/spec/dummy/app/models/.keep +0 -0
  27. data/spec/dummy/app/models/concerns/.keep +0 -0
  28. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  29. data/spec/dummy/bin/bundle +3 -0
  30. data/spec/dummy/bin/rails +4 -0
  31. data/spec/dummy/bin/rake +4 -0
  32. data/spec/dummy/config/application.rb +28 -0
  33. data/spec/dummy/config/boot.rb +5 -0
  34. data/spec/dummy/config/environment.rb +5 -0
  35. data/spec/dummy/config/environments/development.rb +34 -0
  36. data/spec/dummy/config/environments/production.rb +80 -0
  37. data/spec/dummy/config/environments/test.rb +40 -0
  38. data/spec/dummy/config/environments/test_with_specific_position.rb +41 -0
  39. data/spec/dummy/config/environments/test_without_buchestache.rb +39 -0
  40. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  41. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  42. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/spec/dummy/config/initializers/inflections.rb +16 -0
  44. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  45. data/spec/dummy/config/initializers/session_store.rb +3 -0
  46. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  47. data/spec/dummy/config/locales/en.yml +23 -0
  48. data/spec/dummy/config/routes.rb +56 -0
  49. data/spec/dummy/config/secrets.yml +22 -0
  50. data/spec/dummy/config.ru +4 -0
  51. data/spec/dummy/lib/assets/.keep +0 -0
  52. data/spec/dummy/log/.keep +0 -0
  53. data/spec/dummy/public/404.html +67 -0
  54. data/spec/dummy/public/422.html +67 -0
  55. data/spec/dummy/public/500.html +66 -0
  56. data/spec/dummy/public/favicon.ico +0 -0
  57. data/spec/peastash/middleware_spec.rb +144 -0
  58. data/spec/peastash/rails_ext/railtie_spec.rb +92 -0
  59. data/spec/peastash/rails_ext/watch_spec.rb +24 -0
  60. data/spec/peastash_spec.rb +213 -0
  61. data/spec/spec_helper.rb +31 -0
  62. metadata +289 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9736868c5166f30e722153e48ca37fdcee47d6bd
4
+ data.tar.gz: 99514ca3d88f07f2d0d7e72ab3354776b4243820
5
+ SHA512:
6
+ metadata.gz: 92ca9c0ba61390259c47f8cd54085519f6a3be4f5c7cc65f30eddd14a2327b66e22bf1144cb94b438dc51e841ca82889923d9ab5e6065836994f3c91f49ef943
7
+ data.tar.gz: a0f1b7e7b12de3a15a3ba15a327675574843faf4bcfb61cf24614ffa51e2033b3549e4184ba6472c70025b60c37835f32bf930dd9ef52659210021b1ac18b0d0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ coverage
2
+ pkg
3
+ .DS_STORE
4
+ *.log
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - jruby-19mode
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,129 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ peastash (0.0.8)
5
+ logstash-event
6
+ thread_safe
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actionmailer (4.1.1)
12
+ actionpack (= 4.1.1)
13
+ actionview (= 4.1.1)
14
+ mail (~> 2.5.4)
15
+ actionpack (4.1.1)
16
+ actionview (= 4.1.1)
17
+ activesupport (= 4.1.1)
18
+ rack (~> 1.5.2)
19
+ rack-test (~> 0.6.2)
20
+ actionview (4.1.1)
21
+ activesupport (= 4.1.1)
22
+ builder (~> 3.1)
23
+ erubis (~> 2.7.0)
24
+ activemodel (4.1.1)
25
+ activesupport (= 4.1.1)
26
+ builder (~> 3.1)
27
+ activerecord (4.1.1)
28
+ activemodel (= 4.1.1)
29
+ activesupport (= 4.1.1)
30
+ arel (~> 5.0.0)
31
+ activesupport (4.1.1)
32
+ i18n (~> 0.6, >= 0.6.9)
33
+ json (~> 1.7, >= 1.7.7)
34
+ minitest (~> 5.1)
35
+ thread_safe (~> 0.1)
36
+ tzinfo (~> 1.1)
37
+ arel (5.0.1.20140414130214)
38
+ builder (3.2.2)
39
+ coderay (1.1.0)
40
+ diff-lcs (1.2.5)
41
+ erubis (2.7.0)
42
+ hike (1.2.3)
43
+ i18n (0.6.9)
44
+ json (1.8.1)
45
+ logstash-event (1.2.02)
46
+ mail (2.5.4)
47
+ mime-types (~> 1.16)
48
+ treetop (~> 1.4.8)
49
+ method_source (0.8.2)
50
+ mime-types (1.25.1)
51
+ minitest (5.3.4)
52
+ multi_json (1.10.0)
53
+ polyglot (0.3.4)
54
+ pry (0.9.12.6)
55
+ coderay (~> 1.0)
56
+ method_source (~> 0.8)
57
+ slop (~> 3.4)
58
+ rack (1.5.2)
59
+ rack-test (0.6.2)
60
+ rack (>= 1.0)
61
+ rails (4.1.1)
62
+ actionmailer (= 4.1.1)
63
+ actionpack (= 4.1.1)
64
+ actionview (= 4.1.1)
65
+ activemodel (= 4.1.1)
66
+ activerecord (= 4.1.1)
67
+ activesupport (= 4.1.1)
68
+ bundler (>= 1.3.0, < 2.0)
69
+ railties (= 4.1.1)
70
+ sprockets-rails (~> 2.0)
71
+ railties (4.1.1)
72
+ actionpack (= 4.1.1)
73
+ activesupport (= 4.1.1)
74
+ rake (>= 0.8.7)
75
+ thor (>= 0.18.1, < 2.0)
76
+ rake (10.3.1)
77
+ rspec (2.14.1)
78
+ rspec-core (~> 2.14.0)
79
+ rspec-expectations (~> 2.14.0)
80
+ rspec-mocks (~> 2.14.0)
81
+ rspec-core (2.14.8)
82
+ rspec-expectations (2.14.5)
83
+ diff-lcs (>= 1.1.3, < 2.0)
84
+ rspec-mocks (2.14.6)
85
+ rspec-rails (2.14.1)
86
+ actionpack (>= 3.0)
87
+ activemodel (>= 3.0)
88
+ activesupport (>= 3.0)
89
+ railties (>= 3.0)
90
+ rspec-core (~> 2.14.0)
91
+ rspec-expectations (~> 2.14.0)
92
+ rspec-mocks (~> 2.14.0)
93
+ simplecov (0.7.1)
94
+ multi_json (~> 1.0)
95
+ simplecov-html (~> 0.7.1)
96
+ simplecov-html (0.7.1)
97
+ slop (3.5.0)
98
+ sprockets (2.12.1)
99
+ hike (~> 1.2)
100
+ multi_json (~> 1.0)
101
+ rack (~> 1.0)
102
+ tilt (~> 1.1, != 1.3.0)
103
+ sprockets-rails (2.1.3)
104
+ actionpack (>= 3.0)
105
+ activesupport (>= 3.0)
106
+ sprockets (~> 2.8)
107
+ thor (0.19.1)
108
+ thread_safe (0.3.4)
109
+ tilt (1.4.1)
110
+ timecop (0.7.1)
111
+ treetop (1.4.15)
112
+ polyglot
113
+ polyglot (>= 0.3.1)
114
+ tzinfo (1.2.0)
115
+ thread_safe (~> 0.1)
116
+
117
+ PLATFORMS
118
+ ruby
119
+
120
+ DEPENDENCIES
121
+ peastash!
122
+ pry
123
+ rack-test
124
+ rails
125
+ rake
126
+ rspec (~> 2.14)
127
+ rspec-rails
128
+ simplecov (~> 0.7.1)
129
+ timecop
data/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # Peastash [![build status](https://travis-ci.org/elhu/peastash.png?branch=master)](https://travis-ci.org/elhu/peastash) [![Code Climate](https://codeclimate.com/github/elhu/peastash.png)](https://codeclimate.com/github/elhu/peastash)
2
+
3
+ ## Description
4
+
5
+ Peastash helps you instrument your Ruby code with the ELK stack. It provides useful methods to easily write logs in a format that Logstash can understand, which will turn in (hopefully) beautiful dashboards using Elasticsearch and Kibana.
6
+
7
+ The design philosophy behind Peastash is:
8
+ * Be as unobstrusive as possible. Your code shouldn't be riddled with references to Peastash.
9
+ * Keep the code-base small and well-tested
10
+ * Provide sane default that will work for most people out of the box
11
+
12
+ Any ruby application can benefit from having clear metrics, and now you have one less excuse to start instrumenting your code today.
13
+
14
+ ## Usage
15
+
16
+ ### Installing
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'peastash'
22
+ ```
23
+
24
+ ### Getting started
25
+ ``Peastash`` provides a simple interface to aggregate data to be sent to Logstash.
26
+ The boundaries of a log entry are defined by a the ``Peastash.with_instance.log`` block.
27
+ When the code inside the block is done running a log entry will be created and written.
28
+ The most basic usage would look like:
29
+
30
+ ```ruby
31
+ Peastash.with_instance.log do
32
+ Peastash.with_instance.store[:foo] = 'bar'
33
+ end
34
+ # This will produce a Logstash log entry looking like this:
35
+ # {"@source":"peastash","@fields":{"foo":"bar"},"@tags":[],"@timestamp":"2014-05-27T15:18:29.824Z","@version":"1"}
36
+ ```
37
+
38
+ ### Configuration
39
+
40
+ ``Peastash`` ships with sane defaults, but can be easily configured:
41
+
42
+ ```ruby
43
+ Peastash.with_instance.configure!({
44
+ source: 'peastash', # This value will be used for the @source field of the logstash event
45
+ # Any tag you wish to find in Logstash later (tags passed as argument to #log are added).
46
+ tags: [], # Defaults to [].
47
+ # Dumps to STDOUT by default. Any object answering to the dump method
48
+ output: Peastash::Outputs::IO.new('/tmp/peastash.log'),
49
+ store_name: 'peastash', # The key used to reference the store in the Thread.current
50
+ dump_if_empty: true # Whether or not Peastash should produce a log entry when the store is empty
51
+ })
52
+ ```
53
+
54
+ #### Safety
55
+
56
+ ``Peastash`` is safe by default. This means that if anything raises, Peastash will keep on going with the program's execution flow to be as unobtrusive as possible.
57
+ If this behaviour is unsuitable for your needs (for example if you want to test the new Peastash output you've just developped), simply call:
58
+
59
+ ```ruby
60
+ Peastash.unsafe!
61
+ ```
62
+ Please note that this is a global setting and cannot be set per-instance.
63
+
64
+ #### Outputs
65
+
66
+ Peastash ships with a single output: ``Peastash::Outputs::IO``. It can be initialized either by passing it path or an IO object.
67
+ Peastash can easily be extended to output to any target.
68
+ Simply configure Peastash's output with an object that responds to the ``#dump`` method. This method will be called at the end of the ``#log`` block, with 1 argument : a ``LogStash::Event`` object, that you will probably need to serialize to json.
69
+
70
+ ### What if I want to use it in my rack app?
71
+
72
+ There's a middleware for that! Simply add the following somewhere:
73
+
74
+ ```ruby
75
+ require 'peastash/middleware'
76
+ use Peastash::Middleware
77
+ ```
78
+
79
+ By default, the middleware only adds the ``duration``, ``status`` and ``hostname`` (machine name) fields to the log entry.
80
+ In addition to using ``Peastash.with_instance.store`` to add information, you can pass one or two block arguments to ``use``, that will be called with the request env and the Rack response in parameter, in the context of the Middleware's instance.
81
+ The first block will be called **before** the request (with a ``response = nil``), while the second one will be called **after**, with the actual response. For example:
82
+
83
+ ```ruby
84
+ require 'peastash/middleware'
85
+ before_block = ->(env, response) { Peastash.with_instance.store[:path] = Rack::Request.new(env).path }
86
+ after_block = ->(env, response) { Peastash.with_instance.store[:headers] = response[1] }
87
+ use Peastash::Middleware, before_block, after_block
88
+ # Will add 'path' and headers to the list of fields.
89
+ ```
90
+
91
+ Any instance variable you set in ``before_block`` will be available in ``after_block``, but those instances variables **WILL NOT** be reset at the end of the request.
92
+
93
+ ### But I want to use it in Rails!
94
+
95
+ It's easy! Simply add Peastash to your Gemfile, and add the following to the configure block of either ``config/environment.rb`` or ``config/<RAILS_ENV>.rb``:
96
+
97
+ ```ruby
98
+ config.peastash.enabled = true
99
+ # You can also configure Peastash from here, for example:
100
+ config.peastash.output = Peastash::Outputs::IO.new(File.join(Rails.root, 'log', "logstash_#{Rails.env}.log"))
101
+ config.peastash.source = Rails.application.class.parent_name
102
+ ```
103
+
104
+ By default, Peastash's Rails integration will log the same parameters as the Middleware version, plus the fields in the payload of the [``process_action.action_controller``](http://edgeguides.rubyonrails.org/active_support_instrumentation.html#process_action.action_controller) notification (except the params).
105
+
106
+ All the options for ``Peastash`` can be set using the ``config.peastash`` configuratio object.
107
+
108
+ #### Logging request parameters
109
+
110
+ To enable parameter logging, you must add the following to your configuration:
111
+
112
+ ```ruby
113
+ config.peastash.log_parameters = true
114
+ ```
115
+
116
+ Be careful, as this can significantly increase the size of the log entries, as well as causing problem if other Logstash entries have the same field with a different data type.
117
+
118
+ #### Listening to ``ActiveSupport::Notifications``
119
+ Additionaly, you can use Peastash to aggregate data from any ``ActiveSupport::Notifications``:
120
+
121
+ ```ruby
122
+ # In config/initializers/peastash.rb
123
+ if defined?(Peastash.with_instance.watch)
124
+ Peastash.with_instance.watch('request.rsolr', event_group: 'solr') do |name, start, finish, id, payload, event_store|
125
+ event_store[:queries] = event_store[:queries].to_i.succ
126
+ event_store[:duration] = event_store[:duration].to_f + ((finish - start) * 1000)
127
+ end
128
+ end
129
+ # This will add something like the following to the log entry fields
130
+ # {'request.rsolr': {'queries': 1, 'duration': 42}}
131
+ ```
132
+
133
+ The store exposed to the blocked passed to watch is thread-safe, and reset after each request. By default, the store is only shared between occurences of the same event. You can easily share the same store between different types of notifications by assigning them to the same event group:
134
+
135
+ ```ruby
136
+ Peastash.with_instance.watch('foo.notification', event_group: 'notification') do |*args, store|
137
+ # Shared store with 'bar.notification'
138
+ end
139
+
140
+ Peastash.with_instance.watch('bar.notification', event_group: 'notification') do |*args, store|
141
+ # Shared store with 'foo.notification'
142
+ end
143
+ ```
144
+
145
+ ### Playing with multiple instances
146
+
147
+ Calling ``Peastash.with_instance`` is the same as calling ``Peastash.with_instance(:global)``.
148
+ You can use different instances to log different things.
149
+ Each instance as its own:
150
+
151
+ * Configuration (inherited from the ``global`` instance)
152
+ * Store
153
+ * Tags
154
+
155
+ Using several instances, you can nest ``log`` blocks without sharing the store, or simply use them for different purposes.
156
+ For example, you can log your Rails query with the ``global`` instance, and your asynchronous jobs with a ``worker`` instance, and have those instances output to different files.
157
+
158
+ ### Playing with tags
159
+ There are three ways to tag your log entries in Peastash.with_instance.
160
+
161
+ * The first one is through configuration (see above).
162
+ * The second one is by passing tags to the ``Peastash.with_instance.log`` method.
163
+ * The last one is to call the ``Peastash.with_instance.tags`` method from within a ``Peastash.with_instance.log`` block. Tags defined with this method are not persistent and will disappear at the next call to ``Peastash.with_instance.log``
164
+
165
+ For example:
166
+
167
+ ```ruby
168
+ Peastash.with_instance.configure!(tags: 'foo')
169
+ Peastash.with_instance.log(['bar']) { Peastash.with_instance.tags << 'baz' }
170
+ # Tags are ['foo', 'bar', 'baz']
171
+ Peastash.with_instance.log(['bar']) { Peastash.with_instance.tags << 'qux' }
172
+ # Tags are ['foo', 'bar', 'qux']
173
+ ```
174
+
175
+ ## Contributing
176
+
177
+ 1. Fork it
178
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
179
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
180
+ 4. Push to the branch (`git push origin my-new-feature`)
181
+ 5. Create new Pull Request
182
+
183
+ Don't forget to run the tests with `rake`.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ $:.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new
7
+
8
+ task :default => :spec
9
+ task :test => :spec
10
+
11
+ task :console do
12
+ require 'irb'
13
+ require 'irb/completion'
14
+ require 'peastash'
15
+ ARGV.clear
16
+ IRB.start
17
+ end
@@ -0,0 +1,37 @@
1
+ require 'socket'
2
+
3
+ class Peastash
4
+ class Middleware
5
+ def initialize(app, before_block = nil, after_block = nil)
6
+ @app = app
7
+ define_singleton_method(:before_block, before_block || ->(_, _) {})
8
+ define_singleton_method(:after_block, after_block || ->(_, _) {})
9
+ end
10
+
11
+ def call(env)
12
+ response = nil
13
+ Peastash.with_instance.log do
14
+ start = Time.now
15
+
16
+ Peastash.safely do
17
+ before_block(env, response)
18
+
19
+ # Setting this before calling the next middleware so it can be overriden
20
+ request = Rack::Request.new(env)
21
+ Peastash.with_instance.store[:ip] = request.ip
22
+ end
23
+
24
+ response = @app.call(env)
25
+
26
+ Peastash.safely do
27
+ Peastash.with_instance.store[:duration] = ((Time.now - start) * 1000.0).round(2)
28
+ Peastash.with_instance.store[:status] = response.first
29
+
30
+ after_block(env, response)
31
+ end
32
+ end
33
+
34
+ response
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ require 'logger'
2
+
3
+ class Peastash
4
+ module Outputs
5
+ class IO
6
+ @@default_io = STDOUT
7
+
8
+ def self.default_io
9
+ @@default_io
10
+ end
11
+
12
+ def initialize(file)
13
+ @device = Logger::LogDevice.new(file)
14
+ end
15
+
16
+ def dump(event)
17
+ @device.write(event.to_json + "\n")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ require 'peastash/middleware'
2
+ require 'active_support/notifications'
3
+
4
+ class Peastash
5
+ class Railtie < ::Rails::Railtie
6
+ config.peastash = ActiveSupport::OrderedOptions.new
7
+
8
+ initializer 'peastash.configure' do |app|
9
+ if app.config.peastash[:enabled]
10
+ Peastash.with_instance.configure!(app.config.peastash)
11
+ ActiveSupport::Notifications.subscribe('process_action.action_controller') do |name, started, finished, unique_id, data|
12
+ # Handle parameters and sanitize if need be
13
+ to_reject = [:db_runtime, :view_runtime, :exception]
14
+ payload = data.reject { |key, _| to_reject.include?(key) }
15
+ payload.merge!(db: data[:db_runtime], view: data[:view_runtime])
16
+ payload.merge!(exception: { class: data[:exception].first, message: data[:exception].last[0..200] }) if data.has_key?(:exception)
17
+ if Peastash.with_instance.configuration[:log_parameters]
18
+ payload[:params].reject { |k, _| ActionController::LogSubscriber::INTERNAL_PARAMS.include?(k) }
19
+ else
20
+ payload.delete(:params)
21
+ end
22
+ # Preserve explicitely set data
23
+ Peastash.with_instance.store.merge!(payload) { |key, old_val, new_val| old_val }
24
+ end
25
+ before_middleware = app.config.peastash[:insert_before] || ActionDispatch::ShowExceptions
26
+ app.config.middleware.insert_before before_middleware, Peastash::Middleware
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support/notifications'
2
+
3
+ class Peastash
4
+ module Watch
5
+ def watch(event, opts = {}, &block)
6
+ event_group = opts[:event_group] || event
7
+ ActiveSupport::Notifications.subscribe(event) do |*args|
8
+ # Calling the processing block with the Notification args and the store
9
+ block.call(*args, self.store[event_group])
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Peastash.send :include, Peastash::Watch
@@ -0,0 +1,2 @@
1
+ require 'peastash/rails_ext/watch'
2
+ require 'peastash/rails_ext/railtie'
@@ -0,0 +1,3 @@
1
+ class Peastash
2
+ VERSION = "0.0.8"
3
+ end
data/lib/peastash.rb ADDED
@@ -0,0 +1,120 @@
1
+ require 'logstash/event'
2
+ require 'thread_safe'
3
+ require 'peastash/outputs/io'
4
+ require 'peastash/rails_ext' if defined?(Rails)
5
+
6
+ class Peastash
7
+
8
+ STORE_NAME = 'peastash'
9
+
10
+ class << self
11
+
12
+ def configure!(conf = {})
13
+ with_instance.configure!(conf)
14
+ end
15
+
16
+ def with_instance(instance_name = :global)
17
+ @@instance_cache[instance_name] ||= Peastash.new(instance_name)
18
+ end
19
+
20
+ def safely
21
+ yield
22
+ rescue StandardError => e
23
+ STDERR.puts e
24
+ raise e unless safe?
25
+ end
26
+
27
+ def safe?
28
+ !@unsafe
29
+ end
30
+
31
+ def safe!
32
+ @unsafe = false
33
+ end
34
+
35
+ def unsafe!
36
+ @unsafe = true
37
+ end
38
+
39
+ end
40
+
41
+ attr_reader :instance_name
42
+ attr_accessor :configuration
43
+
44
+ @@instance_cache = ThreadSafe::Cache.new
45
+
46
+ def initialize(instance_name)
47
+ @instance_name = instance_name
48
+
49
+ @configuration = {
50
+ :source => STORE_NAME,
51
+ :tags => [],
52
+ :output => Outputs::IO.new(Outputs::IO::default_io),
53
+ :store_name => STORE_NAME,
54
+ :dump_if_empty => true
55
+ }
56
+
57
+ configure!(@@instance_cache[:global].configuration || {}) if @@instance_cache[:global]
58
+ end
59
+
60
+ def store
61
+ Thread.current[instance_name] ||= Hash.new
62
+ Thread.current[instance_name][@store_name] ||= Hash.new { |hash, key| hash[key] = {} }
63
+ end
64
+
65
+ def configure!(conf = {})
66
+ self.configuration.merge!(conf)
67
+ @source = configuration[:source]
68
+ @base_tags = configuration[:tags].flatten
69
+ @output = configuration[:output]
70
+ @store_name = configuration[:store_name]
71
+ @dump_if_empty = configuration[:dump_if_empty]
72
+ @configured = true
73
+ end
74
+
75
+ def log(additional_tags = [])
76
+ Peastash.safely do
77
+ configure! unless configured?
78
+ tags.replace(additional_tags)
79
+ store.clear
80
+ end
81
+
82
+ yield(instance)
83
+
84
+ Peastash.safely do
85
+ if enabled? && (!store.empty? || dump_if_empty?)
86
+ event = build_event(@source, tags)
87
+ @output.dump(event)
88
+ end
89
+ end
90
+ end
91
+
92
+ def tags
93
+ Peastash.safely do
94
+ configure! unless configured?
95
+ Thread.current[@store_name + ":tags"] ||= []
96
+ end
97
+ end
98
+
99
+ def enabled?
100
+ !!configuration[:enabled]
101
+ end
102
+
103
+ def instance
104
+ @@instance_cache[instance_name]
105
+ end
106
+
107
+ private
108
+
109
+ def configured?
110
+ @configured
111
+ end
112
+
113
+ def dump_if_empty?
114
+ @dump_if_empty
115
+ end
116
+
117
+ def build_event(source, tags)
118
+ LogStash::Event.new('@source' => source, '@fields' => store, '@tags' => @base_tags + tags)
119
+ end
120
+ end
data/peastash.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'peastash/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "peastash"
8
+ gem.version = Peastash::VERSION
9
+ gem.authors = ["Vincent Boisard"]
10
+ gem.email = ["boisard.v@gmail.com"]
11
+ gem.description = %q{RequestStore gives you per-request global storage.}
12
+ gem.summary = %q{RequestStore gives you per-request global storage.}
13
+ gem.homepage = "http://github.com/elhu/peastash"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency "logstash-event"
21
+ gem.add_runtime_dependency "thread_safe"
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "rspec", '~> 2.14'
25
+ gem.add_development_dependency "rspec-rails"
26
+ gem.add_development_dependency "rack-test"
27
+ gem.add_development_dependency "simplecov", '~> 0.7.1'
28
+ gem.add_development_dependency "timecop"
29
+ gem.add_development_dependency "pry"
30
+ gem.add_development_dependency "rails"
31
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
File without changes
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .