# This file is distributed under New Relic’s license terms. # See github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. # frozen_string_literal: true
require_relative ‘../../new_relic/language_support’ require ‘thor’
class Instrumentation < Thor
include Thor::Actions INSTRUMENTATION_ROOT = 'lib/new_relic/agent/instrumentation/' MULTIVERSE_SUITE_ROOT = 'test/multiverse/suites/' DEFAULT_SOURCE_LOCATION = 'lib/new_relic/agent/configuration/default_source.rb' NEWRELIC_YML_LOCATION = 'newrelic.yml' desc('scaffold NAME', 'Scaffold the required files for adding new instrumentation') long_desc <<~LONGDESC `instrumentation scaffold` requires one parameter by default: the name of the library or class you are instrumenting. This task generates the basic file structure needed to add new instrumentation to the Ruby agent. LONGDESC source_root(File.dirname(__FILE__)) option :method, default: 'method_to_instrument', desc: 'The method you would like to prepend or chain instrumentation onto' option :args, default: '*args', desc: 'The arguments associated with the original method' def scaffold(name) @name = name @snake_name = snake_name(@name) @method = options[:method] if options[:method] @args = options[:args] if options[:args] @class_name = ::NewRelic::LanguageSupport.camelize(name) base_path = "#{INSTRUMENTATION_ROOT}#{@snake_name}" empty_directory(base_path) create_instrumentation_files(base_path) append_to_default_source(@name, @snake_name) # append_to_newrelic_yml(@name, @snake_name) # This is now done on release, we don't need it anymore, but leaving it to be sure. create_tests(name) end desc 'add_new_method NAME', 'Inserts a new method into an existing piece of instrumentation' option :method, required: true, desc: 'The name of the method to instrument' option :args, default: '*args', desc: 'The arguments associated with the instrumented method' def add_new_method(name, method_name) # Verify that existing instrumentation exists # if it doesn't, should we just call the #scaffold method instead since we have all the stuff # otherwise, inject the new method into the instrumentation matching the first arg # add to only chain, instrumentation, prepend # move the method content to a partial end private def create_instrumentation_files(base_path) %w[chain instrumentation prepend].each do |file| template("templates/#{file}.tt", "#{base_path}/#{file}.rb") end template('templates/dependency_detection.tt', "#{base_path}.rb") end def create_tests(name) @name = name @instrumentation_method_global_erb_snippet = '<%= $instrumentation_method %>' @snake_name = snake_name(@name) base_path = "#{MULTIVERSE_SUITE_ROOT}#{@snake_name}" empty_directory(base_path) template('templates/Envfile.tt', "#{base_path}/Envfile") template('templates/test.tt', "#{base_path}/#{@snake_name}_instrumentation_test.rb") empty_directory("#{base_path}/config") template('templates/newrelic.yml.tt', "#{base_path}/config/newrelic.yml") end def append_to_default_source(name, snake_name) insert_into_file( DEFAULT_SOURCE_LOCATION, config_block(name, snake_name), after: ":description => 'Controls auto-instrumentation of bunny at start-up. May be one of: `auto`, `prepend`, `chain`, `disabled`.' },\n" ) end def append_to_newrelic_yml(name, snake_name) insert_into_file( NEWRELIC_YML_LOCATION, yaml_block(name, snake_name), after: "# instrumentation.bunny: auto\n" ) end def config_block(name, snake_name) # Don't change to <<~ # We want to preserve the whitespace so the config is correctly indented <<-CONFIG :'instrumentation.#{snake_name}' => { :default => 'auto', :documentation_default => 'auto', :public => true, :type => String, :dynamic_name => true, :allowed_from_server => false, :description => 'Controls auto-instrumentation of the #{name} library at start-up. May be one of `auto`, `prepend`, `chain`, `disabled`.' }, CONFIG end def yaml_block(name, snake_name) <<~HEREDOC # Controls auto-instrumentation of #{name} at start-up. # May be one of [auto|prepend|chain|disabled] # instrumentation.#{snake_name}: auto HEREDOC end def snake_name(name) name.downcase.tr('-', '_') end
end
Instrumentation.start(ARGV)