# encoding: utf-8 # This file is distributed under Ting Yun's license terms. require 'ting_yun/configuration/manager' require 'ting_yun/logger/startup_logger' require 'ting_yun/frameworks' require 'ting_yun/agent/transaction/transaction_state' require 'ting_yun/agent/transaction' require 'ting_yun/agent/collector/middle_ware_collector/middle_ware' module TingYun module Agent extend self @agent = nil @logger = nil @config = ::TingYun::Configuration::Manager.new attr_reader :config UNKNOWN_METRIC = '(unknown)'.freeze def agent return @agent if @agent TingYun::Agent.logger.warn("Agent unavailable as it hasn't been started.") nil end alias instance agent def agent=(new_instance) @agent = new_instance end def logger @logger || ::TingYun::Logger::StartupLogger.instance end def logger=(log) @logger = log end def reset_config @config.reset_to_defaults end # Record a value for the given metric name. # # This method should be used to record event-based metrics such as method # calls that are associated with a specific duration or magnitude. # # +metric_name+ should follow a slash separated path convention. Application # specific metrics should begin with "Custom/". # # +value+ should be either a single Numeric value representing the duration/ # magnitude of the event being recorded, or a Hash containing :count, # :total, :min, :max, and :sum_of_squares keys. The latter form is useful # for recording pre-aggregated metrics collected externally. # # This method is safe to use from any thread. # # @api public def record_metric(metric_name, value) #THREAD_LOCAL_ACCESS return unless agent stats = TingYun::Metrics::Stats.create_from_hash(value) if value.is_a?(Hash) agent.stats_engine.tl_record_unscoped_metrics(metric_name, stats || value) end # Manual agent configuration and startup/shutdown # Call this to manually start the Agent in situations where the Agent does # not auto-start. # # When the app environment loads, so does the Agent. However, the # Agent will only connect to the service if a web front-end is found. If # you want to selectively monitor ruby processes that don't use # web plugins, then call this method in your code and the Agent # will fire up and start reporting to the service. # # Options are passed in as overrides for values in the # tingyun.yml, such as app_name. In addition, the option +log+ # will take a logger that will be used instead of the standard # file logger. The setting for the tingyun.yml section to use # (ie, RAILS_ENV) can be overridden with an :env argument. # # @api public # def manual_start(options={}) raise "Options must be a hash" unless Hash === options TingYun::Frameworks.init_start({ :'nbs.agent_enabled' => true, :sync_startup => true }.merge(options)) end # Yield to a block that is run with a database metric name context. This means # the Database instrumentation will use this for the metric name if it does not # otherwise know about a model. This is re-entrant. # # @param [String,Class,#to_s] model the DB model class # # @param [String] method the name of the finder method or other method to # identify the operation with. # def with_database_metric_name(model, method = nil, product = nil, &block) #THREAD_LOCAL_ACCESS if txn = TingYun::Agent::TransactionState.tl_get.current_transaction txn.with_database_metric_name(model, method, product, &block) else yield end end # Notice the error with the given available options: # # * :uri => Request path, minus request params or query string # * :metric => The metric name associated with the transaction # * :custom_params => Custom parameters # # @api public # def notice_error(exception, options={}) TingYun::Agent::Transaction.notice_error(exception, options) nil # don't return a noticed error datastructure. it can only hurt. end # Register this method as a callback for processes that fork # jobs. # # If the master/parent connects to the agent prior to forking the # agent in the forked process will use that agent_run. Otherwise # the forked process will establish a new connection with the # server. # # Use this especially when you fork the process to run background # jobs or other work. If you are doing this with a web dispatcher # that forks worker processes then you will need to force the # agent to reconnect, which it won't do by default. Passenger and # Rainbows and Unicorn are already handled, nothing special needed for them. # # Options: # * :force_reconnect => true to force the spawned process to # establish a new connection, such as when forking a long running process. # The default is false--it will only connect to the server if the parent # had not connected. # * :keep_retrying => false if we try to initiate a new # connection, this tells me to only try it once so this method returns # quickly if there is some kind of latency with the server. # # @api public # def after_fork(options={}) agent.after_fork(options) if agent end # Yield to the block without collecting any metrics or traces in # any of the subsequent calls. If executed recursively, will keep # track of the first entry point and turn on tracing again after # leaving that block. This uses the thread local TransactionState. # # @api public # def disable_all_tracing return yield unless agent begin agent.push_trace_execution_flag(false) yield ensure agent.pop_trace_execution_flag end end # Shutdown the agent. Call this before exiting. Sends any queued data # and kills the background thread. # # @param options [Hash] Unused options Hash, for back compatibility only # # @api public # def shutdown agent.shutdown if agent end end end