scout_apm 3.0.0.pre13 → 3.0.0.pre14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/lib/scout_apm.rb +20 -10
  3. data/lib/scout_apm/agent.rb +114 -319
  4. data/lib/scout_apm/agent/exit_handler.rb +66 -0
  5. data/lib/scout_apm/agent/preconditions.rb +69 -0
  6. data/lib/scout_apm/agent_context.rb +234 -0
  7. data/lib/scout_apm/app_server_load.rb +24 -14
  8. data/lib/scout_apm/background_job_integrations/resque.rb +7 -8
  9. data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -2
  10. data/lib/scout_apm/background_recorder.rb +8 -3
  11. data/lib/scout_apm/background_worker.rb +14 -7
  12. data/lib/scout_apm/config.rb +35 -26
  13. data/lib/scout_apm/context.rb +11 -4
  14. data/lib/scout_apm/db_query_metric_set.rb +17 -5
  15. data/lib/scout_apm/debug.rb +1 -1
  16. data/lib/scout_apm/environment.rb +10 -14
  17. data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
  18. data/lib/scout_apm/git_revision.rb +13 -8
  19. data/lib/scout_apm/histogram.rb +1 -1
  20. data/lib/scout_apm/instant/middleware.rb +7 -7
  21. data/lib/scout_apm/instant_reporting.rb +7 -7
  22. data/lib/scout_apm/instrument_manager.rb +87 -0
  23. data/lib/scout_apm/instruments/action_controller_rails_2.rb +12 -7
  24. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +17 -12
  25. data/lib/scout_apm/instruments/action_view.rb +11 -7
  26. data/lib/scout_apm/instruments/active_record.rb +25 -11
  27. data/lib/scout_apm/instruments/elasticsearch.rb +10 -6
  28. data/lib/scout_apm/instruments/grape.rb +12 -8
  29. data/lib/scout_apm/instruments/http_client.rb +10 -6
  30. data/lib/scout_apm/instruments/influxdb.rb +10 -6
  31. data/lib/scout_apm/instruments/middleware_detailed.rb +11 -5
  32. data/lib/scout_apm/instruments/middleware_summary.rb +11 -5
  33. data/lib/scout_apm/instruments/mongoid.rb +10 -5
  34. data/lib/scout_apm/instruments/moped.rb +11 -6
  35. data/lib/scout_apm/instruments/net_http.rb +10 -6
  36. data/lib/scout_apm/instruments/percentile_sampler.rb +8 -6
  37. data/lib/scout_apm/instruments/process/process_cpu.rb +8 -4
  38. data/lib/scout_apm/instruments/process/process_memory.rb +15 -10
  39. data/lib/scout_apm/instruments/rails_router.rb +12 -6
  40. data/lib/scout_apm/instruments/redis.rb +10 -6
  41. data/lib/scout_apm/instruments/samplers.rb +11 -0
  42. data/lib/scout_apm/instruments/sinatra.rb +5 -4
  43. data/lib/scout_apm/layaway.rb +21 -20
  44. data/lib/scout_apm/layaway_file.rb +8 -3
  45. data/lib/scout_apm/layer.rb +3 -3
  46. data/lib/scout_apm/layer_converters/converter_base.rb +6 -7
  47. data/lib/scout_apm/layer_converters/database_converter.rb +1 -1
  48. data/lib/scout_apm/layer_converters/histograms.rb +2 -2
  49. data/lib/scout_apm/layer_converters/slow_job_converter.rb +4 -3
  50. data/lib/scout_apm/layer_converters/slow_request_converter.rb +5 -4
  51. data/lib/scout_apm/logger.rb +143 -0
  52. data/lib/scout_apm/middleware.rb +7 -9
  53. data/lib/scout_apm/periodic_work.rb +28 -0
  54. data/lib/scout_apm/reporter.rb +14 -8
  55. data/lib/scout_apm/reporting.rb +135 -0
  56. data/lib/scout_apm/request_manager.rb +4 -6
  57. data/lib/scout_apm/serializers/payload_serializer.rb +1 -1
  58. data/lib/scout_apm/slow_job_policy.rb +6 -2
  59. data/lib/scout_apm/slow_job_record.rb +5 -5
  60. data/lib/scout_apm/slow_request_policy.rb +6 -2
  61. data/lib/scout_apm/slow_transaction.rb +5 -5
  62. data/lib/scout_apm/store.rb +22 -16
  63. data/lib/scout_apm/synchronous_recorder.rb +7 -3
  64. data/lib/scout_apm/tasks/doctor.rb +75 -0
  65. data/lib/scout_apm/tasks/support.rb +22 -0
  66. data/lib/scout_apm/tracer.rb +5 -5
  67. data/lib/scout_apm/tracked_request.rb +43 -19
  68. data/lib/scout_apm/utils/active_record_metric_name.rb +66 -8
  69. data/lib/scout_apm/utils/backtrace_parser.rb +1 -1
  70. data/lib/scout_apm/utils/installed_gems.rb +7 -3
  71. data/lib/scout_apm/utils/klass_helper.rb +8 -2
  72. data/lib/scout_apm/utils/scm.rb +1 -1
  73. data/lib/scout_apm/utils/sql_sanitizer.rb +3 -3
  74. data/lib/scout_apm/version.rb +1 -1
  75. data/lib/tasks/doctor.rake +11 -0
  76. data/scout_apm.gemspec +1 -0
  77. data/test/test_helper.rb +17 -2
  78. data/test/unit/agent_test.rb +1 -54
  79. data/test/unit/config_test.rb +9 -5
  80. data/test/unit/context_test.rb +4 -4
  81. data/test/unit/db_query_metric_set_test.rb +11 -4
  82. data/test/unit/fake_store_test.rb +1 -1
  83. data/test/unit/git_revision_test.rb +3 -3
  84. data/test/unit/instruments/net_http_test.rb +2 -1
  85. data/test/unit/instruments/percentile_sampler_test.rb +5 -9
  86. data/test/unit/layaway_test.rb +10 -5
  87. data/test/unit/layer_converters/metric_converter_test.rb +2 -2
  88. data/test/unit/slow_request_policy_test.rb +7 -3
  89. data/test/unit/sql_sanitizer_test.rb +0 -6
  90. data/test/unit/store_test.rb +11 -8
  91. data/test/unit/utils/active_record_metric_name_test.rb +45 -7
  92. metadata +27 -5
  93. data/lib/scout_apm/agent/logging.rb +0 -74
  94. data/lib/scout_apm/agent/reporting.rb +0 -129
  95. data/lib/scout_apm/utils/null_logger.rb +0 -13
