lib/instana/agent.rb in instana-0.8.4 vs lib/instana/agent.rb in instana-0.8.6

- old
+ new

@@ -23,13 +23,13 @@ # Snapshot data is collected once per process but resent # every 10 minutes along side process metrics. @snapshot = take_snapshot - # Set last snapshot to 10 minutes ago - # so we send a snapshot on first report - @last_snapshot = Time.now - 601 + # Set last snapshot to just under 10 minutes ago + # so we send a snapshot sooner than later + @last_snapshot = Time.now - 570 # Timestamp of the last successful response from # entity data reporting. @entity_last_seen = Time.now @@ -41,11 +41,27 @@ # Detect if we're on linux or not (used in host_agent_ready?) @is_linux = (RUBY_PLATFORM =~ /linux/i) ? true : false # In case we're running in Docker, have the default gateway available # to check in case we're running in bridged network mode - @default_gateway = `/sbin/ip route | awk '/default/ { print $3 }'`.chomp + if @is_linux + @default_gateway = `/sbin/ip route | awk '/default/ { print $3 }'`.chomp + else + @default_gateway = nil + end + + # The agent UUID returned from the host agent + @agent_uuid = nil + + @process = {} + cmdline = ProcTable.ps(Process.pid).cmdline.split("\0") + @process[:name] = cmdline.shift + @process[:arguments] = cmdline + @process[:original_pid] = Process.pid + # This is usually Process.pid but in the case of docker, the host agent + # will return to us the true host pid in which we use to report data. + @process[:report_pid] = nil end ## # start # @@ -98,64 +114,78 @@ # # Collect process ID, name and arguments to notify # the host agent. # def announce_sensor - process = ProcTable.ps(Process.pid) announce_payload = {} - announce_payload[:pid] = Process.pid + announce_payload[:pid] = pid_namespace? ? get_real_pid : Process.pid + announce_payload[:args] = @process[:arguments] - arguments = process.cmdline.split(' ') - arguments.shift - announce_payload[:args] = arguments - uri = URI.parse("http://#{@host}:#{@port}/#{DISCOVERY_PATH}") req = Net::HTTP::Put.new(uri) req.body = announce_payload.to_json + # ::Instana.logger.debug "Announce: http://#{@host}:#{@port}/#{DISCOVERY_PATH} - payload: #{req.body}" + response = make_host_agent_request(req) - response && (response.code.to_i == 200) ? true : false + + if response && (response.code.to_i == 200) + data = JSON.parse(response.body) + @process[:report_pid] = data['pid'] + @agent_uuid = data['agentUuid'] + true + else + false + end rescue => e - Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" + Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" Instana.logger.debug e.backtrace.join("\r\n") + return false end ## # report_entity_data # # Method to report metrics data to the host agent. # def report_entity_data(payload) with_snapshot = false - path = "com.instana.plugin.ruby.#{Process.pid}" + path = "com.instana.plugin.ruby.#{@process[:report_pid]}" uri = URI.parse("http://#{@host}:#{@port}/#{path}") req = Net::HTTP::Post.new(uri) # Every 5 minutes, send snapshot data as well if (Time.now - @last_snapshot) > 600 with_snapshot = true payload.merge!(@snapshot) + + # Add in process related that could have changed since + # snapshot was taken. + p = { :pid => @process[:report_pid] } + p[:name] = @process[:name] + p[:exec_args] = @process[:arguments] + payload.merge!(p) end req.body = payload.to_json response = make_host_agent_request(req) if response last_entity_response = response.code.to_i + #::Instana.logger.debug "entity http://#{@host}:#{@port}/#{path}: response=#{last_entity_response}: #{payload.to_json}" + if last_entity_response == 200 @entity_last_seen = Time.now @last_snapshot = Time.now if with_snapshot - #::Instana.logger.debug "entity response #{last_entity_response}: #{payload.to_json}" return true end - #::Instana.logger.debug "entity response #{last_entity_response}: #{payload.to_json}" end false rescue => e - Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" + Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" Instana.logger.debug e.backtrace.join("\r\n") end ## # host_agent_ready? @@ -190,11 +220,11 @@ @host = @default_gateway return true end false rescue => e - Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" + Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" Instana.logger.debug e.backtrace.join("\r\n") return false end private @@ -230,44 +260,61 @@ # Centralization of the net/http communications # with the host agent. Pass in a prepared <req> # of type Net::HTTP::Get|Put|Head # def make_host_agent_request(req) - req[:Accept] = MIME_JSON - req[:'Content-Type'] = MIME_JSON + req['Accept'] = MIME_JSON + req['Content-Type'] = MIME_JSON response = nil Net::HTTP.start(req.uri.hostname, req.uri.port, :open_timeout => 1, :read_timeout => 1) do |http| response = http.request(req) end response rescue Errno::ECONNREFUSED => e - Instana.logger.debug "Agent not responding. Connection refused." return nil rescue => e - Instana.logger.debug "Host agent request error: #{e.inspect}" + Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" + Instana.logger.debug e.backtrace.join("\r\n") return nil end ## + # pid_namespace? + # + # Indicates whether we are running in a pid namespace (such as + # Docker). + # + def pid_namespace? + return false unless @is_linux + Process.pid != get_real_pid + end + + ## + # get_real_pid + # + # Attempts to determine the true process ID by querying the + # /proc/<pid>/sched file. This works on linux currently. + # + def get_real_pid + raise RuntimeError.new("Unsupported platform: get_real_pid") unless @is_linux + v = File.open("/proc/#{Process.pid}/sched", &:readline) + v.match(/\d+/).to_s.to_i + end + + ## # take_snapshot # # Method to collect up process info for snapshots. This # is generally used once per process. # def take_snapshot data = {} data[:sensorVersion] = ::Instana::VERSION - data[:pid] = ::Process.pid data[:ruby_version] = RUBY_VERSION - process = ::ProcTable.ps(Process.pid) - arguments = process.cmdline.split(' ') - data[:name] = arguments.shift - data[:exec_args] = arguments - # Since a snapshot is only taken on process boot, # this is ok here. data[:start_time] = Time.now.to_s # Framework Detection @@ -287,10 +334,10 @@ data[:framework] = "Sinatra #{::Sinatra::VERSION}" end data rescue => e - ::Instana.logger.debug "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" + ::Instana.logger.error "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" ::Instana.logger.debug e.backtrace.join("\r\n") return data end end end