Module: Sensu::Daemon

Includes:
Utilities
Included in:
API::Process, Client::Process, Server::Process
Defined in:
lib/sensu/daemon.rb

Constant Summary

Constants included from Utilities

Utilities::EVAL_PREFIX

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utilities

#attributes_match?, #check_subdued?, #deep_dup, #deep_merge, #determine_check_cron_time, #eval_attribute_value, #find_attribute_value, #in_time_window?, #in_time_windows?, #object_substitute_tokens, #process_cpu_times, #process_eval_string, #random_uuid, #redact_sensitive, #retry_until_true, #substitute_tokens, #system_address, #system_hostname, #testing?

Instance Attribute Details

#settingsObject (readonly)

Returns the value of attribute settings.



37
38
39
# File 'lib/sensu/daemon.rb', line 37

def settings
  @settings
end

#start_timeObject (readonly)

Returns the value of attribute start_time.



37
38
39
# File 'lib/sensu/daemon.rb', line 37

def start_time
  @start_time
end

Instance Method Details

#initialize(options = {}) ⇒ Object

Initialize the Sensu process. Set the start time, initial service state, double the maximum number of EventMachine timers, set up the logger, and load settings. This method will load extensions and setup Sensu Spawn if the Sensu process is not the Sensu API. This method can and optionally daemonize the process and/or create a PID file.

Parameters:

  • options (Hash) (defaults to: {})


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/sensu/daemon.rb', line 47

def initialize(options={})
  @start_time = Time.now.to_i
  @state = :initializing
  @timers = {:run => []}
  unless EM::reactor_running?
    EM::epoll
    EM::set_max_timers(200000)
    EM::error_handler do |error|
      unexpected_error(error)
    end
  end
  setup_logger(options)
  load_settings(options)
  unless sensu_service_name == "api"
    load_extensions(options)
    setup_spawn
  end
  setup_process(options)
end

#load_extensions(options = {}) ⇒ Object

Load Sensu extensions and log any notices. Set the logger and settings for each extension instance. This method creates the extensions instance variable: ‘@extensions`.

github.com/sensu/sensu-extensions github.com/sensu/sensu-extension

Parameters:

  • options (Hash) (defaults to: {})


186
187
188
189
190
191
192
193
194
195
# File 'lib/sensu/daemon.rb', line 186

def load_extensions(options={})
  extensions_options = options.merge(:extensions => @settings[:extensions])
  @extensions = Extensions.get(extensions_options)
  log_notices(@extensions.warnings)
  extension_settings = @settings.to_hash.dup
  @extensions.all.each do |extension|
    extension.logger = @logger
    extension.settings = extension_settings
  end
end

#load_settings(options = {}) ⇒ Object

Load Sensu settings. This method creates the settings instance variable: ‘@settings`. If the `validate_config` option is true, this method calls `validate_settings!()` to validate the latest compiled configuration settings and will then exit the process. If the `print_config` option is true, this method calls `print_settings!()` to output the compiled configuration settings and will then exit the process. If there are loading or validation errors, they will be logged (notices), and this method will exit(2) the process.

github.com/sensu/sensu-settings

Parameters:

  • options (Hash) (defaults to: {})


165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/sensu/daemon.rb', line 165

def load_settings(options={})
  @settings = Settings.get(options)
  validate_settings!(@settings) if options[:validate_config]
  log_notices(@settings.warnings)
  log_notices(@settings.errors, :fatal)
  print_settings!(@settings) if options[:print_config]
  unless @settings.errors.empty?
    @logger.fatal("SENSU NOT RUNNING!")
    exit 2
  end
  @settings.set_env!
end

#log_notices(notices = [], level = :warn) ⇒ Object

Log setting or extension loading notices, sensitive information is redacted.

Parameters:

  • notices (Array) (defaults to: [])

    to be logged.

  • level (Symbol) (defaults to: :warn)

    to log the notices at.



111
112
113
114
115
116
# File 'lib/sensu/daemon.rb', line 111

