logworm_client_amqp 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+