haproxy2rpm 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Push haproxy logs to newrelic rpm
2
2
  This is useful for languages where newrelic does not provide an agent,
3
- such as erlang or node.js based applications
3
+ such as erlang or node.js based applications.
4
+
5
+ It runs in syslog mode or tail a log file.
4
6
 
5
7
  ## Installation
6
8
 
@@ -9,26 +11,22 @@ gem install haproxy2rpm or clone it
9
11
  * copy your newrelic.yml file to $HOME/.newrelic/newrelic.yml
10
12
  * or set $NRCONFIG to point to your newrelic.yml file
11
13
 
12
- ## Running it
13
- haproxy2rpm /path/to/logfile or ./bin/haproxy2rpm /path/to/log/file
14
-
15
- ## Analyzing it
16
-
17
- At the moment, it only works with custom views
14
+ Tell haproxy to log to syslog. e.g:
18
15
 
19
- <verbatim>
20
- <h3>Charts</h3>
21
- <table width='100%'>
22
- <tr>
23
- <td>line_chart {% line_chart value:'average_value' title:'Test' metric:'Custom/HAProxy/response_times' %}</td>
24
- </tr>
16
+ # /etc/haproxy/haproxy.cfg
17
+ global
18
+ log 127.0.0.1:3333 daemon
25
19
 
26
- </table>
27
- </verbatim/>
28
-
29
-
30
- ## Roadmap
31
-
32
- * daemonize option
33
- * syslog (udp maybe tcp) server with https://github.com/loggly/logporter and then point HaProxy to that port. Why touch the disk if we don't have to?
34
- * Figure out how to report rpms and response times so that they show up inside the newrelic application view and not only as a custom metric
20
+ ## Running it
21
+ haproxy2rpm /path/to/logfile
22
+ haproxy2rpm --syslog
23
+ haproxy2rpm --syslog --daemonize
24
+
25
+ ## Supported RPM features
26
+
27
+ * Response times in application server
28
+ * Error rate
29
+ * Queue time
30
+ * Apdex
31
+ * Histogram
32
+ * Web transactions
data/Rakefile CHANGED
@@ -11,4 +11,14 @@ namespace :test do
11
11
 
12
12
  t.verbose = true
13
13
  end
14
+
15
+ desc 'send udp log lines for testing purposes'
16
+ task :send_udp_log_lines do
17
+ port = ENV['port'] || 3333
18
+ status_code = ENV['status_code'] || 200
19
+ uri = ENV['uri'] || '/'
20
+ puts 'make sure the udp server is running'
21
+
22
+ `yes '<13>May 19 18:30:17 localhost haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 6559/100/7/147/6723 #{status_code} 243 - - ---- 1/3/5 0/0 "PUT #{uri} HTTP/1.0"' | nc -u -i 1 localhost #{port}`
23
+ end
14
24
  end
data/Research.md ADDED
@@ -0,0 +1,57 @@
1
+ # Agent#record_transaction
2
+ # lib/new_relic/agent.rb
3
+ # NewRelic::Agent.record_transaction( params['value'].to_f, params )
4
+ # lib/new_relic/rack.rb
5
+ # test/rpm_agent_test.rb
6
+ # NewRelic::Agent.record_transaction 0.5, 'uri' => "/users/create?foo=bar"
7
+
8
+ # NewRelic::Agent.record_transaction rand(100) / 100.0, {'metric' => 'Controller/josef'}
9
+ # stat = NewRelic::Agent.agent.stats_engine.get_stats_no_scope 'WebFrontend/QueueTime'
10
+ # stat.record_data_point(0.5)
11
+
12
+ module NewRelic
13
+ module Metrics
14
+ CONTROLLER = "Controller"
15
+ DISPATCHER = "HttpDispatcher"
16
+ ACTIVE_RECORD = "ActiveRecord"
17
+ USER_TIME = "CPU/User Time"
18
+ MEMORY = "Memory/Physical"
19
+ end
20
+ end
21
+
22
+ def record_transaction(duration_seconds, options={})
23
+ is_error = options['is_error'] || options['error_message'] ||
24
+ options['exception']
25
+ metric = options['metric']
26
+ metric ||= options['uri'] # normalize this with url rules
27
+ raise "metric or uri arguments required" unless metric
28
+ metric_info =
29
+ NewRelic::MetricParser::MetricParser.for_metric_named(metric)
30
+
31
+ if metric_info.is_web_transaction?
32
+ NewRelic::Agent::Instrumentation::MetricFrame.record_apdex(metric_info,
33
+ duration_seconds, duration_seconds, is_error)
34
+ histogram.process(duration_seconds)
35
+ end
36
+ metrics = metric_info.summary_metrics
37
+
38
+ metrics << metric
39
+ metrics.each do |name|
40
+ stats = stats_engine.get_stats_no_scope(name)
41
+ stats.record_data_point(duration_seconds)
42
+ end
43
+
44
+ if is_error
45
+ if options['exception']
46
+ e = options['exception']
47
+ elsif options['error_message']
48
+ e = Exception.new options['error_message']
49
+ else
50
+ e = Exception.new 'Unknown Error'
51
+ end
52
+ error_collector.notice_error e, :uri => options['uri'],
53
+ :metric => metric
54
+ end
55
+ # busy time ?
56
+ end
57
+
data/bin/haproxy2rpm CHANGED
@@ -10,23 +10,49 @@ NEW_RELIC_CONFIG_PATH = File.join(ENV['HOME'], '.newrelic', 'newrelic.yml')
10
10
  help = <<HELP