def log_notices(notices=[], level=:warn)
  notices.each do |concern|
    message = concern.delete(:message)
    @logger.send(level, message, redact_sensitive(concern))
  end
end

#pauseObject

Pause the Sensu service and set the service state to ‘:paused`. This method will likely be overridden by a subclass.



230
231
232
# File 'lib/sensu/daemon.rb', line 230

def pause
  @state = :paused
end

Print the Sensu settings (JSON) to STDOUT and immediately exit the process with the appropriate exit status code. This method is used while troubleshooting configuration issues, triggered by a CLI argument, e.g. ‘–print_config`. Sensu settings with sensitive values (e.g. passwords) are first redacted.

Parameters:

  • settings (Object)


144
145
146
147
148
149
# File 'lib/sensu/daemon.rb', line 144

def print_settings!(settings)
  redacted_settings = redact_sensitive(settings.to_hash)
  @logger.warn("outputting compiled configuration and exiting")
  puts Sensu::JSON.dump(redacted_settings, :pretty => true)
  exit(settings.errors.empty? ? 0 : 2)
end

#resumeObject

Resume the paused Sensu service and set the service state to ‘:running`. This method will likely be overridden by a subclass.



236
237
238
# File 'lib/sensu/daemon.rb', line 236

def resume
  @state = :running
end

#setup_logger(options = {}) ⇒ Object

Set up the Sensu logger and its process signal traps for log rotation and debug log level toggling. This method creates the logger instance variable: ‘@logger`.

github.com/sensu/sensu-logger

Parameters:

  • options (Hash) (defaults to: {})


101
102
103
104
# File 'lib/sensu/daemon.rb', line 101

def setup_logger(options={})
  @logger = Logger.get(options)
  @logger.setup_signal_traps
end

#setup_process(options) ⇒ Object

Manage the current process, optionally daemonize and/or write the current process ID to a PID file.

Parameters:

  • options (Hash)


215
216
217
218
# File 'lib/sensu/daemon.rb', line 215

def setup_process(options)
  daemonize if options[:daemonize]
  write_pid(options[:pid_file]) if options[:pid_file]
end

#setup_redis {|Object| ... } ⇒ Object

Set up the Redis connection. Sensu uses Redis as a data store, to store the client registry, current events, etc. The Sensu service will stop gracefully in the event of a Redis error, and pause/resume in the event of connectivity issues. This method creates the Redis instance variable: ‘@redis`.

github.com/sensu/sensu-redis

Yields:

  • (Object)

    passes initialized and connected Redis connection object to the callback/block.



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/sensu/daemon.rb', line 323

def setup_redis
  @logger.debug("connecting to redis", :settings => @settings[:redis])
  Redis.logger = @logger
  Redis.connect(@settings[:redis]) do |connection|
    @redis = connection
    @redis.on_error do |error|
      @logger.error("redis connection error", :error => error.to_s)
    end
    @redis.before_reconnect do
      unless testing?
        @logger.warn("reconnecting to redis")
        pause
      end
    end
    @redis.after_reconnect do
      @logger.info("reconnected to redis")
      resume
    end
    yield(@redis) if block_given?
  end
end

#setup_signal_trapsObject

Set up process signal traps. This method uses the ‘STOP_SIGNALS` constant to determine which process signals will result in a graceful service stop. A periodic timer must be used to poll for received signals, as Mutex#lock cannot be used within the context of `trap()`.



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/sensu/daemon.rb', line 254

def setup_signal_traps
  @signals = []
  STOP_SIGNALS.each do |signal|
    Signal.trap(signal) do
      @signals << signal
    end
  end
  EM::PeriodicTimer.new(1) do
    signal = @signals.shift
    if STOP_SIGNALS.include?(signal)
      @logger.warn("received signal", :signal => signal)
      stop
    end
  end
end

#setup_spawnObject

Set up Sensu spawn, creating a worker to create, control, and limit spawned child processes. This method adjusts the EventMachine thread pool size to accommodate the concurrent process spawn limit and other Sensu process operations.

github.com/sensu/sensu-spawn



