# 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