skylight 0.3.21 → 0.4.0.alpha1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -4
  3. data/ext/extconf.rb +92 -47
  4. data/ext/libskylight.yml +4 -4
  5. data/ext/skylight_native.c +248 -286
  6. data/lib/skylight.rb +19 -114
  7. data/lib/skylight/api.rb +1 -1
  8. data/lib/skylight/config.rb +176 -146
  9. data/lib/skylight/data/cacert.pem +717 -719
  10. data/lib/skylight/formatters/http.rb +1 -1
  11. data/lib/skylight/instrumenter.rb +28 -35
  12. data/lib/skylight/native.rb +58 -72
  13. data/lib/skylight/normalizers.rb +0 -1
  14. data/lib/skylight/normalizers/active_record/sql.rb +0 -4
  15. data/lib/skylight/probes/excon/middleware.rb +3 -1
  16. data/lib/skylight/probes/net_http.rb +3 -1
  17. data/lib/skylight/subscriber.rb +0 -4
  18. data/lib/skylight/trace.rb +189 -0
  19. data/lib/skylight/util.rb +10 -12
  20. data/lib/skylight/util/hostname.rb +17 -0
  21. data/lib/skylight/util/http.rb +33 -36
  22. data/lib/skylight/util/logging.rb +20 -1
  23. data/lib/skylight/util/multi_io.rb +21 -0
  24. data/lib/skylight/util/native_ext_fetcher.rb +83 -69
  25. data/lib/skylight/util/platform.rb +67 -0
  26. data/lib/skylight/util/ssl.rb +50 -0
  27. data/lib/skylight/version.rb +1 -1
  28. metadata +9 -34
  29. data/ext/rust_support/ruby.h +0 -93
  30. data/ext/skylight.h +0 -85
  31. data/ext/skylight.map +0 -4
  32. data/ext/test/extconf.rb +0 -18
  33. data/ext/test/skylight_native_test.c +0 -82
  34. data/ext/test/skylight_test.h +0 -20
  35. data/lib/skylight/formatters.rb +0 -6
  36. data/lib/skylight/messages.rb +0 -21
  37. data/lib/skylight/messages/error.rb +0 -15
  38. data/lib/skylight/messages/hello.rb +0 -13
  39. data/lib/skylight/messages/trace.rb +0 -179
  40. data/lib/skylight/messages/trace_envelope.rb +0 -19
  41. data/lib/skylight/metrics.rb +0 -9
  42. data/lib/skylight/metrics/ewma.rb +0 -69
  43. data/lib/skylight/metrics/meter.rb +0 -58
  44. data/lib/skylight/metrics/process_cpu_gauge.rb +0 -65
  45. data/lib/skylight/metrics/process_mem_gauge.rb +0 -34
  46. data/lib/skylight/util/conversions.rb +0 -9
  47. data/lib/skylight/util/queue.rb +0 -96
  48. data/lib/skylight/util/task.rb +0 -172
  49. data/lib/skylight/util/uniform_sample.rb +0 -63
  50. data/lib/skylight/worker.rb +0 -19
  51. data/lib/skylight/worker/builder.rb +0 -73
  52. data/lib/skylight/worker/collector.rb +0 -274
  53. data/lib/skylight/worker/connection.rb +0 -87
  54. data/lib/skylight/worker/connection_set.rb +0 -56
  55. data/lib/skylight/worker/embedded.rb +0 -24
  56. data/lib/skylight/worker/metrics_reporter.rb +0 -104
  57. data/lib/skylight/worker/server.rb +0 -336
  58. data/lib/skylight/worker/standalone.rb +0 -421
@@ -1,124 +1,37 @@
1
- require 'rbconfig'
2
- require 'socket'
3
1
  require 'skylight/version'
4
2
 
5
3
  module Skylight
6
4
  # @api private
7
- TRACE_ENV_KEY = 'SKYLIGHT_ENABLE_TRACE_LOGS'.freeze
5
+ TRACE_ENV_KEY = 'SKYLIGHT_ENABLE_TRACE_LOGS'.freeze
8
6
 
