Module: Instana::Util

Defined in:
lib/instana/util.rb

Constant Summary collapse

ID_RANGE =
-2**63..2**63-1

Class Method Summary collapse

Class Method Details

.collect_process_infoObject

Used in class initialization and after a fork, this method collects up process information



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/instana/util.rb', line 130

def collect_process_info
  process = {}
  cmdline_file = "/proc/#{Process.pid}/cmdline"

  # If there is a /proc filesystem, we read this manually so
  # we can split on embedded null bytes.  Otherwise (e.g. OSX, Windows)
  # use ProcTable.
  if File.exist?(cmdline_file)
    cmdline = IO.read(cmdline_file).split(?\x00)
  else
    # Attempt to support older versions of sys-proctable and ffi.
    #
    # Alternatively we could use Sys::ProcTable::VERSION here but the
    # consistency across historical versions is unknown.  Alternative
    # to the alternative, would be Ruby metaprogramming using the `arity`
    # and `parameters` methods.
    # e.g ProcTable.method(:ps).arity/parameters
    if Gem.loaded_specs.key?("sys-proctable") &&
      (Gem.loaded_specs["sys-proctable"].version >= Gem::Version.new("1.2.0"))
      cmdline = ProcTable.ps(:pid => Process.pid).cmdline.split(' ')
    else
      cmdline = ProcTable.ps(Process.pid).cmdline.split(' ')
    end
  end

  if RUBY_PLATFORM =~ /darwin/i
    cmdline.delete_if{ |e| e.include?('=') }
    process[:name] = cmdline.join(' ')
  else
    process[:name] = cmdline.shift
    process[:arguments] = cmdline
  end

  process[:pid] = Process.pid
  # This is usually Process.pid but in the case of containers, the host agent
  # will return to us the true host pid in which we use to report data.
  process[:report_pid] = nil
  process
end

.generate_idInteger

Generate a random 64bit ID

Returns:

  • (Integer)

    a random 64bit integer



215
216
217
218
# File 'lib/instana/util.rb', line 215

def generate_id
  # Max value is 9223372036854775807 (signed long in Java)
  rand(ID_RANGE)
end

.get_app_nameObject

Best effort to determine a name for the instrumented application on the dashboard.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/instana/util.rb', line 173