@@ -1,22 +1,26 @@
1
1
  module ScoutApm
2
2
  module Instruments
3
3
  class Redis
4
- attr_reader :logger
4
+ attr_reader :context
5
5
 
6
- def initalize(logger=ScoutApm::Agent.instance.logger)
7
- @logger = logger
6
+ def initialize(context)
7
+ @context = context
8
8
  @installed = false
9
9
  end
10
10
 
11
+ def logger
12
+ context.logger
13
+ end
14
+
11
15
  def installed?
12
16
  @installed
13
17
  end
14
18
 
15
19
  def install
16
- @installed = true
17
-
18
20
  if defined?(::Redis) && defined?(::Redis::Client)
19
- ScoutApm::Agent.instance.logger.info "Instrumenting Redis"
21
+ @installed = true
22
+
23
+ logger.info "Instrumenting Redis"
20
24
 
21
25
  ::Redis::Client.class_eval do
22
26
  include ScoutApm::Tracer
@@ -0,0 +1,11 @@
1
+ module ScoutApm
2
+ module Instruments
3
+ class Samplers
4
+ DEFAULT_SAMPLERS = [
5
+ ScoutApm::Instruments::Process::ProcessCpu,
6
+ ScoutApm::Instruments::Process::ProcessMemory,
7
+ ScoutApm::Instruments::PercentileSampler,
8
+ ]
9
+ end
10
+ end
11
+ end
@@ -1,9 +1,10 @@
1
+ # XXX: Is this file used?
1
2
  module ScoutApm
