logworm_client_amqp 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,81 @@
1
+ v0.8.0
2
+ Dropped http for amqp for high scalability and reduce latency
3
+
4
+ v0.7.2
5
+ Renamed Rails and Rack configuration commands:
6
+ lw_disable_request_logging ==> donot_log_requests
7
+ lw_log_request_headers ==> log_headers
8
+ lw_enable_dev_logging ==> log_in_development
9
+
10
+ Renamed :headers field in web_log to :request_headers
11
+
12
+ v0.7.1
13
+ lw-tail and lw-compute now receive an optional app parameter, for the cases where you want to call command-line tools from a directory other than the app's directory... or when you have more than one Heroku remote/app from the same directory
14
+
15
+ v0.7.0
16
+ Eliminated long logs. Instead, there's now a parameter (log_request_headers in Rack, lw_log_request_headers in Rails) to add
17
+ request and response headers to the web_log.
18
+ Cleaned up fields in the web_log table
19
+
20
+ Added option to turn off automatic logging of requests (disable_request_logging in Rack, lw_disable_request_logging in Rails)
21
+ Added option to turn on logging even if in development mode (enable_dev_logging in Rack, lw_enable_dev_logging in Rails)
22
+
23
+ Honor filter_parameter_logging switch when used with Rails
24
+
25
+ Cleaned up log and flush
26
+ Cleaned up display of elapsed time for flush
27
+ Cleaned up call from Rack and Rails, and enforce timeout (1 sec by default)
28
+ Added unit tests
29
+
30
+ v0.6.2
31
+ Added list_tables command
32
+
33
+ v0.6.1
34
+ Added a bit of documentation
35
+
36
+ v0.6.0
37
+ Add support for querying API: lw_query
38
+
39
+ v0.5.6
40
+ log HEROKU_QUEUE as an integer value
41
+
42
+ v0.5.5
43
+ Added default logging of HEROKU_QUEUE
44
+
45
+ v0.5.4
46
+ Removed obsolete lw-heroku application
47
+
48
+ v0.5.3
49
+ Show nicer error message with tail and compute if the app is not properly configured.
50
+
51
+ v0.5.2
52
+ Fixes minor issues with in Rails logging
53
+
54
+ v0.5.1
55
+ Fixes minor issues with logging statements sent to console
56
+
57
+ v0.5.0
58
+ Supports new base logworm gem, which can work as a Heroku addon
59
+ Configuration parameters now stored as URL
60
+ No error if logworm cannot be properly configured -- lw_log simply ignored
61
+ No logging in development mode
62
+
63
+ v0.4.1
64
+ Show in console time spent communicating with the server.
65
+
66
+ v0.4.0 Added support for running on Rails!
67
+ To use, add config.gem ‘logworm_client’ to environment.rb
68
+ Then you can configure in ApplicationController, via logs_requests [:short | :long]+
69
+ Works from views, models, or controllers.
70
+
71
+ v0.3.2 added lw_log method --fixed small issue
72
+
73
+ v0.3.1 added lw_log method
74
+
75
+ v0.3.0 ensure gem and dependencies live in gemcutter
76
+
77
+ v0.2.0 log short requests by default in the rack
78
+
79
+ v0.1.1 minor changes
80
+
81
+ v0.1.0 initial version.
data/Manifest ADDED
@@ -0,0 +1,18 @@
1
+ CHANGELOG
2
+ Manifest
3
+ README.md
4
+ Rakefile
5
+ bin/lw-compute
6
+ bin/lw-tail
7
+ lib/logworm_client/logger.rb
8
+ lib/logworm_client/rack.rb
9
+ lib/logworm_client/rails.rb
10
+ lib/logworm_client_amqp.rb
11
+ lib/logworm_utils.rb
12
+ lib/logworm_utils/compute.rb
13
+ lib/logworm_utils/tail.rb
14
+ spec/logger_spec.rb
15
+ spec/rack_spec.rb
16
+ spec/rails_spec.rb
17
+ spec/spec.opts
18
+ spec/spec_helper.rb
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ logworm client gem
2
+ ------------------
3
+
4
+ Rails Usage
5
+ -----------
6
+
7
+ Edit environment.rb and add
8
+
9
+ <pre><code>config.gem 'logworm_client'</pre></code>
10
+
11
+ inside the <code>Rails::Initializer.run</code> block
12
+
13
+ See http://www.logworm.com/docs/setup
14
+
15
+ Rack Usage
16
+ ----------
17
+
18
+ <p>Edit your config.ru file and indicate that you want to use logworm's Rack middleware. For example, for a Sinatra application you'd have:</p>
19
+
20
+ <code><pre>require "myapp"
21
+ require "logworm_client"
22
+ use Logworm::Rack
23
+ run Sinatra::Application
24
+ </pre></code>
25
+
26
+ See http://www.logworm.com/docs/setup
27
+
28
+ Configuration Options
29
+ ---------------------
30
+
31
+ See http://www.logworm.com/docs/config
32
+
33
+ Command-Line tools
34
+ ------------------
35
+
36
+ lw-tail
37
+ -------
38
+
39
+ See http://www.logworm.com/docs/tail
40
+
41
+ lw-compute
42
+ ----------
43
+
44
+ See http://www.logworm.com/docs/compute
45
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'echoe'
2
+ Echoe.new('logworm_client_amqp', '0.8.0') do |p|
3
+ p.description = "logworm client utilities"
4
+ p.url = "http://www.logworm.com"
5
+ p.author = "Pomelo, LLC"
6
+ p.email = "schapira@pomelollc.com"
7
+ p.ignore_pattern = ["tmp/*", "script/*"]
8
+ p.development_dependencies = ["logworm_amqp >=0.8.0", "json"]
9
+ p.runtime_dependencies = ["logworm_amqp >=0.8.0", "json"]
10
+ end
data/bin/lw-compute ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'logworm_utils'
5
+ require 'optparse'
6
+
7
+ options = {
8
+ :aggregate_group => [],
9
+ :conditions => [],
10
+ :start => nil,
11
+ :end => nil,
12
+ :debug => false
13
+ }
14
+
15
+ option_parser = OptionParser.new do |opts|
16
+ opts.banner = "Usage: #{$0} [options] <log table> <function> <field>"
17
+
18
+ opts.on("--app app", String, "Specify the Heroku app") do |app|
19
+ options[:app] = app.strip
20
+ end
21
+
22
+ opts.on("-g group", String, "Specify an aggregation group (e.g, hour(_ts_utc), or response.code)") do |k|
23
+ options[:aggregate_group] = k
24
+ end
25
+
26
+ opts.on("-c condition", String, "Specify a condition to match. May be used multiple times") do |c|
27
+ options[:conditions] << c.strip
28
+ end
29
+
30
+ opts.on("-s starttime", String, "Specify the start time for the query") do |c|
31
+ options[:start] = c.strip
32
+ end
33
+
34
+ opts.on("-e endtime", String, "Specify the end time for the query") do |c|
35
+ options[:end] = c.strip
36
+ end
37
+
38
+ opts.on("-v", "Show debug information") do
39
+ options[:debug] = true
40
+ end
41
+
42
+ opts.on( '-h', '--help', 'Display this screen' ) do
43
+ puts option_parser.help
44
+ exit(1)
45
+ end
46
+
47
+ end
48
+
49
+ # Parse, and get the required <table>
50
+ option_parser.parse!
51
+ if ARGV.size == 3
52
+ table = ARGV[0].strip
53
+ function = ARGV[1].strip
54
+ field = ARGV[2].strip
55
+ elsif ARGV.size == 2
56
+ table = ARGV[0].strip
57
+ function = ARGV[1].strip
58
+ else
59
+ puts option_parser.help
60
+ exit(1)
61
+ end
62
+
63
+ # and run
64
+ LogwormCompute.new(table, function, field, options).run
65
+
66
+
data/bin/lw-tail ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'logworm_utils'
5
+ require 'optparse'
6
+
7
+ def help(option_parser, options)
8
+ puts option_parser.help
9
+ puts
10
+ LogwormTail.list(options)
11
+ end
12
+
13
+ options = {
14
+ :limit => 200,
15
+ :loop => false,
16
+ :frequency => 10,
17
+ :fields => [],
18
+ :conditions => [],
19
+ :start => nil,
20
+ :end => nil,
21
+ :debug => false,
22
+ :fkat => false
23
+ }
24
+
25
+ option_parser = OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{$0} [options] <log table>"
27
+
28
+ opts.on("--app app", String, "Specify the Heroku app") do |app|
29
+ options[:app] = app.strip
30
+ end
31
+
32
+ opts.on("-f [secs]", Integer,
33
+ "Continuously check for more data, every [secs] seconds.", "Default: #{options[:frequency]}") do |s|
34
+ options[:loop] = true
35
+ options[:frequency] = s if s
36
+ end
37
+
38
+ opts.on("-r limit", Integer, "Specify how many log entries to fetch.", "Default: #{options[:limit]}") do |n|
39
+ options[:limit] = n
40
+ end
41
+
42
+ opts.on("-k fields", String, "Specify a comma-separated list of fields to retrieve.") do |k|
43
+ options[:fields] = k.split(",").map {|f| f.strip}
44
+ end
45
+
46
+ opts.on("-c condition", String, "Specify a condition to match. May be used multiple times") do |c|
47
+ options[:conditions] << c.strip
48
+ end
49
+
50
+ opts.on("-s starttime", String, "Specify the start time for the query") do |c|
51
+ options[:start] = c.strip
52
+ end
53
+
54
+ opts.on("-e endtime", String, "Specify the end time for the query") do |c|
55
+ options[:end] = c.strip
56
+ end
57
+
58
+ opts.on("--flat", "Do not expand log entries when printing them") do
59
+ options[:flat] = true
60
+ end
61
+
62
+ opts.on("-v", "Show debug information") do
63
+ options[:debug] = true
64
+ end
65
+
66
+ opts.on( '-h', '--help', 'Display this screen' ) do
67
+ options[:help] = true
68
+ end
69
+
70
+ end
71
+
72
+ # Parse, and get the required <table>
73
+ option_parser.parse!
74
+ table = ARGV.pop
75
+ if !table or options[:help]
76
+ help option_parser, options
77
+ exit(1)
78
+ end
79
+ table.strip!
80
+
81
+ # and run
82
+ t = LogwormTail.new(table, options).run
83
+
84
+
@@ -0,0 +1,93 @@
1
+ require 'json'
2
+
3
+ module Logworm
4
+ class LogwormException < Exception ; end
5
+
6
+ class Logger
7
+ $lr_queue = []
8
+
9
+ ###
10
+ # Use connection to the backend servers specified in environment variable or config file
11
+ ###
12
+ def self.use_default_db
13
+ $lw_server = DB.from_config
14
+ end
15
+
16
+ ###
17
+ # Use a connection to a manually specified server
18
+ ###
19
+ def self.use_db(db)
20
+ $lw_server = db
21
+ end
22
+
23
+ ###
24
+ # Returns a reference to the current backend server
25
+ ###
26
+ def self.db
27
+ $lw_server
28
+ end
29
+
30
+
31
+ ###
32
+ # Starts a new cycle: sets a request_id variable, so that all entries (until flush) share that id
33
+ ###
34
+ def self.start_cycle
35
+ $request_id = "#{Thread.current.object_id}-#{(Time.now.utc.to_f * 1000).to_i}"
36
+ end
37
+
38
+ ###
39
+ # Record an entry. Not sent to the servers until 'flush' is called
40
+ #
41
+ # Warning: may raise Exception if there's a problem. It's up to the called to rescue from it in order to continue the processing
42
+ # of a web request, for example.
43
+ ###
44
+ def self.log(table, values = {})
45
+ return unless table and (table.is_a? String or table.is_a? Symbol)
46
+ return unless values.is_a? Hash
47
+
48
+ # Turn keys into symbols, delete empty ones, rename conflicting ones
49
+ kvalues = values.delete_if {|k,v| k.to_s == ""}.map {|k,v| [k.to_sym, v]}
50
+ kvalues = Hash[*kvalues.flatten]
51
+ [:_ts, :_ts_utc, :_request_id].each do |k|
52
+ kvalues["orig_#{k}".to_sym] = kvalues[k] if kvalues.has_key? k
53
+ end
54
+
55
+ # Add information
56
+ ts = Time.now.utc
57
+ kvalues[:_ts_utc] = (ts.to_f * 1000).to_i
58
+ kvalues[:_ts] = ts.strftime("%Y-%m-%dT%H:%M:%SZ")
59
+ kvalues[:_request_id] = $request_id if $request_id
60
+
61
+ # Enqueue
62
+ $lr_queue << [table, kvalues]
63
+ end
64
+
65
+ ###
66
+ # Sends the entries to the server, if configured
67
+ # Returns the number of entries send, and the time it takes
68
+ #
69
+ # Warning: may raise Exception if there's a problem. It's up to the called to rescue from it in order to continue the processing
70
+ # of a web request, for example.
71
+ ###
72
+ def self.flush
73
+ to_send = $lr_queue.size
74
+
75
+ # Return if no entries
76
+ return [0,0] if to_send == 0
77
+
78
+ # Return if no server
79
+ unless $lw_server
80
+ $stderr.puts "\t logworm not configured. #{to_send} entries dropped."
81
+ $lr_queue = []
82
+ return [0,0]
83
+ end
84
+
85
+ startTime = Time.now
86
+ $lw_server.batch_log($lr_queue.to_json)
87
+ $lr_queue = []
88
+ endTime = Time.now
89
+
90
+ [to_send, (endTime - startTime)]
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,62 @@
1
+ require 'rack/request'
2
+
3
+ module Logworm
4
+ class Rack
5
+
6
+ def initialize(app, options = {})
7
+ @app = app
8
+
9
+ @log_requests = (options[:donot_log_requests].nil? or options[:donot_log_requests] != true)
10
+ @log_headers = (options[:log_headers] and options[:log_headers] == true)
11
+ @dev_logging = (options[:log_in_development] and options[:log_in_development] == true)
12
+ Logger.use_default_db
13
+ @timeout = 1
14
+ end
15
+
16
+ def call(env)
17
+ return @app.call(env) unless (ENV['RACK_ENV'] == 'production' or (ENV['RACK_ENV'] == 'development' and @dev_logging))
18
+
19
+ Logger.start_cycle
20
+ begin
21
+ startTime = Time.now
22
+ status, response_headers, body = @app.call(env)
23
+ appTime = (Time.now - startTime)
24
+ ensure
25
+ log_request(env, status, response_headers, appTime)
26
+ return [status, response_headers, body]
27
+ end
28
+ end
29
+
30
+ private
31
+ def log_request(env, status, response_headers, appTime)
32
+ method = env['REQUEST_METHOD']
33
+ path = (env['REQUEST_PATH'].nil? or env['REQUEST_PATH'] == "") ? "/" : env['REQUEST_PATH']
34
+ ip = env['REMOTE_ADDR']
35
+ http_headers = env.reject {|k,v| !(k.to_s =~ /^HTTP/) }
36
+ queue_size = env['HTTP_X_HEROKU_QUEUE_DEPTH'].nil? ? -1 : env['HTTP_X_HEROKU_QUEUE_DEPTH'].to_i
37
+
38
+ entry = { :summary => "#{method} #{path} - #{status} #{appTime}",
39
+ :request_method => method,
40
+ :request_path => path,
41
+ :request_ip => ip,
42
+ :input => ::Rack::Request.new(env).params,
43
+ :response_status => status,
44
+ :profiling => appTime,
45
+ :queue_size => queue_size}
46
+ entry[:request_headers] = http_headers if @log_headers
47
+ entry[:response_headers] = response_headers if @log_headers
48
+ Logger.log(:web_log, entry) if @log_requests
49
+
50
+ begin
51
+ Timeout::timeout(@timeout) {
52
+ sent, elapsed = Logger.flush
53
+ env['rack.errors'].puts("-- #{sent} logworm entries recorded in #{sprintf('%.4f', elapsed)} seconds") if sent > 0
54
+ }
55
+ rescue Exception => e
56
+ # Ignore --nothing we can do. The list of logs may (and most likely will) be preserved for the next request
57
+ env['rack.errors'].puts("logworm call failed: #{e}")
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,106 @@
1
+ ###
2
+ # Extemds ActionController::Base and aliases the process method, to wrap it with the standard logworm request cycle
3
+ #
4
+ # By default it will automatically log web requests (in a short format) into web_log
5
+ # Can also log headers, if specified
6
+ ###
7
+ if defined?(ActionController)
8
+
9
+ require 'benchmark'
10
+
11
+ ActionController::Base.class_eval do
12
+ ## Basic settings: log requests, without headers. Use default db, and timeout after 1 second
13
+ @@log_requests = true
14
+ @@log_headers = false
15
+ @@dev_logging = false
16
+ @@timeout = 1
17
+ Logworm::Logger.use_default_db
18
+
19
+ ###
20
+ # Disable automatic logging of requests
21
+ # Use from ApplicationController
22
+ ###
23
+ def self.donot_log_requests
24
+ @@log_requests = false
25
+ end
26
+
27
+ ###
28
+ # Log headers with requests
29
+ # Use from ApplicationController
30
+ ###
31
+ def self.log_headers
32
+ @@log_headers = true
33
+ end
34
+
35
+ ###
36
+ # Turn on logging in development mode
37
+ ###
38
+ def self.log_in_development
39
+ @@dev_logging = true
40
+ end
41
+
42
+ ###
43
+ # Replaces (and embeds) the default Rails processing of a request.
44
+ # Call the original method, logs the request unless disabled, and flushes the logworm list
45
+ ###
46
+ def process_with_logworm_log(request, response, method = :perform_action, *arguments)
47
+ unless (RAILS_ENV == 'production' or (RAILS_ENV == 'development' and @@dev_logging))
48
+ return process_without_logworm_log(request, response, method, *arguments)
49
+ end
50
+
51
+ Logworm::Logger.start_cycle
52
+ begin
53
+ startTime = Time.now
54
+ response = process_without_logworm_log(request, response, method, *arguments)
55
+ appTime = (Time.now - startTime)
56
+ ensure
57
+ log_request(request, response, appTime)
58
+ return response
59
+ end
60
+ end
61
+ alias_method_chain :process, :logworm_log
62
+
63
+
64
+ private
65
+ def log_request(request, response, appTime)
66
+ method = request.env['REQUEST_METHOD']
67
+ path = request.env['REQUEST_PATH'].blank? ? "/" : request.env['REQUEST_PATH']
68
+ ip = request.env['REMOTE_ADDR']
69
+ http_headers = request.headers.reject {|k,v| !(k.to_s =~ /^HTTP/) }
70
+ status = response.status[0..2]
71
+ queue_size = request.env['HTTP_X_HEROKU_QUEUE_DEPTH'].blank? ? -1 : request.env['HTTP_X_HEROKU_QUEUE_DEPTH'].to_i
72
+
73
+ entry = { :summary => "#{method} #{path} - #{status} #{appTime}",
74
+ :request_method => method,
75
+ :request_path => path,
76
+ :request_ip => ip,
77
+ :input => cleaned_input(request),
78
+ :response_status => status,
79
+ :profiling => appTime,
80
+ :queue_size => queue_size}
81
+ entry[:request_headers] = http_headers if @@log_headers
82
+ entry[:response_headers] = response.headers if @@log_headers
83
+ Logworm::Logger.log(:web_log, entry) if @@log_requests
84
+
85
+ begin
86
+ Timeout::timeout(@@timeout) {
87
+ sent, elapsed = Logworm::Logger.flush
88
+ Rails.logger.info("#{sent} logworm entries recorded in #{sprintf('%.4f', elapsed)} seconds") if sent > 0
89
+ }
90
+ rescue Exception => e
91
+ # Ignore --nothing we can do. The list of logs may (and most likely will) be preserved for the next request
92
+ Rails.logger.error("logworm call failed: #{e}")
93
+ end
94
+ end
95
+
96
+ def cleaned_input(request)
97
+ pars = request.parameters.clone
98
+ pars.delete("controller")
99
+ pars.delete("action")
100
+ respond_to?(:filter_parameters) ? filter_parameters(pars) : pars
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
@@ -0,0 +1,62 @@
1
+ require 'logworm'
2
+
3
+ require 'logworm_client/logger'
4
+ require 'logworm_client/rack'
5
+ require 'logworm_client/rails'
6
+
7
+ def lw_log (logname, values)
8
+ Logworm::Logger.log(logname, values)
9
+ end
10
+
11
+ ###
12
+ # Perform a query against the logworm server
13
+ #
14
+ # Requires a log table, and a query
15
+ # The query can be provided as a JSON string, following the syntax described in http://www.logworm.com/docs/query
16
+ # or as a Hash of options, with the following keys (all optional)
17
+ # :fields => String with a comma-separated list of fields (quoted or not), or Array of Strings
18
+ # :aggregate_function => String
19
+ # :aggregate_argument => String
20
+ # :aggregate_group => String with a comma-separated list of fields (quoted or not), or Array of Strings
21
+ # :conditions => String with comma-separated conditions (in MongoDB syntax), or Array of Strings
22
+ # :start => String or Integer (for year)
23
+ # :end => String or Integer (for year)
24
+ # :limit => String or Integer
25
+ #
26
+ # See Logworm::QueryBuilder
27
+ #
28
+ # Returns Hash with
29
+ # id ==> id of the query
30
+ # query_url ==> URL to GET information about the query
31
+ # results_url ==> URL to GET the results for the query
32
+ # created ==> First creation of the query
33
+ # updated ==> most recent update of the query and/or its results
34
+ # expires ==> until that datime, the query won't be rerun against the database
35
+ # execution_time ==> time in ms to run the query
36
+ # results ==> array of hashmaps. Each element corresponds to a log entry, with its fields
37
+ #
38
+ # raises Logworm::DatabaseException, Logworm::ForbiddenAccessException, Logworm::InvalidQueryException
39
+ # or just a regular Exception if it cannot find the URL to the logging database
40
+ ###
41
+ def lw_query(logname, query = {})
42
+ db = Logworm::DB.from_config_or_die # Establish connection to DB
43
+ query = Logworm::QueryBuilder.new(query).to_json if query.is_a? Hash # Turn query into proper JSON string
44
+ query_data = db.query(logname, query) # POST to create query
45
+ db.results(query_data["results_uri"]) # GET from query's results uri
46
+ end
47
+
48
+ ###
49
+ # Returns an array with information about the logging tables in the database
50
+ # Each element in the array has;
51
+ # :tablename => The name of the logging table
52
+ # :url => The URL for POSTing new log entries
53
+ # :last_write => Datetime of last entry
54
+ # :rows => Count of entries
55
+ #
56
+ # raises Logworm::DatabaseException, Logworm::ForbiddenAccessException, Logworm::InvalidQueryException
57
+ # or just a regular Exception if it cannot find the URL to the logging database
58
+ ###
59
+ def lw_list_logs
60
+ db = Logworm::DB.from_config_or_die # Establish connection to DB
61
+ db.tables # Call tables command
62
+ end
@@ -0,0 +1,66 @@
1
+ class LogwormCompute
2
+ def initialize(table, function, field, options)
3
+ @table = table
4
+ @function = function
5
+ @field = field
6
+ @options = options
7
+
8
+ @valuefield = @field ? "#{@function}(#{@field})" : @function
9
+
10
+ begin
11
+ @db = Logworm::DB.from_config_or_die(@options[:app])
12
+ spec = {:aggregate_function => @function, :aggregate_argument => @field}.merge(@options)
13
+ @query = Logworm::QueryBuilder.new(spec)
14
+ rescue Exception => e
15
+ $stderr.puts "There was an error: #{e}"
16
+ exit(-1)
17
+ end
18
+
19
+ end
20
+
21
+ def run
22
+ # Create a resource for the query
23
+ begin
24
+ query_data = @db.query(@table, @query.to_json)
25
+ url = query_data["results_uri"]
26
+ rows = @db.results(url + "?nocache=1")["results"]
27
+ rescue Logworm::DatabaseException, Logworm::ForbiddenAccessException => e
28
+ $stderr.puts "Error: #{e}"
29
+ exit(-1)
30
+ rescue Logworm::InvalidQueryException => e
31
+ $stderr.puts "#{e}, #{@query.to_json}"
32
+ exit(-1)
33
+ rescue Exception => e
34
+ $stderr.puts "Error: #{e}"
35
+ exit(-1)
36
+ end
37
+
38
+ if @options[:debug]
39
+ puts "logworm query: #{@query.to_json}"
40
+ puts "logworm query url: #{query_data["self_uri"]}"
41
+ puts "logworm results url: #{url}"
42
+ puts
43
+ end
44
+
45
+ if @query.groups.length > 0
46
+ results = {}
47
+ rows.each do |r|
48
+ grp = []
49
+ @query.groups.each do |g|
50
+ grp << "#{g}:#{r[g]}"
51
+ end
52
+ key = "[#{grp.join(', ')}]"
53
+ value = r[@function]
54
+ results[key] = {:value => value, :keys => r.dup}
55
+ results[key][:keys].delete(@function)
56
+ end
57
+ results.keys.sort.each do |k|
58
+ puts "#{k} \t ==> #{@valuefield} = #{results[k][:value]}"
59
+ end
60
+ else
61
+ puts "#{@valuefield} = #{rows.first[@function]}"
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,106 @@
1
+ class LogwormTail
2
+ def initialize(table, options)
3
+ @table = table
4
+ @options = options
5
+
6
+ begin
7
+ @db = Logworm::DB.from_config_or_die(@options[:app])
8
+ @query = Logworm::QueryBuilder.new(@options.merge(:force_ts => true))
9
+ rescue Exception => e
10
+ $stderr.puts "Error: #{e}"
11
+ exit(-1)
12
+ end
13
+ end
14
+
15
+ def self.list(options = {})
16
+ begin
17
+ @db = Logworm::DB.from_config_or_die(options[:app])
18
+ @tables = @db.tables
19
+ if @tables and @tables.size > 0
20
+ puts "The following are the tables that you've created thus far:"
21
+ @tables.sort {|x,y| x["tablename"] <=> y["tablename"]}.each do |t|
22
+ puts "\t - #{t["tablename"]}, #{t["rows"]} rows, last updated on #{date_time(t["last_write"])}"
23
+ end
24
+ else
25
+ puts "You haven't recorded any data yet."
26
+ end
27
+ rescue Exception => e
28
+ $stderr.puts "Error: #{e}"
29
+ exit(-1)
30
+ end
31
+ end
32
+
33
+ def run
34
+ # Create a resource for the query
35
+ begin
36
+ query_data = @db.query(@table, @query.to_json)
37
+ url = query_data["results_uri"]
38
+ rescue Logworm::DatabaseException, Logworm::ForbiddenAccessException => e
39
+ $stderr.puts "Error: #{e}"
40
+ exit(-1)
41
+ rescue Logworm::InvalidQueryException => e
42
+ $stderr.puts "#{e}"
43
+ exit(-1)
44
+ rescue Exception => e
45
+ $stderr.puts "Error: #{e}"
46
+ exit(-1)
47
+ end
48
+
49
+ if @options[:debug]
50
+ puts "logworm query: #{@query.to_json}"
51
+ puts "logworm query url: #{query_data["self_uri"]}"
52
+ puts "logworm results url: #{url}"
53
+ puts "refresh frequency: #{@options[:frequency]}" if @options[:loop]
54
+ puts
55
+ end
56
+
57
+ while true do
58
+ begin
59
+ last_printed = print_rows(@db.results(url + "?nocache=1")["results"], last_printed || nil)
60
+ rescue Logworm::DatabaseException, Logworm::ForbiddenAccessException => e
61
+ $stderr.puts "Error: #{e}"
62
+ exit(-1)
63
+ rescue Logworm::InvalidQueryException => e
64
+ $stderr.puts "#{e}"
65
+ exit(-1)
66
+ rescue Exception => e
67
+ $stderr.puts "Error: #{e}"
68
+ exit(-1)
69
+ end
70
+ exit(0) unless @options[:loop]
71
+ sleep @options[:frequency]
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def self.date_time(val)
78
+ val.gsub(/T/, ' @ ').gsub(/Z/, ' GMT')
79
+ end
80
+
81
+ def print_rows(rows, last)
82
+ last = "" if last.nil?
83
+ rows.reverse.each do |r|
84
+ next unless r["_ts"]
85
+ if r["_ts"] > last
86
+ last = r["_ts"]
87
+ r.delete("_id") unless @options[:fields].include?("_id")
88
+ r.delete("_ts") unless @options[:fields].include?("_ts")
89
+ puts "#{LogwormTail.date_time(last)} ==> "
90
+ print_row(r)
91
+ end
92
+ end
93
+ last
94
+ end
95
+
96
+ def print_row(r)
97
+ if @options[:flat]
98
+ puts "\t" + r.keys.sort.map {|k| "#{k}: #{r[k].inspect}"}.join(', ')
99
+ else
100
+ r.keys.sort.each do |k|
101
+ puts "\t#{k}: #{r[k].inspect}"
102
+ end
103
+ puts
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,4 @@
1
+ require 'logworm'
2
+
3
+ require 'logworm_utils/tail'
4
+ require 'logworm_utils/compute'
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{logworm_client_amqp}
5
+ s.version = "0.8.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Pomelo, LLC"]
9
+ s.date = %q{2010-07-31}
10
+ s.description = %q{logworm client utilities}
11
+ s.email = %q{schapira@pomelollc.com}
12
+ s.executables = ["lw-compute", "lw-tail"]
13
+ s.extra_rdoc_files = ["CHANGELOG", "README.md", "bin/lw-compute", "bin/lw-tail", "lib/logworm_client/logger.rb", "lib/logworm_client/rack.rb", "lib/logworm_client/rails.rb", "lib/logworm_client_amqp.rb", "lib/logworm_utils.rb", "lib/logworm_utils/compute.rb", "lib/logworm_utils/tail.rb"]
14
+ s.files = ["CHANGELOG", "Manifest", "README.md", "Rakefile", "bin/lw-compute", "bin/lw-tail", "lib/logworm_client/logger.rb", "lib/logworm_client/rack.rb", "lib/logworm_client/rails.rb", "lib/logworm_client_amqp.rb", "lib/logworm_utils.rb", "lib/logworm_utils/compute.rb", "lib/logworm_utils/tail.rb", "spec/logger_spec.rb", "spec/rack_spec.rb", "spec/rails_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "logworm_client_amqp.gemspec"]
15
+ s.homepage = %q{http://www.logworm.com}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Logworm_client_amqp", "--main", "README.md"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{logworm_client_amqp}
19
+ s.rubygems_version = %q{1.3.7}
20
+ s.summary = %q{logworm client utilities}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<logworm_amqp>, [">= 0.8.0"])
28
+ s.add_runtime_dependency(%q<json>, [">= 0"])
29
+ s.add_development_dependency(%q<logworm_amqp>, [">= 0.8.0"])
30
+ s.add_development_dependency(%q<json>, [">= 0"])
31
+ else
32
+ s.add_dependency(%q<logworm_amqp>, [">= 0.8.0"])
33
+ s.add_dependency(%q<json>, [">= 0"])
34
+ s.add_dependency(%q<logworm_amqp>, [">= 0.8.0"])
35
+ s.add_dependency(%q<json>, [">= 0"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<logworm_amqp>, [">= 0.8.0"])
39
+ s.add_dependency(%q<json>, [">= 0"])
40
+ s.add_dependency(%q<logworm_amqp>, [">= 0.8.0"])
41
+ s.add_dependency(%q<json>, [">= 0"])
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'webmock'
3
+ require 'logworm'
4
+
5
+ require File.dirname(__FILE__) + '/spec_helper'
6
+
7
+ $: << File.dirname(__FILE__) + '/../lib'
8
+ require 'logworm_client/logger.rb'
9
+
10
+ describe Logworm::Logger, " flushing" do
11
+ before do
12
+ Logworm::Logger.use_db Logworm::DB.new("logworm://a:b@localhost/c/d/")
13
+ end
14
+
15
+ it "should only record if it's been initialized" do
16
+ Logworm::Logger.use_db nil
17
+ Logworm::Logger.log(:tbl1, :a => 1)
18
+ Logworm::Logger.flush.should == [0,0]
19
+ end
20
+
21
+ it "should only record if there are entries" do
22
+ Logworm::Logger.flush.should == [0,0]
23
+ end
24
+
25
+ it "should try to record it it's initialized and has entries, and then reset" do
26
+ stub_request(:post, "localhost/log").to_return(:body => {:count => 10, :inserted_at => Time.now}.to_json)
27
+ Logworm::Logger.log(:tbl1, :a => 1)
28
+ Logworm::Logger.flush[0].should == 1
29
+ Logworm::Logger.flush.should == [0,0]
30
+ end
31
+
32
+ end
33
+
data/spec/rack_spec.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'webmock'
3
+ require 'logworm'
4
+
5
+ require File.dirname(__FILE__) + '/spec_helper'
6
+
7
+ $: << File.dirname(__FILE__) + '/../lib'
8
+ require 'logworm_client.rb'
9
+
10
+ describe Logworm::Rack, " init" do
11
+ it "should initialize the logger with a default db" do
12
+ Logworm::Logger.should_receive(:use_default_db)
13
+ Logworm::Rack.new("xx")
14
+ end
15
+ end
16
+
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'webmock'
3
+ require 'logworm'
4
+
5
+ require File.dirname(__FILE__) + '/spec_helper'
6
+
7
+ $: << File.dirname(__FILE__) + '/../lib'
8
+
9
+ describe Logworm, " init" do
10
+ it "should initialize the logger with a default db" do
11
+ require 'logworm_client/logger.rb' # ==> Get Logworm::Logger defined
12
+ Logworm::Logger.should_receive(:use_default_db)
13
+
14
+ require 'action_controller' # ==> Get ActionController defined
15
+ load 'logworm_client/rails.rb' # ==> does class_eval of ActionController (use load to guarantee require)
16
+ end
17
+ end
18
+
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format specdoc
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'spec/autorun'
4
+ require 'spec/interop/test'
5
+ require 'webmock/rspec'
6
+
7
+ include WebMock
8
+
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logworm_client_amqp
3
+ version: !ruby/object:Gem::Version
4
+ hash: 63
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
+ platform: ruby
12
+ authors:
13
+ - Pomelo, LLC
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-31 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: logworm_amqp
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 63
30
+ segments:
31
+ - 0
32
+ - 8
33
+ - 0
34
+ version: 0.8.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: json
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: logworm_amqp
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 63
60
+ segments:
61
+ - 0
62
+ - 8
63
+ - 0
64
+ version: 0.8.0
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: json
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id004
81
+ description: logworm client utilities
82
+ email: schapira@pomelollc.com
83
+ executables:
84
+ - lw-compute
85
+ - lw-tail
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - CHANGELOG
90
+ - README.md
91
+ - bin/lw-compute
92
+ - bin/lw-tail
93
+ - lib/logworm_client/logger.rb
94
+ - lib/logworm_client/rack.rb
95
+ - lib/logworm_client/rails.rb
96
+ - lib/logworm_client_amqp.rb
97
+ - lib/logworm_utils.rb
98
+ - lib/logworm_utils/compute.rb
99
+ - lib/logworm_utils/tail.rb
100
+ files:
101
+ - CHANGELOG
102
+ - Manifest
103
+ - README.md
104
+ - Rakefile
105
+ - bin/lw-compute
106
+ - bin/lw-tail
107
+ - lib/logworm_client/logger.rb
108
+ - lib/logworm_client/rack.rb
109
+ - lib/logworm_client/rails.rb
110
+ - lib/logworm_client_amqp.rb
111
+ - lib/logworm_utils.rb
112
+ - lib/logworm_utils/compute.rb
113
+ - lib/logworm_utils/tail.rb
114
+ - spec/logger_spec.rb
115
+ - spec/rack_spec.rb
116
+ - spec/rails_spec.rb
117
+ - spec/spec.opts
118
+ - spec/spec_helper.rb
119
+ - logworm_client_amqp.gemspec
120
+ has_rdoc: true
121
+ homepage: http://www.logworm.com
122
+ licenses: []
123
+
124
+ post_install_message:
125
+ rdoc_options:
126
+ - --line-numbers
127
+ - --inline-source
128
+ - --title
129
+ - Logworm_client_amqp
130
+ - --main
131
+ - README.md
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ hash: 3
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 11
149
+ segments:
150
+ - 1
151
+ - 2
152
+ version: "1.2"
153
+ requirements: []
154
+
155
+ rubyforge_project: logworm_client_amqp
156
+ rubygems_version: 1.3.7
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: logworm client utilities
160
+ test_files: []
161
+