sapience 0.1.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.
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.