2
3
  module Instruments
3
4
  class Sinatra
4
5
  attr_reader :logger
5
6
 
6
- def initalize(logger=ScoutApm::Agent.instance.logger)
7
+ def initalize(logger=ScoutApm::Agent.instance.context.logger)
7
8
  @logger = logger
8
9
  @installed = false
9
10
  end
@@ -13,10 +14,10 @@ module ScoutApm
13
14
  end
14
15
 
15
16
  def install
16
- @installed = true
17
-
18
17
  if defined?(::Sinatra) && defined?(::Sinatra::Base) && ::Sinatra::Base.private_method_defined?(:dispatch!)
19
- ScoutApm::Agent.instance.logger.info "Instrumenting Sinatra"
18
+ @installed = true
19
+
20
+ logger.info "Instrumenting Sinatra"
20
21
  ::Sinatra::Base.class_eval do
21
22
  include ScoutApm::Tracer
22
23
  include ScoutApm::Instruments::SinatraInstruments
@@ -18,12 +18,13 @@ module ScoutApm
18
18
  # Must be sortable as an integer
19
19
  TIME_FORMAT = "%Y%m%d%H%M"
20
20
 
21
- attr_accessor :config
22
- attr_reader :environment
21
+ attr_reader :context
22
+ def initialize(context)
23
+ @context = context
24
+ end
23
25
 
24
- def initialize(config, environment)
25
- @config = config
26
- @environment = environment
26
+ def logger
27
+ context.logger
27
28
  end
28
29
 
29
30
  # Returns a Pathname object with the fully qualified directory where the layaway files can be placed.
@@ -34,27 +35,27 @@ module ScoutApm
34
35
  def directory
35
36
  return @directory if @directory
36
37
 
37
- data_file = config.value("data_file")
38
+ data_file = context.config.value("data_file")
38
39
  data_file = File.dirname(data_file) if data_file && !File.directory?(data_file)
39
40
 
40
41
  candidates = [
41
42
  data_file,
42
- "#{environment.root}/tmp",
43
+ "#{context.environment.root}/tmp",
43
44
  "/tmp"
44
45
  ].compact
45
46
 
46
47
  found = candidates.detect { |dir| File.writable?(dir) }
47
- ScoutApm::Agent.instance.logger.debug("Storing Layaway Files in #{found}")
48
+ logger.debug("Storing Layaway Files in #{found}")
48
49
  @directory = Pathname.new(found)
49
50
  end
50
51
 
51
52
  def write_reporting_period(reporting_period, files_limit = MAX_FILES_LIMIT)
52
53
  if at_layaway_file_limit?(files_limit)
53
- ScoutApm::Agent.instance.logger.error("Hit layaway file limit. Not writing to layaway file")
54
+ logger.error("Hit layaway file limit. Not writing to layaway file")
54
55
  return false
55
56
  end
56
57
  filename = file_for(reporting_period.timestamp)
57
- layaway_file = LayawayFile.new(filename)
58
+ layaway_file = LayawayFile.new(context, filename)
58
59
  layaway_file.write(reporting_period)
59
60
  end
60
61
 
@@ -74,28 +75,28 @@ module ScoutApm
74
75
  begin
75
76
  if f
76
77
  begin
77
- ScoutApm::Agent.instance.logger.debug("Obtained Reporting Lock")
78
+ logger.debug("Obtained Reporting Lock")
78
79
 
79
80
  log_layaway_file_information
80
81
 
81
82
  files = all_files_for(timestamp).reject{|l| l.to_s == coordinator_file.to_s }
82
- rps = files.map{ |layaway| LayawayFile.new(layaway).load }.compact
83
+ rps = files.map{ |layaway| LayawayFile.new(context, layaway).load }.compact
83
84
  if rps.any?
84
85
  yield rps
85
86
 
