├── test ├── db │ └── .gitkeep ├── test.conf └── test_newrelic_redis.rb ├── .gitignore ├── lib ├── newrelic-redis.rb └── newrelic_redis │ ├── version.rb │ └── instrumentation.rb ├── Gemfile ├── Manifest.txt ├── .autotest ├── History.txt ├── README.md ├── Rakefile └── newrelic-redis.gemspec /test/db/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | -------------------------------------------------------------------------------- /lib/newrelic-redis.rb: -------------------------------------------------------------------------------- 1 | require 'newrelic_redis/instrumentation' 2 | -------------------------------------------------------------------------------- /lib/newrelic_redis/version.rb: -------------------------------------------------------------------------------- 1 | class NewRelicRedis 2 | VERSION = '2.0.2' 3 | end 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gemspec 5 | # gem "rails" 6 | -------------------------------------------------------------------------------- /test/test.conf: -------------------------------------------------------------------------------- 1 | dir ./test/db 2 | pidfile ./redis.pid 3 | port 6381 4 | unixsocket /tmp/redis.sock 5 | timeout 300 6 | loglevel debug 7 | logfile stdout 8 | databases 16 9 | daemonize yes 10 | -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | .autotest 2 | History.txt 3 | Manifest.txt 4 | README.md 5 | Rakefile 6 | lib/newrelic-redis.rb 7 | lib/newrelic_redis/instrumentation.rb 8 | lib/newrelic_redis/version.rb 9 | newrelic-redis.gemspec 10 | test/test.conf 11 | test/test_newrelic_redis.rb 12 | -------------------------------------------------------------------------------- /.autotest: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | 3 | require 'autotest/restart' 4 | 5 | # Autotest.add_hook :initialize do |at| 6 | # at.extra_files << "../some/external/dependency.rb" 7 | # 8 | # at.libs << ":../some/external" 9 | # 10 | # at.add_exception 'vendor' 11 | # 12 | # at.add_mapping(/dependency.rb/) do |f, _| 13 | # at.files_matching(/test_.*rb$/) 14 | # end 15 | # 16 | # %w(TestA TestB).each do |klass| 17 | # at.extra_class_map[klass] = "test/test_misc.rb" 18 | # end 19 | # end 20 | 21 | # Autotest.add_hook :run_command do |at| 22 | # system "rake build" 23 | # end 24 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 2.0.2 / 2015-05-07 2 | 3 | * 1 minor bugfix: 4 | * Actually fix now blowing up when obfuscating data 5 | 6 | === 2.0.1 / 2015-05-07 7 | 8 | * 1 minor bugfix: 9 | * Don't blow up obfuscating data when there is no data 10 | 11 | === 2.0.0 / 2015-03-18 12 | 13 | * 1 feature: 14 | * Switch to newer newrelic datastores format 15 | 16 | === 1.5.0 / 2014-08-25 17 | 18 | * 1 feature: 19 | * Respect the obfuscated configuration parameter for in redis 20 | 21 | === 1.4.0 / 2012-10-31 22 | 23 | HAPPY HALLOWEEN! 24 | 25 | * 1 minor bugfix: 26 | * Use DependencyDetection to load up properly 27 | 28 | === 1.3.2 / 2012-05-18 29 | 30 | * 1 minor bugfix: 31 | * Pass a block through properly for 3.0 32 | 33 | === 1.3.1 / 2012-05-17 34 | 35 | * 1 minor bugfix: 36 | * Bump the gem dependency to redis < 4.0 37 | 38 | === 1.3.0 / 2012-05-17 39 | 40 | * 2 minor feature: 41 | * Supports the redis gem version 3.0 42 | * Can be disabled via some environment flags 43 | 44 | === 1.0.0 / 2012-03-09 45 | 46 | * 1 major enhancement 47 | 48 | * Birthday! 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # newrelic-redis 2 | 3 | ## This gem is deprecated in favor of newrelic_rpm's built-in instrumentation 4 | 5 | If you're using `newrelic-redis`, we recommend removing it and updating your 6 | `newrelic_rpm` to any version `>= 3.18.1` 7 | 8 | ## LICENSE: 9 | 10 | (The MIT License) 11 | 12 | Copyright (c) 2012 Evan Phoenix 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining 15 | a copy of this software and associated documentation files (the 16 | 'Software'), to deal in the Software without restriction, including 17 | without limitation the rights to use, copy, modify, merge, publish, 18 | distribute, sublicense, and/or sell copies of the Software, and to 19 | permit persons to whom the Software is furnished to do so, subject to 20 | the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be 23 | included in all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 26 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 28 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 29 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 30 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 31 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | 3 | require 'rubygems' 4 | require 'hoe' 5 | 6 | REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__) 7 | REDIS_CNF = File.join(REDIS_DIR, "test.conf") 8 | REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid") 9 | 10 | task :default => :run 11 | 12 | desc "Run tests and manage server start/stop" 13 | task :run => [:start, :test, :stop] 14 | 15 | desc "Start the Redis server" 16 | task :start do 17 | redis_running = \ 18 | begin 19 | File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i) 20 | rescue Errno::ESRCH 21 | FileUtils.rm REDIS_PID 22 | false 23 | end 24 | 25 | unless redis_running 26 | unless system("which redis-server") 27 | STDERR.puts "redis-server not in PATH" 28 | exit 1 29 | end 30 | 31 | unless system("redis-server #{REDIS_CNF}") 32 | STDERR.puts "could not start redis-server" 33 | exit 1 34 | end 35 | end 36 | end 37 | 38 | desc "Stop the Redis server" 39 | task :stop do 40 | if File.exists?(REDIS_PID) 41 | Process.kill "INT", File.read(REDIS_PID).to_i 42 | FileUtils.rm REDIS_PID 43 | end 44 | end 45 | 46 | Hoe.plugin :bundler 47 | Hoe.plugin :gemspec 48 | Hoe.plugin :git 49 | 50 | HOE = Hoe.spec 'newrelic-redis' do 51 | developer 'Evan Phoenix', 'evan@phx.io' 52 | license "BSD" 53 | 54 | dependency "redis", "< 4.0" 55 | dependency "newrelic_rpm", "~> 3.11" 56 | 57 | self.readme_file = "README.md" 58 | end 59 | 60 | file "#{HOE.spec.name}.gemspec" => ['Rakefile'] do |t| 61 | puts "Generating #{t.name}" 62 | File.open(t.name, 'wb') { |f| f.write HOE.spec.to_ruby } 63 | end 64 | 65 | desc "Generate or update the standalone gemspec file for the project" 66 | task :gemspec => ["#{HOE.spec.name}.gemspec"] 67 | 68 | # vim: syntax=ruby 69 | -------------------------------------------------------------------------------- /newrelic-redis.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # stub: newrelic-redis 2.0.2 ruby lib 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "newrelic-redis" 6 | s.version = "2.0.2" 7 | 8 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 9 | s.require_paths = ["lib"] 10 | s.authors = ["Evan Phoenix"] 11 | s.date = "2015-04-08" 12 | s.description = "Redis instrumentation for Newrelic." 13 | s.email = ["evan@phx.io"] 14 | s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.md"] 15 | s.files = [".autotest", ".gemtest", "History.txt", "Manifest.txt", "README.md", "Rakefile", "lib/newrelic-redis.rb", "lib/newrelic_redis/instrumentation.rb", "lib/newrelic_redis/version.rb", "newrelic-redis.gemspec", "test/test.conf", "test/test_newrelic_redis.rb"] 16 | s.homepage = "http://github.com/evanphx/newrelic-redis" 17 | s.licenses = ["BSD"] 18 | s.rdoc_options = ["--main", "README.md"] 19 | s.rubygems_version = "2.2.2" 20 | s.summary = "Redis instrumentation for Newrelic." 21 | 22 | if s.respond_to? :specification_version then 23 | s.specification_version = 4 24 | 25 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 26 | s.add_runtime_dependency(%q, ["< 4.0"]) 27 | s.add_runtime_dependency(%q, ["~> 3.11"]) 28 | s.add_development_dependency(%q, ["~> 4.0"]) 29 | s.add_development_dependency(%q, ["~> 3.13"]) 30 | else 31 | s.add_dependency(%q, ["< 4.0"]) 32 | s.add_dependency(%q, ["~> 3.11"]) 33 | s.add_dependency(%q, ["~> 4.0"]) 34 | s.add_dependency(%q, ["~> 3.13"]) 35 | end 36 | else 37 | s.add_dependency(%q, ["< 4.0"]) 38 | s.add_dependency(%q, ["~> 3.11"]) 39 | s.add_dependency(%q, ["~> 4.0"]) 40 | s.add_dependency(%q, ["~> 3.13"]) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/newrelic_redis/instrumentation.rb: -------------------------------------------------------------------------------- 1 | require 'new_relic/agent/method_tracer' 2 | 3 | # Redis instrumentation. 4 | # Originally contributed by Ashley Martens of ngmoco 5 | # Rewritten, reorganized, and repackaged by Evan Phoenix 6 | 7 | DependencyDetection.defer do 8 | @name = :redis 9 | 10 | depends_on do 11 | defined?(::Redis) && 12 | !NewRelic::Control.instance['disable_redis'] && 13 | ENV['NEWRELIC_ENABLE'].to_s !~ /false|off|no/i 14 | end 15 | 16 | executes do 17 | NewRelic::Agent.logger.info 'Installing Redis Instrumentation' 18 | end 19 | 20 | executes do 21 | require 'new_relic/agent/datastores' 22 | 23 | ::Redis::Client.class_eval do 24 | # Support older versions of Redis::Client that used the method 25 | # +raw_call_command+. 26 | 27 | call_method = ::Redis::Client.new.respond_to?(:call) ? :call : :raw_call_command 28 | 29 | def call_with_newrelic_trace(*args, &blk) 30 | method_name = args[0].is_a?(Array) ? args[0][0] : args[0] 31 | callback = proc do |result, metric, elapsed| 32 | _send_to_new_relic(args, elapsed) 33 | end 34 | 35 | NewRelic::Agent::Datastores.wrap("Redis", method_name, nil, callback) do 36 | call_without_newrelic_trace(*args, &blk) 37 | end 38 | end 39 | 40 | alias_method :call_without_newrelic_trace, call_method 41 | alias_method call_method, :call_with_newrelic_trace 42 | 43 | # Older versions of Redis handle pipelining completely differently. 44 | # Don't bother supporting them for now. 45 | # 46 | if public_method_defined? :call_pipelined 47 | def call_pipelined_with_newrelic_trace(commands, *rest) 48 | # Report each command as a metric suffixed with _pipelined, so the 49 | # user can at least see what all the commands were. 50 | additional = commands.map do |c| 51 | name = c.kind_of?(Array) ? c[0] : c 52 | "Datastore/operation/Redis/#{name.to_s.downcase}_pipelined" 53 | end 54 | 55 | callback = proc do |result, metric, elapsed| 56 | _send_to_new_relic(commands, elapsed) 57 | additional.each do |additional_metric| 58 | NewRelic::Agent::MethodTracer.trace_execution_scoped(additional_metric) do 59 | # No-op, just getting them as placeholders in the trace tree 60 | end 61 | end 62 | end 63 | 64 | NewRelic::Agent::Datastores.wrap("Redis", "pipelined", nil, callback) do 65 | call_pipelined_without_newrelic_trace commands, *rest 66 | end 67 | end 68 | 69 | alias_method :call_pipelined_without_newrelic_trace, :call_pipelined 70 | alias_method :call_pipelined, :call_pipelined_with_newrelic_trace 71 | end 72 | 73 | def _send_to_new_relic(args, elapsed) 74 | if NewRelic::Control.instance["transaction_tracer.record_sql"] == "obfuscated" 75 | args.map! do |arg| 76 | if arg.empty? 77 | arg 78 | else 79 | [arg.first] + ["?"] * (arg.count - 1) 80 | end 81 | end 82 | end 83 | NewRelic::Agent::Datastores.notice_statement(args.inspect, elapsed) 84 | end 85 | end 86 | end 87 | end 88 | 89 | 90 | -------------------------------------------------------------------------------- /test/test_newrelic_redis.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'redis' 3 | 4 | require 'newrelic_rpm' 5 | require 'newrelic_redis/instrumentation' 6 | 7 | DependencyDetection.detect! 8 | 9 | NewRelic::Agent.require_test_helper 10 | 11 | class TestNewRelicRedis < Test::Unit::TestCase 12 | PORT = 6381 13 | OPTIONS = {:port => PORT, :db => 15, :timeout => 0.1} 14 | 15 | def setup 16 | NewRelic::Agent.drop_buffered_data 17 | 18 | @redis = Redis.new OPTIONS 19 | @client = @redis.client 20 | end 21 | 22 | def assert_metrics(*m) 23 | assert_metrics_recorded(m) 24 | end 25 | 26 | def assert_segment_has_key(segment_name, expected) 27 | sample = NewRelic::Agent.agent.transaction_sampler.tl_builder.sample 28 | segment = find_segment_with_name(sample, segment_name) 29 | assert_equal expected, segment.params[:statement] 30 | end 31 | 32 | def test_call 33 | with_config(:'transaction_tracer.record_sql' => 'raw') do 34 | in_transaction do 35 | @redis.hgetall "foo" 36 | assert_segment_has_key "Datastore/operation/Redis/select", "[[:select, 15]]" 37 | assert_segment_has_key "Datastore/operation/Redis/hgetall", "[[:hgetall, \"foo\"]]" 38 | end 39 | end 40 | 41 | assert_metrics "Datastore/all", 42 | "Datastore/allOther", 43 | "Datastore/Redis/all", 44 | "Datastore/Redis/allOther", 45 | "Datastore/operation/Redis/select", 46 | "Datastore/operation/Redis/hgetall" 47 | end 48 | 49 | def test_call_pipelined 50 | with_config(:'transaction_tracer.record_sql' => 'raw') do 51 | in_transaction do 52 | @redis.pipelined do 53 | @redis.hgetall "foo" 54 | @redis.incr "bar" 55 | end 56 | 57 | assert_segment_has_key "Datastore/operation/Redis/select", "[[:select, 15]]" 58 | assert_segment_has_key "Datastore/operation/Redis/pipelined", "[[:hgetall, \"foo\"], [:incr, \"bar\"]]" 59 | end 60 | end 61 | 62 | assert_metrics "Datastore/all", 63 | "Datastore/allOther", 64 | "Datastore/Redis/all", 65 | "Datastore/Redis/allOther", 66 | "Datastore/operation/Redis/select", 67 | "Datastore/operation/Redis/pipelined", 68 | "Datastore/operation/Redis/hgetall_pipelined", 69 | "Datastore/operation/Redis/incr_pipelined" 70 | end 71 | 72 | def test_call_with_block 73 | rep = nil 74 | 75 | @redis.client.call [:info] do |reply| 76 | rep = reply 77 | end 78 | 79 | if Redis::VERSION.split(".").first.to_i >= 3 80 | assert_kind_of String, rep 81 | else 82 | assert_nil rep 83 | end 84 | end 85 | 86 | def test_obfuscated 87 | with_config(:'transaction_tracer.record_sql' => 'obfuscated') do 88 | in_transaction do 89 | @redis.pipelined do 90 | @redis.hgetall "foo" 91 | @redis.incr "bar" 92 | end 93 | 94 | assert_segment_has_key "Datastore/operation/Redis/select", "[[:select, \"?\"]]" 95 | assert_segment_has_key "Datastore/operation/Redis/pipelined", "[[:hgetall, \"?\"], [:incr, \"?\"]]" 96 | end 97 | end 98 | 99 | assert_metrics "Datastore/all", 100 | "Datastore/allOther", 101 | "Datastore/Redis/all", 102 | "Datastore/Redis/allOther", 103 | "Datastore/operation/Redis/select", 104 | "Datastore/operation/Redis/pipelined", 105 | "Datastore/operation/Redis/hgetall_pipelined", 106 | "Datastore/operation/Redis/incr_pipelined" 107 | end 108 | end 109 | --------------------------------------------------------------------------------