def get_app_name
  if ENV.key?('INSTANA_SERVICE_NAME')
    return ENV['INSTANA_SERVICE_NAME']
  end


  if defined?(::Resque) && ($0 =~ /resque-#{Resque::Version}/)
    return "Resque Worker"
  end

  if defined?(::RailsLts) || defined?(::Rails)
    return Rails.application.class.to_s.split('::')[0]
  end

  return File.basename($0)
rescue Exception => e
  Instana.logger.info "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
  Instana.logger.debug { e.backtrace.join("\r\n") }
end

.get_rb_source(file) ⇒ Object

Retrieves and returns the source code for any ruby files requested by the UI via the host agent

Parameters:

  • file (String)

    The fully qualified path to a file



74
75
76
77
78
79
80
81
82
# File 'lib/instana/util.rb', line 74

def get_rb_source(file)
  if (file =~ /.rb$/).nil?
    { :error => "Only Ruby source files are allowed. (*.rb)" }
  else
    { :data => File.read(file) }
  end
rescue => e
  return { :error => e.inspect }
end

.header_to_id(header_id) ⇒ Integer

Convert a received header value into a valid ID

Parameters:

  • header_id (String)

    the header value to be converted

Returns:

  • (Integer)


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/instana/util.rb', line 243

def header_to_id(header_id)
  if !header_id.is_a?(String)
    Instana.logger.debug "header_to_id received a #{header_id.class}: returning 0"
    return 0
  end
  if header_id.length < 16
    # The header is less than 16 chars.  Prepend
    # zeros so we can convert correctly
    missing = 16 - header_id.length
    header_id = ("0" * missing) + header_id
  end
  [header_id].pack("H*").unpack("q>")[0]
rescue => e
  Instana.logger.info "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
  Instana.logger.debug { e.backtrace.join("\r\n") }
end

.id_to_header(id) ⇒ String

Convert an ID to a value appropriate to pass in a header.

Parameters:

  • id (Integer)

    the id to be converted

Returns:

  • (String)


226
227
228
229
230
231
232
233
234
235
# File 'lib/instana/util.rb', line 226

def id_to_header(id)
  unless id.is_a?(Integer) || id.is_a?(String)
    Instana.logger.debug "id_to_header received a #{id.class}: returning empty string"
    return String.new
  end
  [id.to_i].pack('q>').unpack('H*')[0].gsub(/^0+/, '')
rescue => e
  Instana.logger.info "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}"
  Instana.logger.debug { e.backtrace.join("\r\n") }
end

.method_alias(klass, method) ⇒ Object

An agnostic approach to method aliasing.

Parameters:

  • klass (Object)

    The class or module that holds the method to be alias’d.

  • method (Symbol)

    The name of the method to be aliased.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/instana/util.rb', line 11

def method_alias(klass, method)
  if klass.method_defined?(method.to_sym) ||
      klass.private_method_defined?(method.to_sym)

    with = "#{method}_with_instana"
    without = "#{method}_without_instana"

    klass.class_eval do
      alias_method without, method.to_s
      alias_method method.to_s, with
    end
  else
    ::Instana.logger.debug "No such method (#{method}) to alias on #{klass}"
  end
end

.now_in_msInteger Also known as: ts_now

Get the current time in milliseconds from the epoch

Returns:

  • (Integer)

    the current time in milliseconds



197
198
199
# File 'lib/instana/util.rb', line 197

def now_in_ms
  Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
end

.pry!Object

Debugging helper method



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

def pry!
  # Only valid for development or test environments
  #env = ENV['RACK_ENV'] || ENV['RAILS_ENV']
  #return unless %w(development, test).include? env
  require 'pry-byebug'

  if defined?(PryByebug)
    Pry.commands.alias_command 'c', 'continue'
    Pry.commands.alias_command 's', 'step'
    Pry.commands.alias_command 'n', 'next'
    Pry.commands.alias_command 'f', 'finish'

    Pry::Commands.command(/^$/, 'repeat last command') do
      _pry_.run_command Pry.history.to_a.last
    end
  end

  binding.pry
rescue LoadError
  ::Instana.logger.warn("No debugger in bundle.  Couldn't load pry-byebug.")
end

.send_extend(target_cls, cls) ⇒ Object

Calls on target_class to ‘extend’ cls

Parameters:

  • target_cls (Object)

    the class/module to do the ‘extending’

  • cls (Object)

    the class/module to be ‘extended’



32
33
34
# File 'lib/instana/util.rb', line 32

def send_extend(target_cls, cls)
  target_cls.send(:extend, cls) if defined?(target_cls)
end

.send_include(target_cls, cls) ⇒ Object

Calls on <target_cls> to include <cls> into itself.

Parameters:

  • target_cls (Object)

    the class/module to do the ‘including’

  • cls (Object)

    the class/module to be ‘included’



41
42
43
# File 'lib/instana/util.rb', line 41

def send_include(target_cls, cls)
  target_cls.send(:include, cls) if defined?(target_cls)
end

.take_snapshotObject

Method to collect up process info for snapshots. This is generally used once per process.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/instana/util.rb', line 87

def take_snapshot
  data = {}

  data[:sensorVersion] = ::Instana::VERSION
  data[:ruby_version] = RUBY_VERSION
  data[:rpl] = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)

  # Framework Detection
  if defined?(::RailsLts::VERSION)
    data[:framework] = "Rails on Rails LTS-#{::RailsLts::VERSION}"

  elsif defined?(::Rails.version)
    data[:framework] = "Ruby on Rails #{::Rails.version}"

  elsif defined?(::Grape::VERSION)
    data[:framework] = "Grape #{::Grape::VERSION}"

  elsif defined?(::Padrino::VERSION)
    data[:framework] = "Padrino #{::Padrino::VERSION}"

  elsif defined?(::Sinatra::VERSION)
    data[:framework] = "Sinatra #{::Sinatra::VERSION}"
  end

  # Report Bundle
  if defined?(::Gem) && Gem.respond_to?(:loaded_specs)
    data[:versions] = {}

    Gem.loaded_specs.each do |k, v|
      data[:versions][k] = v.version.to_s
    end
  end

  data
rescue => e
  ::Instana.logger.debug { "#{__method__}:#{File.basename(__FILE__)}:#{__LINE__}: #{e.message}" }
  ::Instana.logger.debug { e.backtrace.join("\r\n") }
  return data
end

.time_to_ms(time) ⇒ Object

Convert a Time value to milliseconds

Parameters:

  • time (Time)


207
208
209
# File 'lib/instana/util.rb', line 207

def time_to_ms(time)
  (time.to_f * 1000).floor
end