9
- # @api private
10
- STANDALONE_ENV_KEY = 'SKYLIGHT_STANDALONE'.freeze
11
-
12
- # @api private
13
- STANDALONE_ENV_VAL = 'server'.freeze
14
-
15
- # @api private
16
- # Whether or not the native extension is present
17
- @@has_native_ext = false
18
-
19
- def self.native?
20
- @@has_native_ext
21
- end
22
-
23
- begin
24
- unless ENV["SKYLIGHT_DISABLE_AGENT"]
25
- # First attempt to require the native extension
26
- require 'skylight_native'
27
-
28
- # If nothing was thrown, then the native extension is present
29
- @@has_native_ext = true
30
-
31
- # Require ruby support for the native extension
32
- require 'skylight/native'
33
- end
34
- rescue LoadError
35
- raise if ENV.key?("SKYLIGHT_REQUIRED")
36
- end
7
+ # Load the native agent
8
+ require 'skylight/native'
37
9
 
38
10
  if defined?(Rails)
39
11
  require 'skylight/railtie'
40
12
  end
41
13
 
42
- # @api private
43
- def self.check_install_errors(config)
44
- # Note: An unsupported arch doesn't count as an error.
45
- install_log = File.expand_path("../../ext/install.log", __FILE__)
46
-
47
- if File.exist?(install_log) && File.read(install_log) =~ /ERROR/
48
- config.alert_logger.error \
49
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension failed to install. " \
50
- "Please check #{install_log} and notify support@skylight.io." \
51
- "The missing extension will not affect the functioning of your application."
52
- end
53
- end
54
-
55
- # @api private
56
- def self.warn_skylight_native_missing(config)
57
- # TODO: Dumping the error messages this way is pretty hacky
58
- is_rails = defined?(Rails)
59
- env_name = is_rails ? Rails.env : "development"
60
-
61
- if env_name == "development" || env_name == "test"
62
- config.alert_logger.warn \
63
- "[SKYLIGHT] [#{Skylight::VERSION}] Running Skylight in #{env_name} mode. " \
64
- "No data will be reported until you deploy your app.\n" \
65
- "(To disable this message, set `alert_log_file` in your config.)"
66
- else
67
- config.alert_logger.error \
68
- "[SKYLIGHT] [#{Skylight::VERSION}] The Skylight native extension for your platform wasn't found. " \
69
- "The monitoring portion of Skylight is only supported on production servers running 32- or " \
70
- "64-bit Linux. The missing extension will not affect the functioning of your application " \
71
- "and you can continue local development without data being reported. If you are on a " \
72
- "supported platform, please contact support at support@skylight.io."
73
- end
74
- end
75
-
76
- # @api private
77
- def self.daemon?
78
- ENV[STANDALONE_ENV_KEY] == STANDALONE_ENV_VAL
79
- end
80
-
81
- unless daemon?
82
- require 'active_support/notifications'
83
- require 'skylight/compat' # Require after AS::N
84
-
85
- # Require VM specific things
86
- require 'skylight/vm/gc'
87
- end
14
+ require 'active_support/notifications'
15
+ require 'skylight/compat' # Require after AS::N
16
+
17
+ # Require VM specific things
18
+ require 'skylight/config'
19
+ require 'skylight/gc'
20
+ require 'skylight/helpers'
21
+ require 'skylight/instrumenter'
22
+ require 'skylight/middleware'
23
+ require 'skylight/trace'
24
+ require 'skylight/vm/gc'
25
+ require 'skylight/util'
88
26
 
27
+ # Used from the CLI
89
28
  autoload :Api, 'skylight/api'
90
29
  autoload :CLI, 'skylight/cli'