86
- ScoutApm::Agent.instance.logger.debug("Deleting the now-reported layaway files for #{timestamp.to_s}")
87
+ logger.debug("Deleting the now-reported layaway files for #{timestamp.to_s}")
87
88
  delete_files_for(timestamp) # also removes the coodinator_file
88
89
 
89
- ScoutApm::Agent.instance.logger.debug("Checking for any Stale layaway files")
90
+ logger.debug("Checking for any Stale layaway files")
90
91
  delete_stale_files(timestamp.to_time - STALE_AGE)
91
92
  else
92
93
  File.unlink(coordinator_file)
93
- ScoutApm::Agent.instance.logger.debug("No layaway files to report")
94
+ logger.debug("No layaway files to report")
94
95
  end
95
96
 
96
97
  true
97
98
  rescue Exception => e
98
- ScoutApm::Agent.instance.logger.debug("Caught an exception in with_claim, with the coordination file locked: #{e.message}, #{e.backtrace.inspect}")
99
+ logger.debug("Caught an exception in with_claim, with the coordination file locked: #{e.message}, #{e.backtrace.inspect}")
99
100
  raise
100
101
  ensure
101
102
  # Unlock the file when done!
@@ -111,7 +112,7 @@ module ScoutApm
111
112
 
112
113
  def delete_files_for(timestamp)
113
114
  all_files_for(timestamp).each { |layaway|
114
- ScoutApm::Agent.instance.logger.debug("Deleting layaway file: #{layaway}")
115
+ logger.debug("Deleting layaway file: #{layaway}")
115
116
  File.unlink(layaway)
116
117
  }
117
118
  end
@@ -122,10 +123,10 @@ module ScoutApm
122
123
  compact.
123
124
  uniq.
124
125
  select { |timestamp| timestamp.to_i < older_than.strftime(TIME_FORMAT).to_i }.
