Skip to content

Commit 6c59c8a

Browse files
committed
feat: Enable recording for known environments
Recording methods are auto-enabled by known environments. For example, RAILS_ENV and APP_ENV are env vars which indicate the current environment. Test case recording is auto-enabled in "test" environments. Remote and request recording is auto-enabled in "development" environments. Recording methods can still be manually enabled or disabled using env vars. But in most cases, this shouldn't be needed.
1 parent fcd793b commit 6c59c8a

24 files changed

+311
-45
lines changed

lib/appmap.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@
7676

7777
end.call unless ENV['APPMAP_AUTOREQUIRE'] == 'false'
7878

79-
AppMap.initialize_configuration if ENV['APPMAP'] == 'true' && ENV['APPMAP_INITIALIZE'] != 'false'
79+
AppMap.initialize_configuration if AppMap.recording_enabled? && ENV['APPMAP_INITIALIZE'] != 'false'

lib/appmap/agent.rb

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
# load extension
1111
require_relative 'appmap'
12+
require_relative './detect_enabled'
1213

1314
module AppMap
1415
class << self
@@ -114,5 +115,9 @@ def parameter_schema?
114115
def explain_queries?
115116
ENV['APPMAP_EXPLAIN_QUERIES'] == 'true'
116117
end
118+
119+
def recording_enabled?(recording_method = nil)
120+
DetectEnabled.new(recording_method).enabled?
121+
end
117122
end
118123
end

lib/appmap/cucumber.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22

3+
require 'appmap'
34
require 'appmap/util'
45
require 'fileutils'
56

@@ -55,7 +56,7 @@ def write_scenario(scenario, appmap)
5556
end
5657

5758
def enabled?
58-
ENV['APPMAP'] == 'true'
59+
AppMap.recording_enabled?(:cucumber)
5960
end
6061

6162
def run

