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,22 @@
1
+ require "json"
2
+ module Sapience
3
+ module Formatters
4
+ class Json < Raw
5
+ # Default JSON time format is ISO8601
6
+ def initialize(options = {})
7
+ options = options.dup
8
+ options[:time_format] = :iso_8601 unless options.key?(:time_format)
9
+ super(options)
10
+ end
11
+
12
+ # Returns log messages in JSON format
13
+ def call(log, logger)
14
+ h = super(log, logger)
15
+ h.delete(:time)
16
+ h[:timestamp] = format_time(log.time)
17
+ h.to_json
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ require "json"
2
+ module Sapience
3
+ module Formatters
4
+ class Raw < Base
5
+ # Returns log messages in Hash format
6
+ def call(log, logger)
7
+ log.to_h(log_host ? logger.host : nil, log_application ? logger.application : nil)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,221 @@
1
+ module Sapience
2
+ # Log Struct
3
+ #
4
+ # Structure for holding all log entries
5
+ #
6
+ # level
7
+ # Log level of the supplied log call
8
+ # :trace, :debug, :info, :warn, :error, :fatal
9
+ #
10
+ # thread_name
11
+ # Name of the thread in which the logging call was called
12
+ #
13
+ # name
14
+ # Class name supplied to the logging instance
15
+ #
16
+ # message
17
+ # Text message to be logged
18
+ #
19
+ # payload
20
+ # Optional Hash or Ruby Exception object to be logged
21
+ #
22
+ # time
23
+ # The time at which the log entry was created
24
+ #
25
+ # duration
26
+ # The time taken to complete a measure call
27
+ #
28
+ # tags
29
+ # Any tags active on the thread when the log call was made
30
+ #
31
+ # level_index
32
+ # Internal index of the log level
33
+ #
34
+ # exception
35
+ # Ruby Exception object to log
36
+ #
37
+ # metric [Object]
38
+ # Object supplied when measure_x was called
39
+ #
40
+ # backtrace [Array<String>]
41
+ # The backtrace captured at source when the log level >= Sapience.config.backtrace_level
42
+ #
43
+ # metric_amount [Numeric]
44
+ # Used for numeric or counter metrics.
45
+ # For example, the number of inquiries or, the amount purchased etc.
46
+ Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace, :metric_amount) do
47
+ MAX_EXCEPTIONS_TO_UNWRAP = 5
48
+ # Call the block for exception and any nested exception
49
+ def each_exception # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
50
+ # With thanks to https://github.com/bugsnag/bugsnag-ruby/blob/6348306e44323eee347896843d16c690cd7c4362/lib/bugsnag/notification.rb#L81
51
+ depth = 0
52
+ exceptions = []
53
+ ex = exception
54
+ while !ex.nil? && !exceptions.include?(ex) && exceptions.length < MAX_EXCEPTIONS_TO_UNWRAP
55
+ exceptions << ex
56
+ yield(ex, depth)
57
+
58
+ depth += 1
59
+ ex =
60
+ if ex.respond_to?(:cause) && ex.cause
61
+ ex.cause
62
+ elsif ex.respond_to?(:continued_exception) && ex.continued_exception
63
+ ex.continued_exception
64
+ elsif ex.respond_to?(:original_exception) && ex.original_exception
65
+ ex.original_exception
66
+ end
67
+ end
68
+ end
69
+
70
+ # Returns [String] the exception backtrace including all of the child / caused by exceptions
71
+ def backtrace_to_s
72
+ trace = ""
73
+ each_exception do |exception, i|
74
+ if i == 0
75
+ trace = (exception.backtrace || []).join("\n")
76
+ else
77
+ trace << "\nCause: #{exception.class.name}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
78
+ end
79
+ end
80
+ trace
81
+ end
82
+
83
+ # Returns [String] duration of the log entry as a string
84
+ # Returns nil if their is no duration
85
+ def duration_to_s
86
+ return unless duration
87
+ duration < 10.0 ? "#{"%.3f" % duration}ms" : "#{"%.1f" % duration}ms"
88
+ end
89
+
90
+ # Returns [String] the duration in human readable form
91
+ def duration_human # rubocop:disable AbcSize
92
+ return nil unless duration
93
+ seconds = duration / 1000
94
+ if seconds >= 86_400.0 # 1 day
95
+ "#{(seconds / 86_400).to_i}d #{Time.at(seconds).strftime("%-Hh %-Mm")}"
96
+ elsif seconds >= 3600.0 # 1 hour
97
+ Time.at(seconds).strftime("%-Hh %-Mm")
98
+ elsif seconds >= 60.0 # 1 minute
99
+ Time.at(seconds).strftime("%-Mm %-Ss")
100
+ elsif seconds >= 1.0 # 1 second
101
+ "#{"%.3f" % seconds}s"
102
+ else
103
+ duration_to_s
104
+ end
105
+ end
106
+
107
+ # Returns [String] single character upper case log level
108
+ def level_to_s
109
+ level.to_s[0..0].upcase
110
+ end
111
+
112
+ # Returns [String] the available process info
113
+ # Example:
114
+ # 18934:thread 23 test_logging.rb:51
115
+ def process_info(thread_name_length = 30)
116
+ file, line = file_name_and_line(true)
117
+ file_name = " #{file}:#{line}" if file
118
+
119
+ "#{$PROCESS_ID}:#{"%.#{thread_name_length}s" % thread_name}#{file_name}"
120
+ end
121
+
122
+ CALLER_REGEXP = /^(.*):(\d+).*/
123
+
124
+ # Extract the filename and line number from the last entry in the supplied backtrace
125
+ def extract_file_and_line(stack, short_name = false)
126
+ match = CALLER_REGEXP.match(stack.first)
127
+ [short_name ? File.basename(match[1]) : match[1], match[2].to_i]
128
+ end
129
+
130
+ # Returns [String, String] the file_name and line_number from the backtrace supplied
131
+ # in either the backtrace or exception
132
+ def file_name_and_line(short_name = false)
133
+ return unless backtrace || (exception && exception.backtrace)
134
+ stack = backtrace || exception.backtrace
135
+ extract_file_and_line(stack, short_name) if stack && stack.size > 0
136
+ end
137
+
138
+ # Strip the standard Rails colorizing from the logged message
139
+ def cleansed_message
140
+ message.to_s.gsub(/(\e(\[([\d;]*[mz]?))?)?/, "").strip
141
+ end
142
+
143
+ # Return the payload in text form
144
+ # Returns nil if payload is missing or empty
145
+ def payload_to_s
146
+ payload.inspect if payload?
147
+ end
148
+
149
+ # Returns [true|false] whether the log entry has a payload
150
+ def payload?
151
+ !(payload.nil? || (payload.respond_to?(:empty?) && payload.empty?))
152
+ end
153
+
154
+ # Return the Time as a formatted string
155
+ # Ruby MRI supports micro seconds
156
+ # DEPRECATED
157
+ def formatted_time
158
+ "#{time.strftime("%Y-%m-%d %H:%M:%S")}.#{"%06d" % (time.usec)}"
159
+ end
160
+
161
+ # Returns [Hash] representation of this log entry
162
+ def to_h(host = Sapience.config.host, application = Sapience.config.application) # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity, LineLength
163
+ # Header
164
+ h = {
165
+ name: name,
166
+ pid: $PROCESS_ID,
167
+ thread: thread_name,
168
+ time: time,
169
+ level: level,
170
+ level_index: level_index,
171
+ }
172
+ h[:host] = host if host
173
+ h[:application] = application if application
174
+ file, line = file_name_and_line
175
+ if file
176
+ h[:file] = file
177
+ h[:line] = line.to_i
178
+ end
179
+
180
+ # Tags
181
+ h[:tags] = tags if tags && (tags.size > 0)
182
+
183
+ # Duration
184
+ if duration
185
+ h[:duration_ms] = duration
186
+ h[:duration] = duration_human
187
+ end
188
+
189
+ # Log message
190
+ h[:message] = cleansed_message if message
191
+
192
+ # Payload
193
+ if payload
194
+ if payload.is_a?(Hash)
195
+ h.merge!(payload)
196
+ else
197
+ h[:payload] = payload
198
+ end
199
+ end
200
+
201
+ # Exceptions
202
+ if exception
203
+ root = h
204
+ each_exception do |exception, i|
205
+ name = i == 0 ? :exception : :cause
206
+ root[name] = {
207
+ name: exception.class.name,
208
+ message: exception.message,
209
+ stack_trace: exception.backtrace,
210
+ }
211
+ root = root[name]
212
+ end
213
+ end
214
+
215
+ # Metric
216
+ h[:metric] = metric if metric
217
+ h
218
+ end
219
+ end
220
+
221
+ end
@@ -0,0 +1,29 @@
1
+ module Sapience
2
+ # rubocop:disable TrivialAccessors
3
+ module Loggable
4
+ def self.included(base)
5
+ base.class_eval do
6
+ # Returns [Sapience::Logger] class level logger
7
+ def self.logger
8
+ @sapience ||= Sapience[self]
9
+ end
10
+
11
+ # Replace instance class level logger
12
+ def self.logger=(logger)
13
+ @sapience = logger
14
+ end
15
+
16
+ # Returns [Sapience::Logger] instance level logger
17
+ def logger
18
+ @sapience ||= self.class.logger
19
+ end
20
+
21
+ # Replace instance level logger
22
+ def logger=(logger)
23
+ @sapience = logger
24
+ end
25
+ end
26
+ end
27
+ end
28
+ # rubocop:enable TrivialAccessors
29
+ end
@@ -0,0 +1,236 @@
1
+ require "concurrent"
2
+
3
+ # rubocop:disable ClassVars
4
+ module Sapience
5
+ # Logger stores the class name to be used for all log messages so that every
6
+ # log message written by this instance will include the class name
7
+ class Logger < Base # rubocop:disable ClassLength, ClassVars
8
+ include Sapience::Concerns::Compatibility
9
+
10
+ # Returns a Logger instance
11
+ #
12
+ # Return the logger for a specific class, supports class specific log levels
13
+ # logger = Sapience::Logger.new(self)
14
+ # OR
15
+ # logger = Sapience::Logger.new('MyClass')
16
+ #
17
+ # Parameters:
18
+ # application
19
+ # A class, module or a string with the application/class name
20
+ # to be used in the logger
21
+ #
22
+ # level
23
+ # The initial log level to start with for this logger instance
24
+ # Default: Sapience.config.default_level
25
+ #
26
+ # filter [Regexp|Proc]
27
+ # RegExp: Only include log messages where the class name matches the supplied
28
+ # regular expression. All other messages will be ignored
29
+ # Proc: Only include log messages where the supplied Proc returns true
30
+ # The Proc must return true or false
31
+ def initialize(klass, level = nil, filter = nil)
32
+ super
33
+ end
34
+
35
+ # Returns [Integer] the number of log entries that have not been written
36
+ # to the appenders
37
+ #
38
+ # When this number grows it is because the logging appender thread is not
39
+ # able to write to the appenders fast enough. Either reduce the amount of
40
+ # logging, increase the log level, reduce the number of appenders, or
41
+ # look into speeding up the appenders themselves
42
+ def self.queue_size
43
+ queue.size
44
+ end
45
+
46
+ # Flush all queued log entries disk, database, etc.
47
+ # All queued log messages are written and then each appender is flushed in turn
48
+ def self.flush
49
+ msg = "Flushing appenders with #{queue_size} log messages on the queue"
50
+ if queue_size > 1_000
51
+ logger.warn msg
52
+ elsif queue_size > 100
53
+ logger.info msg
54
+ elsif queue_size > 0
55
+ logger.trace msg
56
+ end
57
+ process_request(:flush)
58
+ end
59
+
60
+ # Close all appenders and flush any outstanding messages
61
+ def self.close
62
+ msg = "Closing appenders with #{queue_size} log messages on the queue"
63
+ if queue_size > 1_000
64
+ logger.warn msg
65
+ elsif queue_size > 100
66
+ logger.info msg
67
+ elsif queue_size > 0
68
+ logger.trace msg
69
+ end
70
+ process_request(:close)
71
+ end
72
+
73
+ @@lag_check_interval = 5000
74
+ @@lag_threshold_s = 30
75
+
76
+ # Returns the check_interval which is the number of messages between checks
77
+ # to determine if the appender thread is falling behind
78
+ def self.lag_check_interval
79
+ @@lag_check_interval
80
+ end
81
+
82
+ # Set the check_interval which is the number of messages between checks
83
+ # to determine if the appender thread is falling behind
84
+ def self.lag_check_interval=(lag_check_interval)
85
+ @@lag_check_interval = lag_check_interval
86
+ end
87
+
88
+ # Returns the amount of time in seconds
89
+ # to determine if the appender thread is falling behind
90
+ def self.lag_threshold_s
91
+ @@lag_threshold_s
92
+ end
93
+
94
+ # Allow the internal logger to be overridden from its default to STDERR
95
+ # Can be replaced with another Ruby logger or Rails logger, but never to
96
+ # Sapience::Logger itself since it is for reporting problems
97
+ # while trying to log to the various appenders
98
+ def self.logger=(logger)
99
+ @@logger = logger
100
+ end
101
+
102
+ # Place log request on the queue for the Appender thread to write to each
103
+ # appender in the order that they were registered
104
+ def log(log, message = nil, progname = nil, &block)
105
+ # Compatibility with ::Logger
106
+ return add(log, message, progname, &block) unless log.is_a?(Sapience::Log)
107
+ self.class.queue << log if @@appender_thread
108
+ end
109
+
110
+ private
111
+
112
+ @@appender_thread = nil
113
+ @@queue = Queue.new
114
+
115
+ # Queue to hold messages that need to be logged to the various appenders
116
+ def self.queue
117
+ @@queue
118
+ end
119
+
120
+ # Internal logger for Sapience
121
+ # For example when an appender is not working etc..
122
+ # By default logs to STDERR
123
+ def self.logger
124
+ @@logger ||= begin
125
+ l = Sapience::Appender::File.new(STDERR, :warn)
126
+ l.name = name
127
+ l
128
+ end
129
+ end
130
+
131
+ # Start the appender thread
132
+ def self.start_appender_thread
133
+ return false if appender_thread_active?
134
+ @@appender_thread = Thread.new { appender_thread }
135
+ fail "Failed to start Appender Thread" unless @@appender_thread
136
+ true
137
+ end
138
+
139
+ # Returns true if the appender_thread is active
140
+ def self.appender_thread_active?
141
+ @@appender_thread && @@appender_thread.alive?
142
+ end
143
+
144
+ # Separate appender thread responsible for reading log messages and
145
+ # calling the appenders in it's thread
146
+ # rubocop:disable BlockNesting, AssignmentInCondition, PerceivedComplexity, CyclomaticComplexity, AbcSize, LineLength, RescueException
147
+ def self.appender_thread
148
+ # This thread is designed to never go down unless the main thread terminates
149
+ # Before terminating at_exit is used to flush all the appenders
150
+ #
151
+ # Should any appender fail to log or flush, the exception is logged and
152
+ # other appenders will still be called
153
+ Thread.current.name = "Sapience::AppenderThread"
154
+ logger.trace "V#{VERSION} Appender thread active"
155
+ begin
156
+ count = 0
157
+ while message = queue.pop
158
+ if message.is_a?(Log)
159
+ Sapience.appenders.each do |appender|
160
+ begin
161
+ appender.log(message)
162
+ rescue Exception => exc
163
+ logger.error "Appender thread: Failed to log to appender: #{appender.inspect}", exc
164
+ end
165
+ end
166
+ count += 1
167
+ # Check every few log messages whether this appender thread is falling behind
168
+ if count > lag_check_interval
169
+ if (diff = Time.now - message.time) > lag_threshold_s
170
+ logger.warn "Appender thread has fallen behind by #{diff} seconds with #{queue_size} messages queued up. Consider reducing the log level or changing the appenders"
171
+ end
172
+ count = 0
173
+ end
174
+ else
175
+ case message[:command]
176
+ when :flush
177
+ Sapience.appenders.each do |appender|
178
+ begin
179
+ logger.trace "Appender thread: Flushing appender: #{appender.name}"
180
+ appender.flush
181
+ rescue Exception => exc
182
+ logger.error "Appender thread: Failed to flush appender: #{appender.inspect}", exc
183
+ end
184
+ end
185
+
186
+ message[:reply_queue] << true if message[:reply_queue]
187
+ logger.trace "Appender thread: All appenders flushed"
188
+ when :close
189
+ Sapience.appenders.each do |appender|
190
+ begin
191
+ logger.trace "Appender thread: Closing appender: #{appender.name}"
192
+ appender.flush
193
+ appender.close
194
+ Sapience.remove_appender(appender)
195
+ rescue Exception => exc
196
+ logger.error "Appender thread: Failed to close appender: #{appender.inspect}", exc
197
+ end
198
+ end
199
+
200
+ message[:reply_queue] << true if message[:reply_queue]
201
+ logger.trace "Appender thread: All appenders flushed"
202
+ else
203
+ logger.warn "Appender thread: Ignoring unknown command: #{message[:command]}"
204
+ end
205
+ end
206
+ end
207
+ rescue Exception => exception
208
+ # This block may be called after the file handles have been released by Ruby
209
+ begin
210
+ logger.error "Appender thread restarting due to exception", exception
211
+ rescue Exception
212
+ nil
213
+ end
214
+ retry
215
+ ensure
216
+ @@appender_thread = nil
217
+ # This block may be called after the file handles have been released by Ruby
218
+ begin
219
+ logger.trace "Appender thread has stopped"
220
+ rescue Exception
221
+ nil
222
+ end
223
+ end
224
+ end
225
+ # rubocop:enable BlockNesting, AssignmentInCondition, PerceivedComplexity, CyclomaticComplexity, AbcSize, LineLength, RescueException
226
+
227
+ # Close all appenders and flush any outstanding messages
228
+ def self.process_request(command)
229
+ return false unless appender_thread_active?
230
+
231
+ reply_queue = Queue.new
232
+ queue << { command: command, reply_queue: reply_queue }
233
+ reply_queue.pop
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,15 @@
1
+ module Sapience
2
+ class Rails < ::Rails::Engine
3
+ ::Rails::Application::Bootstrap.initializers.delete_if { |i| i.name == :initialize_logger }
4
+ initializer :initialize_logger, group: :all do
5
+ # TODO: Is this really needed?
6
+ # Existing loggers are ignored because servers like trinidad supply their
7
+ # own file loggers which would result in duplicate logging to the same log file
8
+ # ::Rails.logger = config.logger = begin
9
+ # Sapience[::Rails]
10
+ # end
11
+
12
+ # Replace Rails loggers
13
+ end
14
+ end
15
+ end