91
- autoload :Config, 'skylight/config'
92
- autoload :Helpers, 'skylight/helpers'
93
- autoload :Formatters, 'skylight/formatters'
94
- autoload :GC, 'skylight/gc'
95
- autoload :Instrumenter, 'skylight/instrumenter'
96
- autoload :Messages, 'skylight/messages'
97
- autoload :Metrics, 'skylight/metrics'
98
- autoload :Middleware, 'skylight/middleware'
99
30
  autoload :Normalizers, 'skylight/normalizers'
100
31
  autoload :Subscriber, 'skylight/subscriber'
101
- autoload :Worker, 'skylight/worker'
102
-
103
- # Skylight::Util is defined by the native ext so we can't autoload
104
- require 'skylight/util'
105
-
106
- # ==== Exceptions ====
107
-
108
- # @api private
109
- class IpcProtoError < RuntimeError; end
110
-
111
- # @api private
112
- class WorkerStateError < RuntimeError; end
113
-
114
- # @api private
115
- class ConfigError < RuntimeError; end
116
32
 
117
33
  # @api private
118
- class TraceError < RuntimeError; end
119
-
120
- # @api private
121
- class SerializeError < RuntimeError; end
34
+ class ConfigError < RuntimeError; end
122
35
 
123
36
  # @api private