lib/appmap/detect_enabled.rb

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
require_relative './recording_methods'
2+
3+
module AppMap
4+
# Detects whether AppMap recording should be enabled. This test can be performed generally, or for
5+
# a particular recording method. Recording can be enabled explicitly, for example via APPMAP=true,
6+
# or it can be enabled implicitly, by running in a dev or test web application environment. Recording
7+
# can also disabled explicitly, using environment variables.
8+
class DetectEnabled
9+
@@detected_for_method = {}
10+
11+
class << self
12+
def clear_cache
13+
@@detected_for_method = {}
14+
end
15+
end
16+
17+
def initialize(recording_method)
18+
@recording_method = recording_method
19+
end
20+
21+
def enabled?
22+
unless @@detected_for_method[@recording_method].nil?
23+
return @@detected_for_method[@recording_method]
24+
end
25+
26+
raise "Unrecognized recording method: #{@recording_method}" if @recording_method && !AppMap::RECORDING_METHODS.member?(@recording_method)
27+
28+
message, enabled, enabled_by_env = detect_enabled
29+
30+
@@detected_for_method[@recording_method] = enabled
31+
32+
if @recording_method
33+
if enabled
34+
warn AppMap::Util.color("AppMap #{@recording_method.nil? ? '' : "#{@recording_method} "}recording is enabled because #{message}", :magenta)
35+
# Inform the user that legacy APPMAP=true isn't required any more
36+
if enabled_by_env && ENV['APPMAP'] == 'true'
37+
warn AppMap::Util.color("Note: Setting envirnoment variable APPMAP=true is no longer necessary to enable #{@recording_method} recording in this environment.", :yellow)
38+
end
39+
end
40+
end
41+
42+
enabled
43+
end
44+
45+
def detect_enabled
46+
detection_functions = %i[globally_disabled? recording_method_disabled? globally_enabled? recording_method_enabled? enabled_by_app_env?]
47+
48+
message, enabled = []
49+
while enabled.nil? && !detection_functions.empty?
50+
message, enabled = method(detection_functions.shift).call
51+
end
52+
53+
unless enabled.nil?
54+
_, enabled_by_env = enabled_by_app_env?
55+
return [ message, enabled, enabled_by_env ]
56+
else
57+
return [ 'it is not enabled by any configuration or framework', false, false ]
58+
end
59+
end
60+
61+
def enabled_by_app_env?
62+
env_name, app_env = detect_app_env
63+
if %i[rspec minitest].member?(@recording_method)
64+
return [ "#{env_name} is '#{app_env}'", true ] if app_env == 'test'
65+
end
66+
67+
if @recording_method.nil?
68+
return [ "#{env_name} is '#{app_env}'", true ] if %w[test development].member?(app_env)
69+
end
70+
71+
if %i[remote requests].member?(@recording_method)
72+
return [ "#{env_name} is '#{app_env}'", true ] if app_env == 'development'
73+
end
74+
end
75+
76+
def detect_app_env
77+
if rails_env
78+
return [ 'RAILS_ENV', rails_env ]
79+
elsif ENV['APP_ENV']
80+
return [ 'APP_ENV', ENV['APP_ENV']]
81+
end
82+
end
83+
84+
def globally_enabled?
85+
[ 'APPMAP=true', true ] if ENV['APPMAP'] == 'true'
86+
end
87+
88+
def globally_disabled?
89+
[ 'APPMAP=false', false ] if ENV['APPMAP'] == 'false'
90+
end
91+
92+
def recording_method_disabled?
93+
return false unless @recording_method
94+
95+
env_var = [ 'APPMAP', 'RECORD', @recording_method.upcase ].join('_')
96+
[ "#{[ 'APPMAP', 'RECORD', @recording_method.upcase ].join('_')}=false", false ] if ENV[env_var] == 'false'
97+
end
98+
99+
def recording_method_enabled?
100+
return false unless @recording_method
101+
102+
env_var = [ 'APPMAP', 'RECORD', @recording_method.upcase ].join('_')
103+
[ "#{[ 'APPMAP', 'RECORD', @recording_method.upcase ].join('_')}=true", true ] if ENV[env_var] == 'true'
104+
end
105+
106+
def rails_env
107+
return Rails.env if defined?(Rails)
108+
109+
return ENV['RAILS_ENV']
110+
end
111+
end
112+
end

lib/appmap/middleware/remote_recording.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def call(env)
8282
# 0
8383

8484
req = Rack::Request.new(env)
85-
return handle_record_request(req) if req.path == '/_appmap/record'
85+
return handle_record_request(req) if AppMap.recording_enabled?(:remote) && req.path == '/_appmap/record'
8686

8787
start_time = Time.now
8888
# Support multi-threaded web server such as Puma by recording each thread
@@ -150,7 +150,7 @@ def html_response?(headers)
150150
end
151151

152152
def record_all_requests?
153-
ENV['APPMAP_RECORD_REQUESTS'] == 'true'
153+
AppMap.recording_enabled?(:requests)
154154
end
155155

156156
def recording?

lib/appmap/minitest.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22

3+
require 'appmap'
34
require 'appmap/util'
45
require 'fileutils'
56
require 'active_support'
@@ -125,7 +126,7 @@ def save(name:, class_map:, source_location:, test_status:, exception:, events:)
125126
end
126127

127128
def enabled?
128-
ENV['APPMAP'] == 'true'
129+
AppMap.recording_enabled?(:minitest)
129130
end
130131

131132
def run

lib/appmap/railtie.rb

+6-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ module AppMap
44
# Railtie connects the AppMap recorder to Rails-specific features.
55
class Railtie < ::Rails::Railtie
66
initializer 'appmap.remote_recording' do
7+
# Indicate early in the log when these methods are enabled.
8+
%i[remote requests].each do |recording_method|
9+
AppMap.recording_enabled?(recording_method)
10+
end
11+
712
require 'appmap/middleware/remote_recording'
813
Rails.application.config.middleware.insert_before \
914
ActionDispatch::Executor,
@@ -29,4 +34,4 @@ class Railtie < ::Rails::Railtie
2934
end
3035
end
3136
end
32-
end if ENV['APPMAP'] == 'true'
37+
end if AppMap.recording_enabled?

lib/appmap/recording_methods.rb

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module AppMap
2+
RECORDING_METHODS = %i[rspec minitest cucumber remote requests].freeze
3+
end

lib/appmap/rspec.rb

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
# frozen_string_literal: true
22

3+
require 'appmap'
34
require 'appmap/util'
45
require 'set'
56
require 'fileutils'
67

78
module AppMap
8-
# Integration of AppMap with RSpec. When enabled with APPMAP=true, the AppMap tracer will
9-
# be activated around each scenario which has the metadata key `:appmap`.
109
module RSpec
1110
APPMAP_OUTPUT_DIR = 'tmp/appmap/rspec'
1211
LOG = false
@@ -216,7 +215,7 @@ def save(name:, class_map:, source_location:, test_status:, exception:, events:)
216215
end
217216

218217
def enabled?
219-
ENV['APPMAP'] == 'true'
218+
AppMap.recording_enabled?(:rspec)
220219
end
221220

222221
def run
@@ -227,7 +226,6 @@ def run
227226
end
228227

229228
if AppMap::RSpec.enabled?
230-
require 'appmap'
231229
require 'active_support/inflector/transliterate'
232230
require 'rspec/core'
233231
require 'rspec/core/example'

lib/appmap/service/test_command_provider.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def all
1616
command: {
1717
program: 'bundle',
1818
args: %w[exec rspec] + integration_test_paths[:rspec].map { |path| "./#{path}" },
19-
environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
19+
environment: { }
2020
}
2121
}
2222
end
@@ -30,7 +30,7 @@ def all
3030
command: {
3131
program: 'bundle',
3232
args: %w[exec cucumber],
33-
environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
33+
environment: { }
3434
}
3535
}
3636
end
@@ -48,7 +48,7 @@ def minitest_commands
4848
command: {
4949
program: 'bundle',
5050
args: %w[exec rails test] + integration_test_paths[:minitest].map { |path| "./#{path}" },
51-
environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
51+
environment: { }
5252
}
5353
}
5454
]
@@ -59,7 +59,7 @@ def minitest_commands
5959
command: {
6060
program: 'bundle',
6161
args: ['exec', 'ruby', "./#{path}"],
62-
environment: { APPMAP: 'true', DISABLE_SPRING: 'true' }
62+
environment: { }
6363
}
6464
}
6565
end

0 commit comments

Comments
 (0)