11
11
  haproxy2rpm
12
12
 
13
- Basic Command Line Usage:
14
- haproxy2rpm <haproxy log file to parse>
15
-
16
- Coniguration for newrelic is read from #{NEW_RELIC_CONFIG_PATH}
13
+ Coniguration for newrelic is read from #{NEW_RELIC_CONFIG_PATH}
17
14
  HELP
18
15
 
19
- options = { :daemonize => false, :version => false, :syslog => false }
16
+ options = {
17
+ :daemonize => false,
18
+ :version => false,
19
+ :syslog => false,
20
+ :port => 3333,
21
+ :listen => '0.0.0.0'
22
+ }
20
23
 
21
24
  opts = OptionParser.new do |opts|
22
- opts.banner = help
25
+ # opts.banner = help
23
26
  opts.on("-D", "--daemonize", "Daemonize") do
24
27
  options[:daemonize] = true
25
28
  end
26
29
 
27
30
  opts.on("-s", "--syslog", "Run syslog server") do
28
31
  options[:syslog] = true
29
- end
32
+ end
33
+
34
+ opts.on("-lIP", "--listen IP", "Set IP to listen on") do |ip|
35
+ options[:listen] = ip
36
+ end
37
+
38
+ opts.on("-pPORT", "--port PORT", "Set port to listen on") do |port|
39
+ options[:port] = port
40
+ end
41
+
42
+ opts.on("-nAPP_NAME", "--app_name APP_NAME", "Set application name") do |app_name|
43
+ options[:app_name] = app_name
44
+ end
45
+
46
+ opts.on("-eENVIRONMENT", "--environment ENVIRONMENT", "Set Newrelic agent env") do |env|
47
+ options[:env] = env
48
+ end
49
+
50
+ opts.on_tail("-h", "--help", "Show this message") do
51
+ puts opts.banner
52
+ puts opts
53
+ exit
54
+ end
55
+
30
56
  opts.on("-v", "--version", "Print the version number and exit") do
31
57
  options[:version] = true
32
58
  end
@@ -47,6 +73,7 @@ unless options[:syslog] || File.exists?(log_file)
47
73
  puts 'please proivde a valid path to a haproxy log file'
48
74
  puts ''
49
75
  puts help
76
+ puts opts
50
77
  exit(1)
51
78
  end
52
79
 
data/lib/haproxy2rpm.rb CHANGED
@@ -11,7 +11,7 @@ require "haproxy2rpm/rpm"
11
11
 
12
12
  module Haproxy2Rpm
13
13
  def self.run(log_file, options)
14
- @rpm = Rpm.new
14
+ @rpm = Rpm.new(options)
15
15
  if(options[:daemonize])
16
16
  puts 'daemonizing'
17
17
  run_daemonized(log_file, options)
@@ -28,14 +28,13 @@ module Haproxy2Rpm
28
28
  puts 'stopping new relic agent'
29
29
  NewRelic::Agent.shutdown
30
30
  end
31
-
31
+
32
32
  def self.run_syslog_server(options)
33
- NewRelic::Agent.manual_start
34
33
  EventMachine::run do
35
- EventMachine.start_server("0.0.0.0", 3333, SyslogHandler)
36
- end
34
+ EventMachine::open_datagram_socket(options[:listen], options[:port], SyslogHandler)
35
+ end
37
36
  end
38
-
37
+
39
38
  def self.default_run(log_file,options)
40
39
  EventMachine.run do
41
40
  EventMachine::file_tail(log_file) do |filetail, line|
@@ -69,4 +68,4 @@ module Haproxy2Rpm
69
68
 
70
69
  exit
71
70
  end
72
- end
71
+ end
@@ -36,7 +36,19 @@ module Haproxy2Rpm
36
36
  end
37
37
 
38
38
  def uri
39
- @uri ||= @parts[14]
39
+ @uri ||= URI.parse(@parts[14])
40
+ end
41
+
42
+ def http_path
43
+ uri.path
44
+ end
45
+
46
+ def http_query
47
+ uri.query
48
+ end
49
+
50
+ def is_error?
51
+ status_code >= 500
40
52
  end