124
37
  TIERS = %w(
@@ -159,9 +72,9 @@ module Skylight
159
72
  end
160
73
 
161
74
  if block_given?
162
- inst.trace(endpoint, cat, title) { yield }
75
+ inst.trace(endpoint, cat || DEFAULT_CATEGORY, title) { yield }
163
76
  else
164
- inst.trace(endpoint, cat, title)
77
+ inst.trace(endpoint, cat || DEFAULT_CATEGORY, title)
165
78
  end
166
79
  end
167
80
 
@@ -207,13 +120,5 @@ module Skylight
207
120
  inst.disable { yield }
208
121
  end
209
122
 
210
- # @api private
211
- RUBYBIN = File.join(
212
- RbConfig::CONFIG['bindir'],
213
- "#{RbConfig::CONFIG['ruby_install_name']}#{RbConfig::CONFIG['EXEEXT']}")
214
-
215
- # Called by the standalone agent
216
- Worker::Server.boot if daemon?
217
-
218
123
  require 'skylight/probes'
219
124
  end
@@ -27,7 +27,7 @@ module Skylight
27
27
  end
28
28
  end
29
29
 
30
- def initialize(config, service = :accounts)
30
+ def initialize(config, service = :auth)
31
31
  @config = config
32
32
  @http = Util::HTTP.new(config, service)
33
33
  end
@@ -1,82 +1,92 @@
1
+ require 'uri'
1
2
  require 'yaml'
2
3
  require 'fileutils'
3
- require 'logger'
4
4
  require 'thread'
5
- require 'socket'
6
-
7
- class AlertLoggerIO
8
- def initialize(config)
9
- @config = config
10
- end
11
-
12
- def write(*args)
13
- STDERR.write *args
14
- @config.logger.<<(*args)
15
- end
16
-
17
- def close
18
- end
19
- end
5
+ require 'openssl'
6
+ require 'skylight/util/hostname'
7
+ require 'skylight/util/logging'
8
+ require 'skylight/util/platform'
9
+ require 'skylight/util/ssl'
20
10
 
21
11
  module Skylight
22
12
  class Config
23
13
  # @api private
24
14
  MUTEX = Mutex.new
25
15
 
26
- def self.default_hostname
27
- if hostname = Socket.gethostname
28
- hostname.strip!
29
- hostname = nil if hostname == ''
30
- end
31
-
32
- hostname || "gen-#{SecureRandom.uuid}"
33
- end
34
-
35
16
  # Map environment variable keys with Skylight configuration keys
36
17
  ENV_TO_KEY = {
37
- 'ROOT' => :'root',
38
- 'LOG_FILE' => :'log_file',
39
- 'LOG_LEVEL' => :'log_level',
40
- 'ALERT_LOG_FILE' => :'alert_log_file',
41
- 'APPLICATION' => :'application',
42
- 'AUTHENTICATION' => :'authentication',
43
- 'HOSTNAME' => :'hostname',
44
- 'SKIP_VALIDATION' => :'skip_validation',
45
- 'AGENT_INTERVAL' => :'agent.interval',
46
- 'AGENT_KEEPALIVE' => :'agent.keepalive',
47
- 'AGENT_LOCKFILE' => :'agent.lockfile',
48
- 'AGENT_SAMPLE_SIZE' => :'agent.sample',
49
- 'AGENT_SOCKFILE_PATH' => :'agent.sockfile_path',
50
- 'AGENT_STRATEGY' => :'agent.strategy',
18
+ # == Authentication ==
19
+ 'AUTHENTICATION' => :'authentication',
20
+
21
+ # == Version ==
22
+ 'VERSION' => :'version',
23
+
24
+ # == App settings ==
25
+ 'ROOT' => :'root',
26
+ 'APPLICATION' => :'application',
27
+ 'HOSTNAME' => :'hostname',
28
+
29
+ # == Logging ==
30
+ 'LOG_FILE' => :'log_file',
31
+ 'LOG_LEVEL' => :'log_level',
32
+ 'ALERT_LOG_FILE' => :'alert_log_file',
33
+
34
+ # == Proxy ==
35
+ 'PROXY_URL' => :'proxy_url',
36
+
37
+ # == Skylight Remote ==
38
+ "AUTH_URL" => :'auth_url',
39
+ "AUTH_HTTP_DEFLATE" => :'auth_http_deflate',
40
+ "AUTH_HTTP_CONNECT_TIMEOUT" => :'auth_http_connect_timeout',
41
+ "AUTH_HTTP_READ_TIMEOUT" => :'auth_http_read_timeout',
42
+ "REPORT_URL" => :'report_url',
43
+ "REPORT_HTTP_DEFLATE" => :'report_http_deflate',
44
+ "REPORT_HTTP_CONNECT_TIMEOUT" => :'report_http_connect_timeout',
45
+ "REPORT_HTTP_READ_TIMEOUT" => :'report_http_read_timeout',
46
+
47
+ # == Native agent settings ==
48
+ #
49
+ "LAZY_START" => :'daemon.lazy_start',
50
+ "DAEMON_EXEC_PATH" => :'daemon.exec_path',
51
+ "DAEMON_LIB_PATH" => :'daemon.lib_path',
52
+ "PIDFILE_PATH" => :'daemon.pidfile_path',
53
+ "SOCKDIR_PATH" => :'daemon.sockdir_path',
54
+ "BATCH_QUEUE_DEPTH" => :'daemon.batch_queue_depth',
55
+ "BATCH_SAMPLE_SIZE" => :'daemon.batch_sample_size',
56
+ "BATCH_FLUSH_INTERVAL" => :'daemon.batch_flush_interval',
57
+ "DAEMON_TICK_INTERVAL" => :'daemon.tick_interval',
58
+ "DAEMON_SANITY_CHECK_INTERVAL" => :'daemon.sanity_check_interval',
59
+ "DAEMON_INACTIVITY_TIMEOUT" => :'daemon.inactivity_timeout',
60
+ "CLIENT_MAX_TRIES" => :'daemon.max_connect_tries',
61
+ "CLIENT_CONN_TRY_WIN" => :'daemon.connect_try_window',
62
+ "MAX_PRESPAWN_JITTER" => :'daemon.max_prespawn_jitter',
63
+ "DAEMON_WAIT_TIMEOUT" => :'daemon.wait_timeout',
64
+ "CLIENT_CHECK_INTERVAL" => :'daemon.client_check_interval',
65
+ "CLIENT_QUEUE_DEPTH" => :'daemon.client_queue_depth',
66
+ "CLIENT_WRITE_TIMEOUT" => :'daemon.client_write_timeout',
67
+ "SSL_CERT_PATH" => :'daemon.ssl_cert_path',
68
+ "SSL_CERT_DIR" => :'daemon.ssl_cert_dir',
69
+
70
+ # == Legacy settings ==
71
+ #
51
72
  'AGENT_MAX_MEMORY' => :'agent.max_memory',
52
- 'REPORT_HOST' => :'report.host',
53
- 'REPORT_PORT' => :'report.port',
54
- 'REPORT_SSL' => :'report.ssl',
55
- 'REPORT_DEFLATE' => :'report.deflate',
56
- 'REPORT_PROXY_ADDR' => :'report.proxy_addr',
57
- 'REPORT_PROXY_PORT' => :'report.proxy_port',
58
- 'REPORT_PROXY_USER' => :'report.proxy_user',
59
- 'REPORT_PROXY_PASS' => :'report.proxy_user',
60
- 'ACCOUNTS_HOST' => :'accounts.host',
61
- 'ACCOUNTS_PORT' => :'accounts.port',
62
- 'ACCOUNTS_SSL' => :'accounts.ssl',
63
- 'ACCOUNTS_DEFLATE' => :'accounts.deflate',
64
- 'ACCOUNTS_PROXY_ADDR' => :'accounts.proxy_addr',
65
- 'ACCOUNTS_PROXY_PORT' => :'accounts.proxy_port',
66
- 'ACCOUNTS_PROXY_USER' => :'accounts.proxy_user',
67
- 'ACCOUNTS_PROXY_PASS' => :'accounts.proxy_user',
68
73
  'ME_AUTHENTICATION' => :'me.authentication',
69
74
  'ME_CREDENTIALS_PATH' => :'me.credentials_path',
70
- 'METRICS_REPORT_INTERVAL' => :'metrics.report_interval',
71
- 'TEST_CONSTANT_FLUSH' => :'test.constant_flush',
72
75
  'TEST_IGNORE_TOKEN' => :'test.ignore_token' }
73
76
 
74
77
  # Default values for Skylight configuration keys
75
78
  DEFAULTS = {
79
+ :'version' => VERSION,
80
+ :'auth_url' => 'https://www.skylight.io/agent/authenticate',
81
+ :'daemon.lazy_start' => true,
82
+ :'daemon.ssl_cert_path' => Util::SSL.ca_cert_file_or_default,
83
+ :'daemon.ssl_cert_dir' => Util::SSL.ca_cert_dir,
84
+
85
+ # == Legacy ==
76
86
  :'log_file' => '-'.freeze,
77
87
  :'log_level' => 'INFO'.freeze,
78
88
  :'alert_log_file' => '-'.freeze,
79
- :'hostname' => default_hostname,
89
+ :'hostname' => Util::Hostname.default_hostname,
80
90
  :'agent.keepalive' => 60,
81
91
  :'agent.interval' => 5,
82
92
  :'agent.sample' => 200,
@@ -91,7 +101,16 @@ module Skylight
91
101
  :'accounts.deflate' => false,
92
102
  :'me.credentials_path' => '~/.skylight',
93
103
  :'metrics.report_interval' => 60
94
- }.freeze
104
+ }
105
+
106
+ if Skylight.native?
107
+ native_path = Skylight.libskylight_path
108
+
109
+ DEFAULTS[:'daemon.lib_path'] = native_path
110
+ DEFAULTS[:'daemon.exec_path'] = File.join(native_path, 'skylightd')
111
+ end
112
+
113
+ DEFAULTS.freeze
95
114
 
