sapience 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +13 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/.simplecov +15 -0
  7. data/.travis.yml +27 -0
  8. data/CODE_OF_CONDUCT.md +49 -0
  9. data/Gemfile +12 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +43 -0
  12. data/Rakefile +13 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/lib/sapience/ansi_colors.rb +27 -0
  16. data/lib/sapience/appender/file.rb +138 -0
  17. data/lib/sapience/appender/sentry.rb +72 -0
  18. data/lib/sapience/appender/statsd.rb +68 -0
  19. data/lib/sapience/appender/wrapper.rb +74 -0
  20. data/lib/sapience/base.rb +381 -0
  21. data/lib/sapience/concerns/compatibility.rb +53 -0
  22. data/lib/sapience/configuration.rb +86 -0
  23. data/lib/sapience/core_ext/thread.rb +14 -0
  24. data/lib/sapience/formatters/base.rb +36 -0
  25. data/lib/sapience/formatters/color.rb +68 -0
  26. data/lib/sapience/formatters/default.rb +41 -0
  27. data/lib/sapience/formatters/json.rb +22 -0
  28. data/lib/sapience/formatters/raw.rb +12 -0
  29. data/lib/sapience/log.rb +221 -0
  30. data/lib/sapience/loggable.rb +29 -0
  31. data/lib/sapience/logger.rb +236 -0
  32. data/lib/sapience/rails.rb +15 -0
  33. data/lib/sapience/sapience.rb +357 -0
  34. data/lib/sapience/subscriber.rb +127 -0
  35. data/lib/sapience/version.rb +3 -0
  36. data/lib/sapience.rb +35 -0
  37. data/sapience.gemspec +39 -0
  38. data/test_app/.gitignore +42 -0
  39. data/test_app/.rspec +2 -0
  40. data/test_app/.ruby-version +1 -0
  41. data/test_app/Gemfile +36 -0
  42. data/test_app/README.md +24 -0
  43. data/test_app/Rakefile +6 -0
  44. data/test_app/app/assets/config/manifest.js +2 -0
  45. data/test_app/app/assets/images/.keep +0 -0
  46. data/test_app/app/assets/javascripts/posts.js +2 -0
  47. data/test_app/app/assets/stylesheets/application.css +15 -0
  48. data/test_app/app/assets/stylesheets/posts.css +4 -0
  49. data/test_app/app/assets/stylesheets/scaffold.css +84 -0
  50. data/test_app/app/channels/application_cable/channel.rb +4 -0
  51. data/test_app/app/channels/application_cable/connection.rb +4 -0
  52. data/test_app/app/controllers/application_controller.rb +3 -0
  53. data/test_app/app/controllers/concerns/.keep +0 -0
  54. data/test_app/app/controllers/posts_controller.rb +58 -0
  55. data/test_app/app/helpers/application_helper.rb +2 -0
  56. data/test_app/app/helpers/posts_helper.rb +2 -0
  57. data/test_app/app/jobs/application_job.rb +2 -0
  58. data/test_app/app/mailers/application_mailer.rb +4 -0
  59. data/test_app/app/models/application_record.rb +3 -0
  60. data/test_app/app/models/concerns/.keep +0 -0
  61. data/test_app/app/models/post.rb +3 -0
  62. data/test_app/app/models/user.rb +2 -0
  63. data/test_app/app/views/layouts/application.html.erb +13 -0
  64. data/test_app/app/views/layouts/mailer.html.erb +13 -0
  65. data/test_app/app/views/layouts/mailer.text.erb +1 -0
  66. data/test_app/app/views/posts/_form.html.erb +32 -0
  67. data/test_app/app/views/posts/create.html.erb +2 -0
  68. data/test_app/app/views/posts/destroy.html.erb +2 -0
  69. data/test_app/app/views/posts/edit.html.erb +6 -0
  70. data/test_app/app/views/posts/index.html.erb +31 -0
  71. data/test_app/app/views/posts/new.html.erb +5 -0
  72. data/test_app/app/views/posts/show.html.erb +19 -0
  73. data/test_app/app/views/posts/update.html.erb +2 -0
  74. data/test_app/bin/bundle +3 -0
  75. data/test_app/bin/rails +4 -0
  76. data/test_app/bin/rake +4 -0
  77. data/test_app/bin/setup +34 -0
  78. data/test_app/bin/update +29 -0
  79. data/test_app/config/application.rb +26 -0
  80. data/test_app/config/boot.rb +3 -0
  81. data/test_app/config/cable.yml +9 -0
  82. data/test_app/config/database.yml +25 -0
  83. data/test_app/config/environment.rb +5 -0
  84. data/test_app/config/environments/development.rb +49 -0
  85. data/test_app/config/environments/production.rb +78 -0
  86. data/test_app/config/environments/test.rb +42 -0
  87. data/test_app/config/initializers/application_controller_renderer.rb +6 -0
  88. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  89. data/test_app/config/initializers/cookies_serializer.rb +5 -0
  90. data/test_app/config/initializers/filter_parameter_logging.rb +4 -0
  91. data/test_app/config/initializers/inflections.rb +16 -0
  92. data/test_app/config/initializers/mime_types.rb +4 -0
  93. data/test_app/config/initializers/new_framework_defaults.rb +24 -0
  94. data/test_app/config/initializers/session_store.rb +3 -0
  95. data/test_app/config/initializers/wrap_parameters.rb +14 -0
  96. data/test_app/config/locales/en.yml +23 -0
  97. data/test_app/config/puma.rb +45 -0
  98. data/test_app/config/routes.rb +3 -0
  99. data/test_app/config/secrets.yml +22 -0
  100. data/test_app/config.ru +5 -0
  101. data/test_app/db/migrate/20160812092236_create_users.rb +13 -0
  102. data/test_app/db/migrate/20160812093621_create_posts.rb +11 -0
  103. data/test_app/db/schema.rb +33 -0
  104. data/test_app/db/seeds.rb +7 -0
  105. data/test_app/lib/assets/.keep +0 -0
  106. data/test_app/lib/tasks/.keep +0 -0
  107. data/test_app/log/.keep +0 -0
  108. data/test_app/public/404.html +67 -0
  109. data/test_app/public/422.html +67 -0
  110. data/test_app/public/500.html +66 -0
  111. data/test_app/public/apple-touch-icon-precomposed.png +0 -0
  112. data/test_app/public/apple-touch-icon.png +0 -0
  113. data/test_app/public/favicon.ico +0 -0
  114. data/test_app/public/robots.txt +5 -0
  115. data/test_app/spec/controllers/posts_controller_spec.rb +7 -0
  116. data/test_app/spec/helpers/posts_helper_spec.rb +15 -0
  117. data/test_app/spec/models/post_spec.rb +5 -0
  118. data/test_app/spec/models/user_spec.rb +5 -0
  119. data/test_app/spec/rails_helper.rb +23 -0
  120. data/test_app/spec/requests/posts_spec.rb +10 -0
  121. data/test_app/spec/routing/posts_routing_spec.rb +39 -0
  122. data/test_app/spec/spec_helper.rb +60 -0
  123. data/test_app/tmp/.keep +0 -0
  124. data/test_app/vendor/assets/stylesheets/.keep +0 -0
  125. metadata +298 -0