125
- tap { |timestamps| ScoutApm::Agent.instance.logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
126
+ tap { |timestamps| logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
126
127
  map { |timestamp| delete_files_for(timestamp) }
127
128
  rescue => e
128
- ScoutApm::Agent.instance.logger.debug("Problem deleting stale files: #{e.message}, #{e.backtrace.inspect}")
129
+ logger.debug("Problem deleting stale files: #{e.message}, #{e.backtrace.inspect}")
129
130
  end
130
131
 
131
132
  private
@@ -192,7 +193,7 @@ module ScoutApm
192
193
  ]
193
194
 
194
195
 
195
- ScoutApm::Agent.instance.logger.debug("Total in #{directory}: #{files_in_temp}. Total Layaway Files: #{all_filenames.size}. By Timestamp: #{count_per_timestamp.inspect}")
196
+ logger.debug("Total in #{directory}: #{files_in_temp}. Total Layaway Files: #{all_filenames.size}. By Timestamp: #{count_per_timestamp.inspect}")
196
197
  end
197
198
  end
198
199
  end
@@ -2,18 +2,23 @@
2
2
  module ScoutApm
3
3
  class LayawayFile
4
4
  attr_reader :path
5
+ attr_reader :context
5
6
 
6
- def initialize(path)
7
+ def initialize(context, path)
7
8
  @path = path
8
9
  end
9
10
 
11
+ def logger
12
+ context.logger
13
+ end
14
+
10
15
  def load
11
16
  data = File.open(path, "r") { |f| read_raw(f) }
12
17
  deserialize(data)
13
18
  rescue NameError, ArgumentError, TypeError => e
14
19
  # Marshal error
15
- ScoutApm::Agent.instance.logger.info("Unable to load data from Layaway file, resetting.")
16
- ScoutApm::Agent.instance.logger.debug("#{e.message}, #{e.backtrace.join("\n\t")}")
20
+ logger.info("Unable to load data from Layaway file, resetting.")
21
+ logger.debug("#{e.message}, #{e.backtrace.join("\n\t")}")
17
22
  nil
18
23
  end
19
24
 
@@ -138,7 +138,7 @@ module ScoutApm
138
138
  # In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.
139
139
  def caller_array
140
140
  # omits the first several callers which are in the ScoutAPM stack.
141
- if ScoutApm::Environment.instance.ruby_2?
141
+ if ScoutApm::Agent.instance.context.environment.ruby_2?
142
142
  caller(3...BACKTRACE_CALLER_LIMIT)
143
143
  else
144
144
  caller[3...BACKTRACE_CALLER_LIMIT]
@@ -152,7 +152,7 @@ module ScoutApm
152
152
  end
153
153
 
154
154
  def start_sampling
155
- if ScoutApm::Agent.instance.config.value('profile') && traced?
155
+ if ScoutApm::Agent.instance.context.config.value('profile') && traced?
156
156
  ScoutApm::Instruments::Stacks.update_indexes(frame_index, trace_index)
157
157
  ScoutApm::Instruments::Stacks.start_sampling
158
158
  else
@@ -161,7 +161,7 @@ module ScoutApm
161
161
  end
162
162
 
163
163
  def record_traces!
164
- if ScoutApm::Agent.instance.config.value('profile')
164
+ if ScoutApm::Agent.instance.context.config.value('profile')
165
165
  ScoutApm::Instruments::Stacks.stop_sampling(false)
166
166
  if traced?
167
167
  traces.raw_traces = ScoutApm::Instruments::Stacks.profile_frames
@@ -2,11 +2,13 @@ module ScoutApm
2
2
  module LayerConverters
3
3
  class ConverterBase
4
4
 
5
+ attr_reader :context
5
6
  attr_reader :request
6
7
  attr_reader :root_layer
7
8
  attr_reader :layer_finder
8
9
 
9
- def initialize(request, layer_finder, store=nil)
10
+ def initialize(context, request, layer_finder, store=nil)
11
+ @context = context
10
12
  @request = request
11
13
  @layer_finder = layer_finder
12
14
  @store = store
@@ -218,17 +220,14 @@ module ScoutApm
218
220
 
219
221
  # Debug logging for scoutprof traces
220
222
  def debug_scoutprof(layer)
221
- agent = ScoutApm::Agent.instance
222
- config = agent.config
223
-
224
223
  if layer.type =~ %r{^(Controller|Queue|Job)$}.freeze
225
- if config.value('profile')
226
- agent.logger.debug do
224
+ if context.config.value('profile')
225
+ context.logger.debug do
227
226
  traces_inspect = layer.traces.inspect
228
227
  "****** Slow Request #{layer.type} Traces (#{layer.name}, tet: #{layer.total_exclusive_time}, tct: #{layer.total_call_time}), total raw traces: #{layer.traces.cube.total_count}, total clean traces: #{layer.traces.total_count}, skipped gc: #{layer.traces.skipped_in_gc}, skipped handler: #{layer.traces.skipped_in_handler}, skipped registered #{layer.traces.skipped_in_job_registered}, skipped not_running #{layer.traces.skipped_in_not_running}:\n#{traces_inspect}"
229
228
  end
230
229
  else
231
- agent.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
230
+ context.logger.debug "****** Slow Request #{layer.type} Traces: Scoutprof is not enabled"
232
231
  end
233
232
  end
234
233
  end
@@ -3,7 +3,7 @@ module ScoutApm
3
3
  class DatabaseConverter < ConverterBase
4
4
  def initialize(*)
5
5
  super
6
- @db_query_metric_set = DbQueryMetricSet.new
6
+ @db_query_metric_set = DbQueryMetricSet.new(context)
7
7
  end
8
8
 
9
9
  def register_hooks(walker)
@@ -4,8 +4,8 @@ module ScoutApm
4
4
  # Updates immediate and long-term histograms for both job and web requests
5
5
  def record!
6
6
  if request.unique_name != :unknown
7
- ScoutApm::Agent.instance.request_histograms.add(request.unique_name, root_layer.total_call_time)
8
- ScoutApm::Agent.instance.request_histograms_by_time[@store.current_timestamp].
7
+ context.request_histograms.add(request.unique_name, root_layer.total_call_time)
8
+ context.request_histograms_by_time[@store.current_timestamp].
9
9
  add(request.unique_name, root_layer.total_call_time)
10
10
  end
11
11
  end
@@ -10,7 +10,7 @@ module ScoutApm
10
10
  ###################
11
11
  def record!
12
12
  return nil unless request.job?
13
- @points = ScoutApm::Agent.instance.slow_job_policy.score(request)
13
+ @points = context.slow_job_policy.score(request)
14
14
 
15
15
  # Let the store know we're here, and if it wants our data, it will call
16
16
  # back into #call
@@ -29,10 +29,10 @@ module ScoutApm
29
29
  return nil unless layer_finder.queue
30
30
  return nil unless layer_finder.job
31
31
 
32
- ScoutApm::Agent.instance.slow_job_policy.stored!(request)
32
+ context.slow_job_policy.stored!(request)
33
33
 
34
34
  # record the change in memory usage
35
- mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(request.capture_mem_delta!)
35
+ mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(request.capture_mem_delta!)
36
36
 
37
37
  timing_metrics, allocation_metrics = create_metrics
38
38
 
@@ -41,6 +41,7 @@ module ScoutApm
41
41
  end
42
42
 
43
43
  SlowJobRecord.new(
44
+ context,
44
45
  queue_layer.name,
45
46
  job_layer.name,
46
47
  root_layer.stop_time,
@@ -6,7 +6,7 @@ module ScoutApm
6
6
  ###################
7
7
  def record!
8
8
  return nil unless request.web?
9
- @points = ScoutApm::Agent.instance.slow_request_policy.score(request)
9
+ @points = context.slow_request_policy.score(request)
10
10
 
11
11
  # Let the store know we're here, and if it wants our data, it will call
12
12
  # back into #call
@@ -25,10 +25,10 @@ module ScoutApm
25
25
  return nil unless request.web?
26
26
  return nil unless scope_layer
27
27
 
28
- ScoutApm::Agent.instance.slow_request_policy.stored!(request)
28
+ context.slow_request_policy.stored!(request)
29
29
 
30
30
  # record the change in memory usage
31
- mem_delta = ScoutApm::Instruments::Process::ProcessMemory.rss_to_mb(@request.capture_mem_delta!)
31
+ mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(@request.capture_mem_delta!)
32
32
 
33
33
  uri = request.annotations[:uri] || ""
34
34
 
@@ -38,7 +38,8 @@ module ScoutApm
38
38
  allocation_metrics = {}
39
39
  end
40
40
 
41
- SlowTransaction.new(uri,
41
+ SlowTransaction.new(context,
42
+ uri,
42
43
  scope_layer.legacy_metric_name,
43
44
  root_layer.total_call_time,
44
45
  timing_metrics,
@@ -0,0 +1,143 @@
1
+ # Pass in the "root" of the application you're using.
2
+ # - Rails.root
3
+ # - `Dir.pwd`
4
+ #
5
+ # Currently Valid opts:
6
+ # :force => Boolean - used to reinitialize logger.
7
+ # :log_file_path => String - explicitly set what file to send the log to
8
+ # :stdout => true - explicitly force the log to write to stdout (if set, ignore log_file_path)
9
+ # :stderr => true - explicitly force the log to write to stderr (if set, ignore log_file_path)
10
+ # :logger_class => Class or String - a class to use as the underlying logger. Defaults to Ruby's Logger. See notes
11
+ # :log_level => symbol, string, or integer - defualts to INFO level
12
+ #
13
+ # The :logger_class option
14
+ # - allows any class to be used as the underlying logger. Currently requires to respond to:
15
+ # - debug, info, warn, error, fatal in both string and block form.
16
+ # - #level= with a number (0 = debug, 1 = info, 2= warn, 3=error, 4=fatal)
17
+ # - #formatter= that takes a Ruby Logger::Formatter class. This method must be here, but the value may be ignored
18
+ #
19
+ #config.value('log_level').downcase
20
+ #
21
+ module ScoutApm
22
+ class Logger
23
+ attr_reader :log_destination
24
+
25
+ def initialize(environment_root, opts={})
26
+ @opts = opts
27
+ @environment_root = environment_root
28
+
29
+ @log_destination = determine_log_destination
30
+ @logger = build_logger
31
+ self.log_level = log_level_from_opts
32
+ @logger.formatter = build_formatter
33
+ end
34
+
35
+ # Delegate calls to the underlying logger
36
+ def debug(*args, &block); @logger.debug(*args, &block); end
37
+ def info(*args, &block); @logger.info(*args, &block); end
38
+ def warn(*args, &block); @logger.warn(*args, &block); end
39
+ def error(*args, &block); @logger.error(*args, &block); end
40
+ def fatal(*args, &block); @logger.fatal(*args, &block); end
41
+
42
+ def log_level=(level)
43
+ @logger.level = level
44
+ end
45
+
46
+ def log_file_path
47
+ @opts.fetch(:log_file_path, "#{@environment_root}/log") || "#{@environment_root}/log"
48
+ end
49
+
50
+ def stdout?
51
+ @opts[:stdout] || @opts[:log_file_path] == "STDOUT"
52
+ end
53
+
54
+ def stderr?
55
+ @opts[:stderr] || @opts[:log_file_path] == "STDERR"
56
+ end
57
+
58
+ private
59
+
60
+ def build_logger
61
+ logger_class.new(@log_destination)
62
+ end
63
+
64
+ def logger_class
65
+ klass = @opts.fetch(:logger_class, ::Logger)
66
+ case klass
67
+ when String
68
+ result = KlassHelper.lookup(klass)
69
+ if result == :missing_class
70
+ ::Logger
71
+ else
72
+ result
73
+ end
74
+ when Class
75
+ klass
76
+ else
77
+ ::Logger
78
+ end
79
+ end
80
+
81
+ def build_formatter
82
+ if stdout? || stderr?
83
+ TaggedFormatter.new
84
+ else
85
+ DefaultFormatter.new
86
+ end
87
+ end
88
+
89
+ def log_level_from_opts
90
+ case @opts[:log_level]
91
+ when "debug" then ::Logger::DEBUG
92
+ when "info" then ::Logger::INFO
93
+ when "warn" then ::Logger::WARN
94
+ when "error" then ::Logger::ERROR
95
+ when "fatal" then ::Logger::FATAL
96
+ else ::Logger::INFO
97
+ end
98
+ end
99
+
100
+ def determine_log_destination
101
+ case true
102
+ when stdout?
103
+ STDOUT
104
+ when stderr?
105
+ STDERR
106
+ when validate_path(@opts[:log_file])
107
+ @opts[:log_file]
108
+ when validate_path("#{log_file_path}/scout_apm.log")
109
+ "#{log_file_path}/scout_apm.log"
110
+ else
111
+ # Safe fallback
112
+ STDOUT
113
+ end
114
+ end
115
+
116
+ # Check if this path is ok for a log file.
117
+ # Does it exist?
118
+ # Is it writable?
119
+ # XXX: Implement
120
+ def validate_path(candidate)
121
+ !candidate.nil?
122
+ end
123
+
124
+
125
+ class DefaultFormatter < ::Logger::Formatter
126
+ def call(severity, time, progname, msg)
127
+ # since STDOUT isn't exclusive like the scout_apm.log file, apply a prefix.
128
+ # XXX: Pass in context to the formatter
129
+ "[#{Utils::Time.to_s(time)} #{ScoutApm::Agent.instance.context.environment.hostname} (#{$$})] #{severity} : #{msg}\n"
130
+ end
131
+ end
132
+
133
+ # since STDOUT & STDERR isn't only used for ScoutApm logging, apply a
134
+ # prefix to make it easily greppable
135
+ class TaggedFormatter < DefaultFormatter
136
+ TAG = "[Scout] "
137
+
138
+ def call(severity, time, progname, msg)
139
+ TAG + super
140
+ end
141
+ end
142
+ end
143
+ end