96
115
  REQUIRED = {
97
116
  :'authentication' => "authentication token",
@@ -99,10 +118,50 @@ module Skylight
99
118
  :'report.host' => "skylight remote host",
100
119
  :'report.port' => "skylight remote port" }
101
120
 
121
+ ALWAYS_INCLUDE_IN_ENV = [
122
+ :version,
123
+ :'daemon.lazy_start',
124
+ :'daemon.lib_path',
125
+ :'daemon.exec_path',
126
+ :'daemon.ssl_cert_dir',
127
+ :'daemon.ssl_cert_path' ]
128
+
102
129
  VALIDATORS = {
103
130
  :'agent.interval' => [lambda { |v, c| Integer === v && v > 0 }, "must be an integer greater than 0"]
104
131
  }
105
132
 
133
+ # @api private
134
+ attr_reader :environment
135
+
136
+ # @api private
137
+ def initialize(*args)
138
+ attrs = {}
139
+
140
+ if Hash === args.last
141
+ attrs = args.pop.dup
142
+ end
143
+
144
+ @values = {}
145
+ @priority = {}
146
+ @regexp = nil
147
+
148
+ p = attrs.delete(:priority)
149
+
150
+ if @environment = args[0]
151
+ @regexp = /^#{Regexp.escape(@environment)}\.(.+)$/
152
+ end
153
+
154
+ attrs.each do |k, v|
155
+ self[k] = v
156
+ end
157
+
158
+ if p
159
+ p.each do |k, v|
160
+ @priority[k.to_sym] = v
161
+ end
162
+ end
163
+ end
164
+
106
165
  def self.load(path = nil, environment = nil, env = ENV)