41
53
 
42
54
  private
@@ -1,13 +1,23 @@
1
1
  module Haproxy2Rpm
2
2
  class Rpm
3
- def initialize()
4
- NewRelic::Agent.manual_start
3
+ def initialize(options = {})
4
+ agent_options = {}
5
+ agent_options[:app_name] = options[:app_name] if options[:app_name]
6
+ agent_options[:env] = options[:env] if options[:env]
7
+ NewRelic::Agent.manual_start agent_options
5
8
  @stats_engine = NewRelic::Agent.agent.stats_engine
9
+ @qt_stat = @stats_engine.get_stats_no_scope('WebFrontend/QueueTime')
6
10
  end
7
-
11
+
8
12
  def send(line)
9
13
  request = LineParser.new(line)
10
- @stats_engine.get_stats('Custom/HAProxy/response_times',false).record_data_point(request.tr)
14
+ NewRelic::Agent.record_transaction(
15
+ request.tr / 1000.0,
16
+ 'metric' => "Controller#{request.path}",
17
+ 'is_error' => request.is_error?,
18
+ 'error_message' => "#{request.uri} : Status code #{request.status_code}"
19
+ )
20
+ @qt_stat.record_data_point(request.tw / 1000.0)
11
21
  end
12
22
  end
13
- end
23
+ end
@@ -29,22 +29,12 @@ module Haproxy2Rpm
29
29
  end
30
30
 
31
31
  @syslog3164_re = Regexp.new(re)
32
- end # def initialize
32
+ end
33
33
 
34
34
  def receive_data(data)
35
- # In jruby+netty, we probaby should use the DelimiterBasedFrameDecoder
36
- # But for the sake of simplicity, we'll use EM's BufferedTokenizer for
37
- # all implementations.
38
- @buffer.extract(data).each do |line|
39
- receive_line(line.chomp)
40
- end
41
- end # def receive_event
42
-
43
- def receive_line(line)
44
- @count += 1
45
- # Just try matching, don't need to do anything with it for this benchmark.
46
- m = @syslog3164_re.match(line)
35
+ puts data
36
+ m = @syslog3164_re.match(data)
47
37
  @rpm.send(m[:message])
48
38
  end
49
39
  end
50
- end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module Haproxy2Rpm
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -2,11 +2,28 @@ $LOAD_PATH.unshift( File.dirname(__FILE__) )
2
2
 
3
3
  require 'test_helper'
4
4
 
5
+ def log_entry(options = {})
6
+ defaults = {
7
+ :tq => 6559,
8
+ :tw => 100,
9
+ :tc => 7,
10
+ :tr => 147,
11
+ :tt => 6723,
12
+ :status_code => 200,
13
+ :http_path => '/',
14
+ :http_method => 'GET',
15
+ :http_query => 'example_param=test',
16
+ }
17
+ defaults.merge!(options)
18
+ log_line = <<LOG_LINE
19
+ haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 #{defaults[:tq]}/#{defaults[:tw]}/#{defaults[:tc]}/#{defaults[:tr]}/#{defaults[:tt]} #{defaults[:status_code]} 243 - - ---- 1/3/5 0/0 "#{defaults[:http_method]} #{defaults[:http_path]}#{defaults[:http_query] ? "?#{defaults[:http_query]}" : ''} HTTP/1.0"
20
+ LOG_LINE
21
+ end
22
+
5
23
  class Haproxy2RpmTest < Test::Unit::TestCase
6
24
  context 'parsing of haproxy files' do
7
25
  setup do
8
- @line = File.open(File.join(File.dirname(__FILE__), 'fixtures', 'haproxy.log')){|f| f.readlines}.first
9
- @result = Haproxy2Rpm::LineParser.new(@line)
26
+ @result = Haproxy2Rpm::LineParser.new(log_entry)
10
27
  end
11
28
 
12
29
  should 'parse the Tq (total time in ms spent waiting for client)' do
@@ -15,7 +32,7 @@ class Haproxy2RpmTest < Test::Unit::TestCase
15
32
 
16
33
  # this is the time waiting in the global queue
17
34
  should 'parse the Tw (total time in ms spent waiting in queue)' do
18
- assert_equal 0, @result.tw
35
+ assert_equal 100, @result.tw
19
36
  end
20
37
 
21
38
  should 'parse the Tc (total time in ms spent waiting for the connection to the final server' do
@@ -36,11 +53,34 @@ class Haproxy2RpmTest < Test::Unit::TestCase
36
53
  end
37
54
 
38
55
  should 'parse the http method' do
39
- assert_equal 'HEAD', @result.http_method
56
+ assert_equal 'GET', @result.http_method
40
57
  end
