# Report environment data on the first request. # # XXX: Completely untested outside of MRI (official Ruby runtime) 1.9.3 and up! require 'socket' module Immunio class EnvironmentReporter def initialize(app) @app = app # No need for mutex, reporting environment is idempotent @reported = false end def call(env) response = @app.call(env) # Report gem usage at the end so that everything as been loaded. Request.time "plugin", "#{Module.nesting[0]}::#{__method__}" do report if !@reported end response end def runtime_name if Object.const_defined?(:RUBY_DESCRIPTION) && RUBY_DESCRIPTION =~ /Enterprise/ 'ree' elsif Object.const_defined? :RUBY_ENGINE RUBY_ENGINE.to_s else 'unknown' end end def runtime_version case runtime_name when 'ruby' then RUBY_VERSION when 'jruby' then JRUBY_VERSION when 'rbx' then Rubinius::VERSION else 'unknown' end end def ips ips = Socket.ip_address_list.map(&:ip_address) ips.reject { |ip| ['::1', '127.0.0.1'].include?(ip) } end def hostname Socket.gethostname rescue SocketError end def hostname_ip # SocketError is raised if we can't fetch the hostname with `Socket.gethostname`. Addrinfo.getaddrinfo(hostname, nil).first.ip_address rescue SocketError end def report @reported = true info = { runtime: { name: runtime_name, version: runtime_version }, language: { name: 'ruby', version: RUBY_VERSION }, platform: { description: RUBY_PLATFORM }, host: { hostname: hostname, hostname_ip: hostname_ip, ips: ips }, dependencies: {} } Gem.loaded_specs.each_pair do |name, spec| info[:dependencies][name] = spec.version.to_s end Immunio.agent.environment = info end end end