107
166
  attrs = {}
108
167
  version = nil
@@ -137,6 +196,10 @@ module Skylight
137
196
  def self.remap_env(env)
138
197
  ret = {}
139
198
 
199
+ return ret unless env
200
+
201
+ ret[:proxy_url] = detect_proxy_url(env)
202
+
140
203
  env.each do |k, val|
141
204
  # Support deprecated SK_ key prefix
142
205
  next unless k =~ /^(?:SK|SKYLIGHT)_(.+)$/
@@ -152,40 +215,15 @@ module Skylight
152
215
  else val
153
216
  end
154
217
  end
155
- end if env
218
+ end
156
219
 
157
220
  ret
158
221
  end
159
222
 
160
- # @api private
161
- attr_reader :environment
162
-
163
- # @api private
164
- def initialize(*args)
165
- attrs = {}
166
-
167
- if Hash === args.last
168
- attrs = args.pop.dup
169
- end
170
-
171
- @values = {}
172
- @priority = {}
173
- @regexp = nil
174
-
175
- p = attrs.delete(:priority)
176
-
177
- if @environment = args[0]
178
- @regexp = /^#{Regexp.escape(@environment)}\.(.+)$/
179
- end
180
-
181
- attrs.each do |k, v|
182
- self[k] = v
183
- end
184
-
185
- if p
186
- p.each do |k, v|
187
- @priority[k.to_sym] = v
188
- end
223
+ def self.detect_proxy_url(env)
224
+ if u = env['HTTP_PROXY'] || env['http_proxy']
225
+ u = "http://#{u}" unless u =~ %r[://]
226
+ u
189
227
  end
190
228
  end
191
229
 
@@ -207,24 +245,6 @@ module Skylight
207
245
  true
208
246
  end
209
247
 
210
- # @api private
211
- def validate_token
212
- return :ok if skip_validation?
213
-
214
- http_auth = Util::HTTP.new(self, :accounts, timeout: 5)
215
-
216
- res = http_auth.get("/agent/authenticate?hostname=#{URI.escape(self[:'hostname'])}")
217
-
218
- case res.status
219
- when 200...300
220
- :ok
221
- when 400...500
222
- :invalid
223
- else
224
- :unknown
225
- end
226
- end
227
-
228
248
  def key?(key)
229
249
  key = key.to_sym
230
250
  @priority.key?(key) || @values.key?(key)
@@ -280,12 +300,32 @@ module Skylight
280
300
 
281
301
  alias []= set
282
302
 
303
+ def duration_ms(key, default = nil)
304
+ if (v = self[key]) && v.to_s =~ /^\s*(\d+)(s|sec|ms|micros|nanos)?\s*$/
305
+ v = $1.to_i
306
+ case $2
307
+ when "ms"
308
+ v
309
+ when "micros"
310
+ v / 1_000
311
+ when "nanos"
312
+ v / 1_000_000
313
+ else # "s", "sec", nil
314
+ v * 1000
315
+ end
316
+ else
317
+ default
318
+ end
319
+ end
320
+
283
321
  def to_env
284
- ret = {}
322
+ ret = []
285
323
 
286
324
  ENV_TO_KEY.each do |k, v|
287
- if (c = get(v)) != DEFAULTS[v]
288
- ret["SKYLIGHT_#{k}"] = cast_for_env(c)
325
+ c = get(v)
326
+ # Always need to pass daemon lib_path config even when default
327
+ if c != DEFAULTS[v] || ALWAYS_INCLUDE_IN_ENV.include?(v)
328
+ ret << "SKYLIGHT_#{k}" << cast_for_env(c) if c
289
329
  end
290
330
  end
291
331
 
@@ -313,21 +353,11 @@ authentication: #{self[:authentication]}
313
353
  #
314
354
  #
315
355
 
316
- # @api private
317
- def worker
318
- @worker ||= Worker::Builder.new(self)
319
- end
320
-
321
356
  # @api private
322
357
  def gc
323
358
  @gc ||= GC.new(self, get('gc.profiler', VM::GC.new))
324
359
  end
325
360
 
326
- # @api private
327
- def constant_flush?
328
- get('test.constant_flush')
329
- end
330
-
331
361
  # @api private
332
362
  def ignore_token?
333
363
  get('test.ignore_token')
@@ -339,29 +369,8 @@ authentication: #{self[:authentication]}
339
369
 
340
370
  def logger
341
371
  @logger ||=
342
- begin
343
- MUTEX.synchronize do
344
- unless l = @logger
345
- out = get(:'log_file')
346
- out = STDOUT if out == '-'
347
-
348
- unless IO === out
349
- out = File.expand_path(out, root)
350
- FileUtils.mkdir_p(File.dirname(out))
351
- end
352
-
353
- l = Logger.new(out)
354
- l.level =
355
- case get(:'log_level')
356
- when /^debug$/i then Logger::DEBUG
357
- when /^info$/i then Logger::INFO
358
- when /^warn$/i then Logger::WARN
359
- when /^error$/i then Logger::ERROR
360
- end
361
- end
362
-
363
- l
364
- end
372
+ MUTEX.synchronize do
373
+ load_logger
365
374
  end
366
375
  end
367
376
 
@@ -377,7 +386,7 @@ authentication: #{self[:authentication]}
377
386
  out = get(:'alert_log_file')
378
387
 
379
388
  if out == '-'
380
- out = AlertLoggerIO.new(self)
389
+ out = Util::AlertLogger.new(load_logger)
381
390
  elsif !(IO === out)
382
391
  out = File.expand_path(out, root)
383
392
  FileUtils.mkdir_p(File.dirname(out))
@@ -396,9 +405,31 @@ authentication: #{self[:authentication]}
396
405
  @alert_logger = logger
397
406
  end
398
407
 
399
-
400
408
  private
401
409
 
410
+ def load_logger
411
+ unless l = @logger
412
+ out = get(:'log_file')
413
+ out = STDOUT if out == '-'
414
+
415
+ unless IO === out
416
+ out = File.expand_path(out, root)
417
+ FileUtils.mkdir_p(File.dirname(out))
418
+ end
419
+
420
+ l = Logger.new(out)
421
+ l.level =
422
+ case get(:'log_level')
423
+ when /^debug$/i then Logger::DEBUG
424
+ when /^info$/i then Logger::INFO
425
+ when /^warn$/i then Logger::WARN
426
+ when /^error$/i then Logger::ERROR
427
+ end
428
+ end
429
+
430
+ l
431
+ end
432
+
402
433
  def cast_for_env(v)
403
434
  case v
404
435
  when true then 'true'
@@ -407,6 +438,5 @@ authentication: #{self[:authentication]}
407
438
  else v.to_s
408
439
  end
409
440
  end
410
-
411
441
  end
412
442
  end