41
58
 
42
59
  should 'parse the uri' do
43
- assert_equal '/', @result.uri
60
+ assert_equal '/', @result.http_path
61
+ end
62
+
63
+ should 'parse the GET params' do
64
+ assert_equal 'example_param=test', @result.http_query
65
+ end
66
+ end
67
+
68
+ context 'is_error' do
69
+ should 'return false for redirects' do
70
+ assert !Haproxy2Rpm::LineParser.new(log_entry(:status_code => 302)).is_error?
44
71
  end
72
+
73
+ should 'return false for 200 OK' do
74
+ assert !Haproxy2Rpm::LineParser.new(log_entry(:status_code => 200)).is_error?
75
+ end
76
+
77
+ should 'return false for 404' do
78
+ assert !Haproxy2Rpm::LineParser.new(log_entry(:status_code => 404)).is_error?
79
+ end
80
+
81
+ should 'return for 500 and above' do
82
+ assert Haproxy2Rpm::LineParser.new(log_entry(:status_code => 500)).is_error?
83
+ end
84
+
45
85
  end
46
86
  end
metadata CHANGED
@@ -1,76 +1,74 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: haproxy2rpm
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
4
5
  prerelease:
5
- version: 0.0.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Patrick Huesler
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-07-09 00:00:00 +02:00
12
+ date: 2011-07-11 00:00:00.000000000 +02:00
14
13
  default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
17
16
  name: newrelic_rpm
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2153806900 !ruby/object:Gem::Requirement
20
18
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
25
23
  type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: eventmachine-tail
29
24
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2153806900
26
+ - !ruby/object:Gem::Dependency
27
+ name: eventmachine-tail
28
+ requirement: &2153806480 !ruby/object:Gem::Requirement
31
29
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: "0"
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
36
34
  type: :runtime
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: rake
40
35
  prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
36
+ version_requirements: *2153806480
37
+ - !ruby/object:Gem::Dependency
38
+ name: rake
39
+ requirement: &2153806060 !ruby/object:Gem::Requirement
42
40
  none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: "0"
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
47
45
  type: :development
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: shoulda-context
51
46
  prerelease: false
52
- requirement: &id004 !ruby/object:Gem::Requirement
47
+ version_requirements: *2153806060
48
+ - !ruby/object:Gem::Dependency
49
+ name: shoulda-context
50
+ requirement: &2153805640 !ruby/object:Gem::Requirement
53
51
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: "0"
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
58
56
  type: :development
59
- version_requirements: *id004
57
+ prerelease: false
58
+ version_requirements: *2153805640
60
59
  description: Sending haproxy logs to new relic rpm
61
- email:
60
+ email:
62
61
  - patrick.huesler@gmail.com
63
- executables:
62
+ executables:
64
63
  - haproxy2rpm
65
64
  extensions: []
66
-
67
65
  extra_rdoc_files: []
68
-
69
- files:
66
+ files:
70
67
  - .gitignore
71
68
  - Gemfile
72
69
  - README.md
73
70
  - Rakefile
71
+ - Research.md
74
72
  - bin/haproxy2rpm
75
73
  - haproxy2rpm.gemspec
76
74
  - lib/haproxy2rpm.rb
@@ -79,38 +77,33 @@ files:
79
77
  - lib/haproxy2rpm/rpm.rb
80
78
  - lib/haproxy2rpm/syslog.rb
81
79
  - lib/haproxy2rpm/version.rb
82
- - test/fixtures/haproxy.log
83
80
  - test/haproxy2pm_test.rb
84
81
  - test/test_helper.rb
85
82
  has_rdoc: true
86
- homepage: ""
83
+ homepage: ''
87
84
  licenses: []
88
-
89
85
  post_install_message:
90
86
  rdoc_options: []
91
-
92
- require_paths:
87
+ require_paths:
93
88
  - lib
94
- required_ruby_version: !ruby/object:Gem::Requirement
89
+ required_ruby_version: !ruby/object:Gem::Requirement
95
90
  none: false
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: "0"
100
- required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
96
  none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: "0"
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
106
101
  requirements: []
107
-
108
102
  rubyforge_project:
109
103
  rubygems_version: 1.6.2
110
104
  signing_key:
111
105
  specification_version: 3
112
106
  summary: Sending haproxy logs to new relic rpm
113
- test_files:
114
- - test/fixtures/haproxy.log
107
+ test_files:
115
108
  - test/haproxy2pm_test.rb
116
109
  - test/test_helper.rb
@@ -1 +0,0 @@
1
- haproxy[674]: 127.0.0.1:33319 [15/Oct/2003:08:31:57] relais-http Srv1 6559/0/7/147/6723 200 243 - - ---- 1/3/5 0/0 "HEAD / HTTP/1.0"