# 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