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,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