203
204
205
206
207
208
209
# File 'lib/sensu/daemon.rb', line 203

def setup_spawn
  @logger.info("configuring sensu spawn", :settings => @settings[:sensu][:spawn])
  threadpool_size = @settings[:sensu][:spawn][:limit] + 10
  @logger.debug("setting eventmachine threadpool size", :size => threadpool_size)
  EM::threadpool_size = threadpool_size
  Spawn.setup(@settings[:sensu][:spawn])
end

#setup_transport {|Object| ... } ⇒ Object

Set up the Sensu transport connection. Sensu uses a transport API, allowing it to use various message brokers. By default, Sensu will use the built-in “rabbitmq” transport. The Sensu service will stop gracefully in the event of a transport error, and pause/resume in the event of connectivity issues. This method creates the transport instance variable: ‘@transport`.

github.com/sensu/sensu-transport

Yields:

  • (Object)

    passes initialized and connected Transport connection object to the callback/block.



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/sensu/daemon.rb', line 281

def setup_transport
  transport_name = @settings[:transport][:name]
  transport_settings = @settings[transport_name]
  @logger.debug("connecting to transport", {
    :name => transport_name,
    :settings => transport_settings
  })
  Transport.logger = @logger
  Transport.connect(transport_name, transport_settings) do |connection|
    @transport = connection
    @transport.on_error do |error|
      @logger.error("transport connection error", :error => error.to_s)
      if @settings[:transport][:reconnect_on_error]
        @transport.reconnect
      else
        stop
      end
    end
    @transport.before_reconnect do
      unless testing?
        @logger.warn("reconnecting to transport")
        pause
      end
    end
    @transport.after_reconnect do
      @logger.info("reconnected to transport")
      resume
    end
    yield(@transport) if block_given?
  end
end

#startObject

Start the Sensu service and set the service state to ‘:running`. This method will likely be overridden by a subclass. Yield if a block is provided.



223
224
225
226
# File 'lib/sensu/daemon.rb', line 223

def start
  @state = :running
  yield if block_given?
end

#stopObject

Stop the Sensu service and set the service state to ‘:stopped`. This method will likely be overridden by a subclass. This method should stop the EventMachine event loop.



243
244
245
246
247
# File 'lib/sensu/daemon.rb', line 243

def stop
  @state = :stopped
  @logger.warn("stopping reactor")
  EM::stop_event_loop
end

#unexpected_error(error) ⇒ Object

Handle an unexpected error. This method is used for EM global catch-all error handling, accepting an error object. Error handling is opt-in via a configuration option, e.g. ‘“sensu”: true`. If a user does not opt-in, the provided error will be raised (uncaught). If a user opts-in via configuration, the error will be logged and ignored :itsfine:.

Parameters:

  • error (Object)


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/sensu/daemon.rb', line 75

def unexpected_error(error)
  if @settings && @settings[:sensu][:global_error_handler]
    backtrace = error.backtrace.join("\n")
    if @logger
      @logger.warn("global catch-all error handling enabled")
      @logger.fatal("unexpected error - please address this immediately", {
        :error => error.to_s,
        :error_class => error.class,
        :backtrace => backtrace
      })
    else
      puts "global catch-all error handling enabled"
      puts "unexpected error - please address this immediately: #{error.to_s}\n#{error.class}\n#{backtrace}"
    end
  else
    raise error
  end
end

#validate_settings!(settings) ⇒ Object

Determine if the Sensu settings are valid, if there are load or validation errors, and immediately exit the process with the appropriate exit status code. This method is used to determine if the latest configuration changes are valid prior to restarting the Sensu service, triggered by a CLI argument, e.g. ‘–validate_config`.

Parameters:

  • settings (Object)


126
127
128
129
130
131
132
133
134
135
# File 'lib/sensu/daemon.rb', line 126

def validate_settings!(settings)
  if settings.errors.empty?
    puts "configuration is valid"
    exit
  else
    puts "configuration is invalid"
    puts Sensu::JSON.dump({:errors => @settings.errors}, :pretty => true)
    exit 2
  end
end