# encoding: utf-8
# This file is distributed under Ting Yun's license terms.
require 'ting_yun/support/exception'
require 'ting_yun/support/hostname'
require 'ting_yun/configuration/server_source'
require 'ting_yun/agent/instance_methods/handle_errors'
require 'ting_yun/environment_report'
module TingYun
module Agent
module InstanceMethods
module Connect
include HandleErrors
# number of attempts we've made to contact the server
attr_accessor :connect_attempts
# Disconnect just sets connected to false, which prevents
# the agent from trying to connect again
def disconnect
@connect_state = :disconnected
true
end
def connected?
@connect_state == :connected
end
def disconnected?
@connect_state == :disconnected
end
# Don't connect if we're already connected, or if we tried to connect
# and were rejected with prejudice because of a license issue, unless
# we're forced to by force_reconnect.
def should_connect?(force=false)
force || (!connected? && !disconnected?)
end
# Retry period is a minute for each failed attempt that
# we've made. This should probably do some sort of sane TCP
# backoff to prevent hammering the server, but a minute for
# each attempt seems to work reasonably well.
def connect_retry_period
[600, connect_attempts * 60].min
end
def note_connect_failure
self.connect_attempts += 1
end
def generate_environment_report
@environment_report = environment_for_connect
end
# We've seen objects in the environment report (Rails.env in
# particular) that can't seralize to JSON. Cope with that here and
# clear out so downstream code doesn't have to check again.
def sanitize_environment_report
if !@service.valid_to_marshal?(@environment_report)
@environment_report = {}
end
end
# Checks whether we should send environment info, and if so,
# returns the snapshot from the local environment.
# Generating the EnvironmentReport has the potential to trigger
# require calls in Rails environments, so this method should only
# be called synchronously from on the main thread.
def environment_for_connect
::TingYun::Agent.config[:send_environment_info] ? TingYun::EnvironmentReport.new.data : {}
end
# Initializes the hash of settings that we send to the
# server. Returns a literal hash containing the options
def connect_settings
sanitize_environment_report
settings = {
:pid => $$,
:port => ::TingYun::Agent.config[:port],
:host => local_host,
:appName => ::TingYun::Agent.config.app_names,
:language => 'Ruby',
:agentVersion => ::TingYun::VERSION::STRING,
:env => @environment_report,
:config => ::TingYun::Agent.config.to_collector_hash
}
settings
end
def local_host
TingYun::Support::Hostname.get
end
# Returns connect data passed back from the server
def connect_to_server
@service.connect(connect_settings)
end
#merge server config
def query_server_for_configuration
finish_setup(connect_to_server)
end
# * :keep_retrying => false to only try to connect once, and
# return with the connection set to nil. This ensures we may try again
# later (default true).
# * force_reconnect => true if you want to establish a new connection
# to the server before running the worker loop. This means you get a separate
# agent run and Ting Yun sees it as a separate instance (default is false).
def catch_errors
yield
rescue TingYun::Support::Exception::ExpiredConfigurationException => e
handle_force_restart(e)
retry
rescue TingYun::Support::Exception::InvalidDataTokenException => e
handle_force_restart(e)
retry
rescue TingYun::Support::Exception::InvalidDataException => e
handle_server_error(e)
rescue => e
handle_other_error(e)
end
# Takes a hash of configuration data returned from the
# server and uses it to set local variables and to
# initialize various parts of the agent that are configured
# separately.
#
def finish_setup(config_data)
return if config_data == nil
if config_data['config']
::TingYun::Agent.logger.debug "Using config from server"
end
::TingYun::Agent.logger.debug "Server provided config: #{config_data.inspect}"
server_config = TingYun::Configuration::ServerSource.new(config_data)
::TingYun::Agent.config.replace_or_add_config(server_config)
#log_connection!(config_data)
end
def log_connection!(config_data)
::TingYun::Agent.logger.debug "Connected to TingYun Service at #{@service.collector.name}"
::TingYun::Agent.logger.debug "Application Run = #{@service.applicationId}."
::TingYun::Agent.logger.debug "Connection data = #{config_data.inspect}"
if config_data['messages'] && config_data['messages'].any?
log_collector_messages(config_data['messages'])
end
end
def log_collector_messages(messages)
messages.each do |message|
::TingYun::Agent.logger.send(message['level'].downcase, message['message'])
end
end
def connect_in_sync
TingYun::Agent.disable_all_tracing { connect!(:keep_retrying => false) }
end
end
end
end
end