@@ -0,0 +1,357 @@
1
+ require "concurrent"
2
+ require "socket"
3
+
4
+ # Example:
5
+ #
6
+ # Sapience.configure do |config|
7
+ # config.default_level = ENV.fetch('SAPIENCE_DEFAULT_LEVEL') { :info }.to_sym
8
+ # config.backtrace_level = ENV.fetch('SAPIENCE_BACKTRACE_LEVEL') { :info }.to_sym
9
+ # config.application = 'TestApplication'
10
+ # config.host = ENV.fetch('SAPIENCE_HOST', nil)
11
+ # config.ap_options = { multiline: false }
12
+ # config.appenders = [
13
+ # { file: { io: STDOUT, formatter: :color } },
14
+ # { statsd: { url: 'udp://localhost:2222' } },
15
+ # { sentry: { dsn: 'ASDFASDF' } },
16
+ # ]
17
+ # end
18
+
19
+ # rubocop:disable ClassVars
20
+ module Sapience
21
+ # 1. Have a default configuration
22
+ # 2. Configure Sapience (Sapience.configure { |c| c.configuration = {} })
23
+ # 3. Use configuration for rails
24
+ # 4. Use configuration for grape
25
+
26
+ # Logging levels in order of most detailed to most severe
27
+ LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
28
+
29
+ def self.config
30
+ @@config ||= Configuration.new
31
+ end
32
+
33
+ def self.configure
34
+ yield @@config
35
+
36
+ config.appenders.each do |appender|
37
+ appender.each do |name, options|
38
+ add_appender(name, options)
39
+ end
40
+ end
41
+ end
42
+
43
+ # Return a logger for the supplied class or class_name
44
+ def self.[](klass)
45
+ Sapience::Logger.new(klass)
46
+ end
47
+
48
+ # Add a new logging appender as a new destination for all log messages
49
+ # emitted from Sapience
50
+ #
51
+ # Appenders will be written to in the order that they are added
52
+ #
53
+ # If a block is supplied then it will be used to customize the format
54
+ # of the messages sent to that appender. See Sapience::Logger.new for
55
+ # more information on custom formatters
56
+ #
57
+ # Parameters
58
+ # file_name: [String]
59
+ # File name to write log messages to.
60
+ #
61
+ # Or,
62
+ # io: [IO]
63
+ # An IO Stream to log to.
64
+ # For example STDOUT, STDERR, etc.
65
+ #
66
+ # Or,
67
+ # appender: [Symbol|Sapience::Subscriber]
68
+ # A symbol identifying the appender to create.
69
+ # For example:
70
+ # :bugsnag, :elasticsearch, :graylog, :http, :mongodb, :new_relic, :splunk_http, :syslog, :wrapper
71
+ # Or,
72
+ # An instance of an appender derived from Sapience::Subscriber
73
+ # For example:
74
+ # Sapience::Appender::Http.new(url: 'http://localhost:8088/path')
75
+ #
76
+ # Or,
77
+ # logger: [Logger|Log4r]
78
+ # An instance of a Logger or a Log4r logger.
79
+ #
80
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
81
+ # Override the log level for this appender.
82
+ # Default: Sapience.config.default_level
83
+ #
84
+ # formatter: [Symbol|Object|Proc]
85
+ # Any of the following symbol values: :default, :color, :json
86
+ # Or,
87
+ # An instance of a class that implements #call
88
+ # Or,
89
+ # A Proc to be used to format the output from this appender
90
+ # Default: :default
91
+ #
92
+ # filter: [Regexp|Proc]
93
+ # RegExp: Only include log messages where the class name matches the supplied.
94
+ # regular expression. All other messages will be ignored.
95
+ # Proc: Only include log messages where the supplied Proc returns true
96
+ # The Proc must return true or false.
97
+ #
98
+ # Examples:
99
+ #
100
+ # # Send all logging output to Standard Out (Screen)
101
+ # Sapience.add_appender(:file, io: STDOUT)
102
+ #
103
+ # # Send all logging output to a file
104
+ # Sapience.add_appender(:file, file_name: 'logfile.log')
105
+ #
106
+ # # Send all logging output to a file and only :info and above to standard output
107
+ # Sapience.add_appender(:file, file_name: 'logfile.log')
108
+ # Sapience.add_appender(:file, io: STDOUT, level: :info)
109
+ #
110
+ # Log to log4r, Logger, etc.:
111
+ #
112
+ # # Send logging output to an existing logger
113
+ # require 'logger'
114
+ # require 'sapience'
115
+ #
116
+ # # Built-in Ruby logger
117
+ # log = Logger.new(STDOUT)
118
+ # log.level = Logger::DEBUG
119
+ #
120
+ # Sapience.config.default_level = :debug
121
+ # Sapience.add_appender(:wrapper, logger: log)
122
+ #
123
+ # logger = Sapience['Example']
124
+ # logger.info "Hello World"
125
+ # logger.debug("Login time", user: 'Joe', duration: 100, ip_address: '127.0.0.1')
126
+ def self.add_appender(appender, options, _deprecated_level = nil, &block)
127
+ fail ArgumentError, "options should be a hash" unless options.is_a?(Hash)
128
+ appender_class = constantize_symbol(appender)
129
+ appender = appender_class.new(options)
130
+ @@appenders << appender
131
+
132
+ # Start appender thread if it is not already running
133
+ Sapience::Logger.start_appender_thread
134
+ Sapience::Logger.logger = appender if appender.is_a?(Sapience::Appender::File)
135
+ appender
136
+ end
137
+
138
+ # Remove an existing appender
139
+ # Currently only supports appender instances
140
+ def self.remove_appender(appender)
141
+ @@appenders.delete(appender)
142
+ end
143
+
144
+ # Returns [Sapience::Subscriber] a copy of the list of active
145
+ # appenders for debugging etc.
146
+ # Use Sapience.add_appender and Sapience.remove_appender
147
+ # to manipulate the active appenders list
148
+ def self.appenders
149
+ @@appenders.clone
150
+ end
151
+
152
+ # Wait until all queued log messages have been written and flush all active
153
+ # appenders
154
+ def self.flush
155
+ Sapience::Logger.flush
156
+ end
157
+
158
+ # Close and flush all appenders
159
+ def self.close
160
+ Sapience::Logger.close
161
+ end
162
+
163
+ # After forking an active process call Sapience.reopen to re-open
164
+ # any open file handles etc to resources
165
+ #
166
+ # Note: Only appenders that implement the reopen method will be called
167
+ def self.reopen
168
+ @@appenders.each { |appender| appender.reopen if appender.respond_to?(:reopen) }
169
+ # After a fork the appender thread is not running, start it if it is not running
170
+ Sapience::Logger.start_appender_thread
171
+ end
172
+
173
+ # Add signal handlers for Sapience
174
+ #
175
+ # Two signal handlers will be registered by default:
176
+ #
177
+ # 1. Changing the log_level:
178
+ #
179
+ # The log level can be changed without restarting the process by sending the
180
+ # log_level_signal, which by default is 'USR2'
181
+ #
182
+ # When the log_level_signal is raised on this process, the global default log level
183
+ # rotates through the following log levels in the following order, starting
184
+ # from the current global default level:
185
+ # :warn, :info, :debug, :trace
186
+ #
187
+ # If the current level is :trace it wraps around back to :warn
188
+ #
189
+ # 2. Logging a Ruby thread dump
190
+ #
191
+ # When the signal is raised on this process, Sapience will write the list
192
+ # of threads to the log file, along with their back-traces when available
193
+ #
194
+ # It is recommended to name any threads you create in the application, by
195
+ # calling the following from within the thread itself:
196
+ # Thread.current.name = 'My Worker'
197
+ #
198
+ #
199
+ # Note:
200
+ # To only register one of the signal handlers, set the other to nil
201
+ # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity
202
+ def self.add_signal_handler(log_level_signal = "USR2", thread_dump_signal = "TTIN", _gc_log_microseconds = 100_000)
203
+ Signal.trap(log_level_signal) do
204
+ index = (default_level == :trace) ? LEVELS.find_index(:error) : LEVELS.find_index(default_level)
205
+ new_level = LEVELS[index - 1]
206
+ self["Sapience"].warn "Changed global default log level to #{new_level.inspect}"
207
+ self.default_level = new_level
208
+ end if log_level_signal
209
+
210
+ Signal.trap(thread_dump_signal) do
211
+ logger = Sapience["Thread Dump"]
212
+ Thread.list.each do |thread|
213
+ next if thread == Thread.current
214
+ message = thread.name
215
+ if (backtrace = thread.backtrace)
216
+ message += "\n"
217
+ message << backtrace.join("\n")
218
+ end
219
+ tags = thread[:sapience_tags]
220
+ tags = tags.nil? ? [] : tags.clone
221
+ logger.tagged(tags) { logger.warn(message) }
222
+ end
223
+ end if thread_dump_signal
224
+
225
+ true
226
+ end
227
+
228
+ # rubocop:enable AbcSize, CyclomaticComplexity, PerceivedComplexity
229
+
230
+ # If the tag being supplied is definitely a string then this fast
231
+ # tag api can be used for short lived tags
232
+ def self.fast_tag(tag)
233
+ (Thread.current[:sapience_tags] ||= []) << tag
234
+ yield
235
+ ensure
236
+ Thread.current[:sapience_tags].pop
237
+ end
238
+
239
+ # Add the supplied named tags to the list of tags to log for this thread whilst
240
+ # the supplied block is active.
241
+ #
242
+ # Returns result of block
243
+ #
244
+ # Example:
245
+ def self.named_tags(tag)
246
+ (Thread.current[:sapience_tags] ||= []) << tag
247
+ yield
248
+ ensure
249
+ Thread.current[:sapience_tags].pop
250
+ end
251
+
252
+ # Add the supplied tags to the list of tags to log for this thread whilst
253
+ # the supplied block is active.
254
+ # Returns result of block
255
+ def self.tagged(*tags)
256
+ new_tags = push_tags(*tags)
257
+ yield self
258
+ ensure
259
+ pop_tags(new_tags.size)
260
+ end
261
+
262
+ # Returns a copy of the [Array] of [String] tags currently active for this thread
263
+ # Returns nil if no tags are set
264
+ def self.tags
265
+ # Since tags are stored on a per thread basis this list is thread-safe
266
+ t = Thread.current[:sapience_tags]
267
+ t.nil? ? [] : t.clone
268
+ end
269
+
270
+ # Add tags to the current scope
271
+ # Returns the list of tags pushed after flattening them out and removing blanks
272
+ def self.push_tags(*tags)
273
+ # Need to flatten and reject empties to support calls from Rails 4
274
+ new_tags = tags.flatten.collect(&:to_s).reject(&:empty?)
275
+ t = Thread.current[:sapience_tags]
276
+ Thread.current[:sapience_tags] = t.nil? ? new_tags : t.concat(new_tags)
277
+ new_tags
278
+ end
279
+
280
+ # Remove specified number of tags from the current tag list
281
+ def self.pop_tags(quantity = 1)
282
+ t = Thread.current[:sapience_tags]
283
+ t.pop(quantity) unless t.nil?
284
+ end
285
+
286
+ # Silence noisy log levels by changing the default_level within the block
287
+ #
288
+ # This setting is thread-safe and only applies to the current thread
289
+ #
290
+ # Any threads spawned within the block will not be affected by this setting
291
+ #
292
+ # #silence can be used to both raise and lower the log level within
293
+ # the supplied block.
294
+ #
295
+ # Example:
296
+ #
297
+ # # Perform trace level logging within the block when the default is higher
298
+ # Sapience.config.default_level = :info
299
+ #
300
+ # logger.debug 'this will _not_ be logged'
301
+ #
302
+ # Sapience.silence(:trace) do
303
+ # logger.debug "this will be logged"
304
+ # end
305
+ #
306
+ # Parameters
307
+ # new_level
308
+ # The new log level to apply within the block
309
+ # Default: :error
310
+ #
311
+ # Example:
312
+ # # Silence all logging for this thread below :error level
313
+ # Sapience.silence do
314
+ # logger.info "this will _not_ be logged"
315
+ # logger.warn "this neither"
316
+ # logger.error "but errors will be logged"
317
+ # end
318
+ #
319
+ # Note:
320
+ # #silence does not affect any loggers which have had their log level set
321
+ # explicitly. I.e. That do not rely on the global default level
322
+ def self.silence(new_level = :error)
323
+ current_index = Thread.current[:sapience_silence]
324
+ Thread.current[:sapience_silence] = Sapience.config.level_to_index(new_level)
325
+ yield
326
+ ensure
327
+ Thread.current[:sapience_silence] = current_index
328
+ end
329
+
330
+ private
331
+
332
+ @@appenders = Concurrent::Array.new
333
+
334
+ def self.constantize_symbol(symbol, namespace = "Sapience::Appender")
335
+ klass = "#{namespace}::#{camelize(symbol.to_s)}"
336
+ begin
337
+ if RUBY_VERSION.to_i >= 2
338
+ Object.const_get(klass)
339
+ else
340
+ klass.split("::").inject(Object) { |o, name| o.const_get(name) } # rubocop:disable SingleLineBlockParams
341
+ end
342
+ rescue NameError
343
+ raise(ArgumentError, "Could not convert symbol: #{symbol} to a class in: #{namespace}. Looking for: #{klass}")
344
+ end
345
+ end
346
+
347
+ # Borrow from Rails, when not running Rails
348
+ def self.camelize(term) # rubocop:disable AbcSize
349
+ string = term.to_s
350
+ string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
351
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) do
352
+ "#{Regexp.last_match[1]}#{inflections.acronyms[Regexp.last_match[2]] || Regexp.last_match[2].capitalize}"
353
+ end
354
+ string.gsub!("/".freeze, "::".freeze)
355
+ string
356
+ end
357
+ end
@@ -0,0 +1,127 @@
1
+ # Abstract Subscriber
2
+ #
3
+ # Abstract base class for appender and metrics subscribers.
4
+ module Sapience
5
+ class Subscriber < Sapience::Base
6
+ # Every logger has its own formatter
7
+ attr_accessor :formatter
8
+ attr_writer :application, :host
9
+
10
+ # Returns the current log level if set, otherwise it logs everything it receives
11
+ def level
12
+ @level || :trace
13
+ end
14
+
15
+ # A subscriber should implement flush if it can.
16
+ def flush
17
+ # NOOP
18
+ end
19
+
20
+ # A subscriber should implement close if it can.
21
+ def close
22
+ # NOOP
23
+ end
24
+
25
+ # Returns [Sapience::Formatters::Default] formatter default for this subscriber
26
+ def default_formatter
27
+ Sapience::Formatters::Default.new
28
+ end
29
+
30
+ # Allow application name to be set globally or per subscriber
31
+ def application
32
+ @application || Sapience.config.application
33
+ end
34
+
35
+ # Allow host name to be set globally or per subscriber
36
+ def host
37
+ @host || Sapience.config.host
38
+ end
39
+
40
+ private
41
+
42
+ # Initializer for Abstract Class Sapience::Subscriber
43
+ #
44
+ # Parameters
45
+ # level: [:trace | :debug | :info | :warn | :error | :fatal]
46
+ # Override the log level for this subscriber.
47
+ # Default: :error
48
+ #
49
+ # formatter: [Object|Proc]
50
+ # An instance of a class that implements #call, or a Proc to be used to format
51
+ # the output from this subscriber
52
+ # Default: Use the built-in formatter (See: #call)
53
+ #
54
+ # filter: [Regexp|Proc]
55
+ # RegExp: Only include log messages where the class name matches the supplied.
56
+ # regular expression. All other messages will be ignored.
57
+ # Proc: Only include log messages where the supplied Proc returns true
58
+ # The Proc must return true or false.
59
+ #
60
+ # host: [String]
61
+ # Name of this host to appear in log messages.
62
+ # Default: Sapience.config.host
63
+ #
64
+ # application: [String]
65
+ # Name of this application to appear in log messages.
66
+ # Default: Sapience.config.application
67
+ def initialize(options = {}, &block)
68
+ # Backward compatibility
69
+ options = { level: options } unless options.is_a?(Hash)
70
+ options = options.dup
71
+ level = options.delete(:level)
72
+ filter = options.delete(:filter)
73
+ @formatter = extract_formatter(options.delete(:formatter), &block)
74
+ @application = options.delete(:application)
75
+ @host = options.delete(:host)
76
+ fail(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
77
+
78
+ # Subscribers don't take a class name, so use this class name if an subscriber
79
+ # is logged to directly
80
+ super(self.class, level, filter)
81
+ end
82
+
83
+ # Return the level index for fast comparisons
84
+ # Returns the lowest level index if the level has not been explicitly
85
+ # set for this instance
86
+ def level_index
87
+ @level_index || 0
88
+ end
89
+
90
+ # Return formatter that responds to call
91
+ # Supports formatter supplied as:
92
+ # - Symbol
93
+ # - Hash ( Symbol => { options })
94
+ # - Instance of any of Sapience::Formatters
95
+ # - Proc
96
+ # - Any object that responds to :call
97
+ # - If none of the above apply, then the supplied block is returned as the formatter.
98
+ # - Otherwise an instance of the default formatter is returned.
99
+ def extract_formatter(formatter, &block) # rubocop:disable Metrics/CyclomaticComplexity
100
+ case
101
+ when formatter.is_a?(Symbol)
102
+ Sapience.constantize_symbol(formatter, "Sapience::Formatters").new
103
+ when formatter.is_a?(Hash) && formatter.size > 0
104
+ fmt, options = formatter.first
105
+ Sapience.constantize_symbol(fmt.to_sym, "Sapience::Formatters").new(options)
106
+ when formatter.respond_to?(:call)
107
+ formatter
108
+ when block
109
+ block
110
+ when respond_to?(:call)
111
+ self
112
+ else
113
+ default_formatter
114
+ end
115
+ end
116
+
117
+ SUBSCRIBER_OPTIONS = [:level, :formatter, :filter, :application, :host].freeze
118
+
119
+ # Returns [Hash] the subscriber common options from the supplied Hash
120
+ def extract_subscriber_options!(options)
121
+ subscriber_options = {}
122
+ SUBSCRIBER_OPTIONS.each { |key| subscriber_options[key] = options.delete(key) if options.key?(key) }
123
+ subscriber_options
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module Sapience
2
+ VERSION = "0.1.1"
3
+ end
data/lib/sapience.rb ADDED
@@ -0,0 +1,35 @@
1
+ require "sapience/version"
2
+ require "sapience/sapience"
3
+
4
+ # @formatter:off
5
+
6
+ require "sapience/concerns/compatibility"
7
+
8
+ require "sapience/formatters/base"
9
+ require "sapience/formatters/raw"
10
+ require "sapience/formatters/default"
11
+ require "sapience/formatters/color"
12
+ require "sapience/formatters/json"
13
+
14
+ require "sapience/configuration"
15
+ require "sapience/ansi_colors"
16
+ require "sapience/core_ext/thread"
17
+ require "sapience/base"
18
+ require "sapience/log"
19
+ require "sapience/logger"
20
+ require "sapience/loggable"
21
+ require "sapience/subscriber"
22
+
23
+ require "sapience/appender/file"
24
+ require "sapience/appender/sentry"
25
+ require "sapience/appender/wrapper"
26
+ require "sapience/appender/statsd"
27
+
28
+ # @formatter:on
29
+
30
+ # Close and flush all appenders at exit, waiting for outstanding messages on the queue
31
+ # to be written first
32
+ at_exit do
33
+ # Cannot call #close since test frameworks use at_exit to run loaded tests
34
+ Sapience.flush
35
+ end
data/sapience.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "sapience/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sapience"
8
+ spec.version = Sapience::VERSION
9
+ spec.authors = ["Mikael Henriksson", "Alex Malkov"]
10
+ spec.email = ["mika@reevoo.com", "alex.malkov@reevoo.com"]
11
+
12
+ spec.summary = "Write a short summary, because Rubygems requires one."
13
+ spec.description = "Write a longer description or delete this line."
14
+ spec.homepage = "https://sapience.github.io"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
21
+ else
22
+ fail "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^(test|spec|features)\//) }
26
+ spec.bindir = "bin"
27
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "concurrent-ruby"
31
+ spec.add_development_dependency "bundler", "~> 1.12"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "rspec", "~> 3.0"
34
+ spec.add_development_dependency "reevoocop"
35
+ spec.add_development_dependency "fuubar"
36
+ spec.add_development_dependency "simplecov"
37
+ spec.add_development_dependency "simplecov-json"
38
+ spec.add_development_dependency "rspec-its"
39
+ end
@@ -0,0 +1,42 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /log
5
+ /tmp
6
+ /db/*.sqlite3
7
+ /db/*.sqlite3-journal
8
+ /public/system
9
+ /coverage/
10
+ /spec/tmp
11
+ **.orig
12
+ rerun.txt
13
+ pickle-email-*.html
14
+
15
+ # TODO Comment out these rules if you are OK with secrets being uploaded to the repo
16
+ config/initializers/secret_token.rb
17
+ config/secrets.yml
18
+
19
+ # dotenv
20
+ # TODO Comment out this rule if environment variables can be committed
21
+ .env
22
+
23
+ ## Environment normalization:
24
+ /.bundle
25
+ /vendor/bundle
26
+
27
+ # these should all be checked in to normalize the environment:
28
+ # Gemfile.lock, .ruby-version, .ruby-gemset
29
+
30
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
31
+ .rvmrc
32
+
33
+ # if using bower-rails ignore default bower_components path bower.json files
34
+ /vendor/assets/bower_components
35
+ *.bowerrc
36
+ bower.json
37
+
38
+ # Ignore pow environment settings
39
+ .powenv
40
+
41
+ # Ignore Byebug command history file.
42
+ .byebug_history
data/test_app/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1 @@
1
+ 2.3.1
data/test_app/Gemfile ADDED
@@ -0,0 +1,36 @@
1
+ source "https://rubygems.org"
2
+
3
+
4
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
5
+ gem "rails", "~> 5.0.0"
6
+ # Use sqlite3 as the database for Active Record
7
+ gem "sqlite3"
8
+ # Use Puma as the app server
9
+ gem "puma", "~> 3.0"
10
+ # Use Redis adapter to run Action Cable in production
11
+ # gem 'redis', '~> 3.0'
12
+ # Use ActiveModel has_secure_password
13
+ # gem 'bcrypt', '~> 3.1.7'
14
+
15
+ # Use Capistrano for deployment
16
+ # gem 'capistrano-rails', group: :development
17
+ gem "sapience", path: "../"
18
+ gem "sentry-raven"
19
+ gem "statsd-ruby"
20
+
21
+ group :development, :test do
22
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
23
+ gem "byebug", platform: :mri
24
+ # gem "pry-nav"
25
+ gem "pry-byebug"
26
+ gem "rspec-rails"
27
+ end
28
+
29
+ group :development do
30
+ # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
31
+ gem "web-console"
32
+ gem "listen", "~> 3.0.5"
33
+ end
34
+
35
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
36
+ gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
@@ -0,0 +1,24 @@
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
+ * ...
data/test_app/Rakefile ADDED
@@ -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_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,2 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.