scout_apm 2.3.5 → 2.4.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +0 -23
- data/lib/scout_apm.rb +21 -10
- data/lib/scout_apm/agent.rb +98 -336
- data/lib/scout_apm/agent/exit_handler.rb +64 -0
- data/lib/scout_apm/agent/preconditions.rb +69 -0
- data/lib/scout_apm/agent_context.rb +226 -0
- data/lib/scout_apm/app_server_load.rb +20 -18
- data/lib/scout_apm/background_job_integrations/resque.rb +7 -8
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +2 -8
- data/lib/scout_apm/background_recorder.rb +8 -3
- data/lib/scout_apm/background_worker.rb +14 -7
- data/lib/scout_apm/config.rb +35 -29
- data/lib/scout_apm/context.rb +11 -4
- data/lib/scout_apm/db_query_metric_set.rb +17 -5
- data/lib/scout_apm/debug.rb +1 -1
- data/lib/scout_apm/environment.rb +10 -14
- data/lib/scout_apm/framework_integrations/sinatra.rb +1 -1
- data/lib/scout_apm/git_revision.rb +13 -8
- data/lib/scout_apm/histogram.rb +1 -1
- data/lib/scout_apm/instant/middleware.rb +7 -7
- data/lib/scout_apm/instant_reporting.rb +7 -7
- data/lib/scout_apm/instrument_manager.rb +87 -0
- data/lib/scout_apm/instruments/action_controller_rails_2.rb +12 -7
- data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +16 -11
- data/lib/scout_apm/instruments/action_view.rb +11 -7
- data/lib/scout_apm/instruments/active_record.rb +28 -51
- data/lib/scout_apm/instruments/elasticsearch.rb +10 -6
- data/lib/scout_apm/instruments/grape.rb +12 -8
- data/lib/scout_apm/instruments/http_client.rb +11 -10
- data/lib/scout_apm/instruments/influxdb.rb +10 -6
- data/lib/scout_apm/instruments/middleware_detailed.rb +11 -5
- data/lib/scout_apm/instruments/middleware_summary.rb +11 -5
- data/lib/scout_apm/instruments/mongoid.rb +10 -5
- data/lib/scout_apm/instruments/moped.rb +11 -6
- data/lib/scout_apm/instruments/net_http.rb +11 -9
- data/lib/scout_apm/instruments/percentile_sampler.rb +8 -6
- data/lib/scout_apm/instruments/process/process_cpu.rb +8 -4
- data/lib/scout_apm/instruments/process/process_memory.rb +15 -10
- data/lib/scout_apm/instruments/rails_router.rb +12 -6
- data/lib/scout_apm/instruments/redis.rb +10 -6
- data/lib/scout_apm/instruments/samplers.rb +11 -0
- data/lib/scout_apm/instruments/sinatra.rb +5 -4
- data/lib/scout_apm/layaway.rb +26 -39
- data/lib/scout_apm/layaway_file.rb +8 -3
- data/lib/scout_apm/layer.rb +1 -1
- data/lib/scout_apm/layer_converters/converter_base.rb +4 -2
- data/lib/scout_apm/layer_converters/database_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/histograms.rb +2 -2
- data/lib/scout_apm/layer_converters/slow_job_converter.rb +4 -3
- data/lib/scout_apm/layer_converters/slow_request_converter.rb +5 -4
- data/lib/scout_apm/logger.rb +143 -0
- data/lib/scout_apm/middleware.rb +7 -9
- data/lib/scout_apm/periodic_work.rb +28 -0
- data/lib/scout_apm/remote/server.rb +0 -2
- data/lib/scout_apm/reporter.rb +14 -8
- data/lib/scout_apm/reporting.rb +135 -0
- data/lib/scout_apm/request_manager.rb +4 -7
- data/lib/scout_apm/serializers/payload_serializer.rb +1 -1
- data/lib/scout_apm/slow_job_policy.rb +6 -2
- data/lib/scout_apm/slow_job_record.rb +5 -5
- data/lib/scout_apm/slow_request_policy.rb +6 -2
- data/lib/scout_apm/slow_transaction.rb +5 -5
- data/lib/scout_apm/store.rb +22 -16
- data/lib/scout_apm/synchronous_recorder.rb +7 -3
- data/lib/scout_apm/tasks/doctor.rb +75 -0
- data/lib/scout_apm/tasks/support.rb +22 -0
- data/lib/scout_apm/tracer.rb +5 -5
- data/lib/scout_apm/tracked_request.rb +23 -35
- data/lib/scout_apm/utils/backtrace_parser.rb +1 -1
- data/lib/scout_apm/utils/installed_gems.rb +7 -3
- data/lib/scout_apm/utils/klass_helper.rb +8 -2
- data/lib/scout_apm/utils/scm.rb +1 -1
- data/lib/scout_apm/utils/sql_sanitizer.rb +4 -6
- data/lib/scout_apm/version.rb +1 -1
- data/lib/tasks/doctor.rake +11 -0
- data/test/test_helper.rb +15 -2
- data/test/unit/agent_test.rb +1 -54
- data/test/unit/config_test.rb +9 -5
- data/test/unit/context_test.rb +4 -4
- data/test/unit/db_query_metric_set_test.rb +11 -4
- data/test/unit/fake_store_test.rb +1 -1
- data/test/unit/git_revision_test.rb +3 -3
- data/test/unit/instruments/net_http_test.rb +2 -1
- data/test/unit/instruments/percentile_sampler_test.rb +5 -9
- data/test/unit/layaway_test.rb +10 -5
- data/test/unit/layer_converters/metric_converter_test.rb +2 -2
- data/test/unit/slow_request_policy_test.rb +7 -3
- data/test/unit/sql_sanitizer_test.rb +0 -6
- data/test/unit/store_test.rb +11 -8
- metadata +15 -7
- data/lib/scout_apm/agent/logging.rb +0 -74
- data/lib/scout_apm/agent/reporting.rb +0 -129
- data/lib/scout_apm/utils/null_logger.rb +0 -13
@@ -1,19 +1,25 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module Instruments
|
3
3
|
class RailsRouter
|
4
|
-
|
5
|
-
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
6
8
|
@installed = false
|
7
9
|
end
|
8
10
|
|
11
|
+
def logger
|
12
|
+
context.logger
|
13
|
+
end
|
14
|
+
|
9
15
|
def installed?
|
10
16
|
@installed
|
11
17
|
end
|
12
18
|
|
13
19
|
def install
|
14
|
-
@installed = true
|
15
|
-
|
16
20
|
if defined?(ActionDispatch) && defined?(ActionDispatch::Routing) && defined?(ActionDispatch::Routing::RouteSet)
|
21
|
+
@installed = true
|
22
|
+
|
17
23
|
ActionDispatch::Routing::RouteSet.class_eval do
|
18
24
|
def call_with_scout_instruments(*args)
|
19
25
|
req = ScoutApm::RequestManager.lookup
|
@@ -26,8 +32,8 @@ module ScoutApm
|
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
|
-
|
30
|
-
|
35
|
+
alias_method :call_without_scout_instruments, :call
|
36
|
+
alias_method :call, :call_with_scout_instruments
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
@@ -1,22 +1,26 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module Instruments
|
3
3
|
class Redis
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :context
|
5
5
|
|
6
|
-
def
|
7
|
-
@
|
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
|
-
|
21
|
+
@installed = true
|
22
|
+
|
23
|
+
logger.info "Instrumenting Redis"
|
20
24
|
|
21
25
|
::Redis::Client.class_eval do
|
22
26
|
include ScoutApm::Tracer
|
@@ -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
|
-
|
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
|
data/lib/scout_apm/layaway.rb
CHANGED
@@ -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
|
-
|
22
|
-
|
21
|
+
attr_reader :context
|
22
|
+
def initialize(context)
|
23
|
+
@context = context
|
24
|
+
end
|
23
25
|
|
24
|
-
def
|
25
|
-
|
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,47 +35,33 @@ 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
|
-
|
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
|
-
|
54
|
-
@wrote_layaway_limit_error_message ||= 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")
|
55
55
|
return false
|
56
56
|
end
|
57
57
|
filename = file_for(reporting_period.timestamp)
|
58
|
-
layaway_file = LayawayFile.new(filename)
|
58
|
+
layaway_file = LayawayFile.new(context, filename)
|
59
59
|
layaway_file.write(reporting_period)
|
60
60
|
end
|
61
61
|
|
62
|
-
# Claims a given timestamp
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# Once the 'claim' is obtained:
|
67
|
-
# * load and yield each ReportingPeriod from the layaway files.
|
68
|
-
# * if there are reporting periods:
|
69
|
-
# * yields any ReportingPeriods collected up from all the files.
|
70
|
-
# * deletes all of the layaway files (including the coordinator) for the timestamp
|
71
|
-
# * if not
|
72
|
-
# * delete the coordinator
|
73
|
-
# * remove any stale layaway files that may be hanging around.
|
74
|
-
# * Finally unlock and ensure the coordinator file is cleared.
|
75
|
-
#
|
76
|
-
# If a claim file can't be obtained, return false without doing any work
|
77
|
-
# Another process is handling the reporting.
|
62
|
+
# Claims a given timestamp (getting a lock on a particular filename),
|
63
|
+
# then yields ReportingPeriods collected up from all the files.
|
64
|
+
# If the yield returns truthy, delete the layaway files that made it up.
|
78
65
|
def with_claim(timestamp)
|
79
66
|
coordinator_file = glob_pattern(timestamp, :coordinator)
|
80
67
|
|
@@ -88,28 +75,28 @@ module ScoutApm
|
|
88
75
|
begin
|
89
76
|
if f
|
90
77
|
begin
|
91
|
-
|
78
|
+
logger.debug("Obtained Reporting Lock")
|
92
79
|
|
93
80
|
log_layaway_file_information
|
94
81
|
|
95
82
|
files = all_files_for(timestamp).reject{|l| l.to_s == coordinator_file.to_s }
|
96
|
-
rps = files.map{ |layaway| LayawayFile.new(layaway).load }.compact
|
83
|
+
rps = files.map{ |layaway| LayawayFile.new(context, layaway).load }.compact
|
97
84
|
if rps.any?
|
98
85
|
yield rps
|
99
86
|
|
100
|
-
|
87
|
+
logger.debug("Deleting the now-reported layaway files for #{timestamp.to_s}")
|
101
88
|
delete_files_for(timestamp) # also removes the coodinator_file
|
89
|
+
|
90
|
+
logger.debug("Checking for any Stale layaway files")
|
91
|
+
delete_stale_files(timestamp.to_time - STALE_AGE)
|
102
92
|
else
|
103
93
|
File.unlink(coordinator_file)
|
104
|
-
|
94
|
+
logger.debug("No layaway files to report")
|
105
95
|
end
|
106
96
|
|
107
|
-
ScoutApm::Agent.instance.logger.debug("Checking for any Stale layaway files")
|
108
|
-
delete_stale_files(timestamp.to_time - STALE_AGE)
|
109
|
-
|
110
97
|
true
|
111
98
|
rescue Exception => e
|
112
|
-
|
99
|
+
logger.debug("Caught an exception in with_claim, with the coordination file locked: #{e.message}, #{e.backtrace.inspect}")
|
113
100
|
raise
|
114
101
|
ensure
|
115
102
|
# Unlock the file when done!
|
@@ -125,7 +112,7 @@ module ScoutApm
|
|
125
112
|
|
126
113
|
def delete_files_for(timestamp)
|
127
114
|
all_files_for(timestamp).each { |layaway|
|
128
|
-
|
115
|
+
logger.debug("Deleting layaway file: #{layaway}")
|
129
116
|
File.unlink(layaway)
|
130
117
|
}
|
131
118
|
end
|
@@ -136,10 +123,10 @@ module ScoutApm
|
|
136
123
|
compact.
|
137
124
|
uniq.
|
138
125
|
select { |timestamp| timestamp.to_i < older_than.strftime(TIME_FORMAT).to_i }.
|
139
|
-
tap { |timestamps|
|
126
|
+
tap { |timestamps| logger.debug("Deleting stale layaway files with timestamps: #{timestamps.inspect}") }.
|
140
127
|
map { |timestamp| delete_files_for(timestamp) }
|
141
128
|
rescue => e
|
142
|
-
|
129
|
+
logger.debug("Problem deleting stale files: #{e.message}, #{e.backtrace.inspect}")
|
143
130
|
end
|
144
131
|
|
145
132
|
private
|
@@ -206,7 +193,7 @@ module ScoutApm
|
|
206
193
|
]
|
207
194
|
|
208
195
|
|
209
|
-
|
196
|
+
logger.debug("Total in #{directory}: #{files_in_temp}. Total Layaway Files: #{all_filenames.size}. By Timestamp: #{count_per_timestamp.inspect}")
|
210
197
|
end
|
211
198
|
end
|
212
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
|
-
|
16
|
-
|
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
|
|
data/lib/scout_apm/layer.rb
CHANGED
@@ -111,7 +111,7 @@ module ScoutApm
|
|
111
111
|
# In Ruby 2.0+, we can pass the range directly to the caller to reduce the memory footprint.
|
112
112
|
def caller_array
|
113
113
|
# omits the first several callers which are in the ScoutAPM stack.
|
114
|
-
if ScoutApm::
|
114
|
+
if ScoutApm::Agent.instance.context.environment.ruby_2?
|
115
115
|
caller(3...BACKTRACE_CALLER_LIMIT)
|
116
116
|
else
|
117
117
|
caller[3...BACKTRACE_CALLER_LIMIT]
|
@@ -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
|
@@ -143,7 +145,7 @@ module ScoutApm
|
|
143
145
|
end
|
144
146
|
end
|
145
147
|
|
146
|
-
def make_meta_options_desc_hash(layer, max_desc_length=
|
148
|
+
def make_meta_options_desc_hash(layer, max_desc_length=1000)
|
147
149
|
if layer.desc
|
148
150
|
desc_s = layer.desc.to_s
|
149
151
|
trimmed_desc = desc_s[0 .. max_desc_length]
|
@@ -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
|
-
|
8
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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(
|
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
|