added a Rack logger middleware
This commit is contained in:
parent
41eeedd49b
commit
ac3d0a988f
263
lib/couchrest/middlewares/logger.rb
Normal file
263
lib/couchrest/middlewares/logger.rb
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
####################################
|
||||||
|
# USAGE
|
||||||
|
#
|
||||||
|
# in your rack.rb file
|
||||||
|
# require this file and then:
|
||||||
|
#
|
||||||
|
# couch = CouchRest.new
|
||||||
|
# LOG_DB = couch.database!('couchrest-logger')
|
||||||
|
# use CouchRest::Logger, LOG_DB
|
||||||
|
#
|
||||||
|
# Note:
|
||||||
|
# to require just this middleware, if you have the gem installed do:
|
||||||
|
# require 'couchrest/middlewares/logger'
|
||||||
|
#
|
||||||
|
# For log processing examples, see examples at the bottom of this file
|
||||||
|
|
||||||
|
module CouchRest
|
||||||
|
class Logger
|
||||||
|
|
||||||
|
def self.log
|
||||||
|
Thread.current["couchrest.logger"] ||= {:queries => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(app, db=nil)
|
||||||
|
@app = app
|
||||||
|
@db = db
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.record(log_info)
|
||||||
|
log[:queries] << log_info
|
||||||
|
end
|
||||||
|
|
||||||
|
def log
|
||||||
|
Thread.current["couchrest.logger"] ||= {:queries => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_log
|
||||||
|
Thread.current["couchrest.logger"] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
reset_log
|
||||||
|
log['started_at'] = Time.now
|
||||||
|
log['env'] = env
|
||||||
|
log['url'] = 'http://' + env['HTTP_HOST'] + env['REQUEST_URI']
|
||||||
|
response = @app.call(env)
|
||||||
|
log['ended_at'] = Time.now
|
||||||
|
log['duration'] = log['ended_at'] - log['started_at']
|
||||||
|
# let's report the log in a different thread so we don't slow down the app
|
||||||
|
@db ? Thread.new(@db, log){|db, rlog| db.save_doc(rlog);} : p(log.inspect)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# inject our logger into CouchRest HTTP abstraction layer
|
||||||
|
module HttpAbstraction
|
||||||
|
|
||||||
|
def self.get(uri, headers=nil)
|
||||||
|
start_query = Time.now
|
||||||
|
log = {:method => :get, :uri => uri, :headers => headers}
|
||||||
|
response = super(uri, headers=nil)
|
||||||
|
end_query = Time.now
|
||||||
|
log[:duration] = (end_query - start_query)
|
||||||
|
CouchRest::Logger.record(log)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.post(uri, payload, headers=nil)
|
||||||
|
start_query = Time.now
|
||||||
|
log = {:method => :post, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
|
||||||
|
response = super(uri, payload, headers=nil)
|
||||||
|
end_query = Time.now
|
||||||
|
log[:duration] = (end_query - start_query)
|
||||||
|
CouchRest::Logger.record(log)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.put(uri, payload, headers=nil)
|
||||||
|
start_query = Time.now
|
||||||
|
log = {:method => :put, :uri => uri, :payload => (payload ? (JSON.load(payload) rescue 'parsing error') : nil), :headers => headers}
|
||||||
|
response = super(uri, payload, headers=nil)
|
||||||
|
end_query = Time.now
|
||||||
|
log[:duration] = (end_query - start_query)
|
||||||
|
CouchRest::Logger.record(log)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete(uri, headers=nil)
|
||||||
|
start_query = Time.now
|
||||||
|
log = {:method => :delete, :uri => uri, :headers => headers}
|
||||||
|
response = super(uri, headers=nil)
|
||||||
|
end_query = Time.now
|
||||||
|
log[:duration] = (end_query - start_query)
|
||||||
|
CouchRest::Logger.record(log)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Advanced usage example
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # DB VIEWS
|
||||||
|
# by_url = {
|
||||||
|
# :map =>
|
||||||
|
# "function(doc) {
|
||||||
|
# if(doc['url']){ emit(doc['url'], 1) };
|
||||||
|
# }",
|
||||||
|
# :reduce =>
|
||||||
|
# 'function (key, values, rereduce) {
|
||||||
|
# return(sum(values));
|
||||||
|
# };'
|
||||||
|
# }
|
||||||
|
# req_duration = {
|
||||||
|
# :map =>
|
||||||
|
# "function(doc) {
|
||||||
|
# if(doc['duration']){ emit(doc['url'], doc['duration']) };
|
||||||
|
# }",
|
||||||
|
# :reduce =>
|
||||||
|
# 'function (key, values, rereduce) {
|
||||||
|
# return(sum(values)/values.length);
|
||||||
|
# };'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# query_duration = {
|
||||||
|
# :map =>
|
||||||
|
# "function(doc) {
|
||||||
|
# if(doc['queries']){
|
||||||
|
# doc.queries.forEach(function(query){
|
||||||
|
# if(query['duration'] && query['method']){
|
||||||
|
# emit(query['method'], query['duration'])
|
||||||
|
# }
|
||||||
|
# });
|
||||||
|
# };
|
||||||
|
# }" ,
|
||||||
|
# :reduce =>
|
||||||
|
# 'function (key, values, rereduce) {
|
||||||
|
# return(sum(values)/values.length);
|
||||||
|
# };'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# action_queries = {
|
||||||
|
# :map =>
|
||||||
|
# "function(doc) {
|
||||||
|
# if(doc['queries']){
|
||||||
|
# emit(doc['url'], doc['queries'].length)
|
||||||
|
# };
|
||||||
|
# }",
|
||||||
|
# :reduce =>
|
||||||
|
# 'function (key, values, rereduce) {
|
||||||
|
# return(sum(values)/values.length);
|
||||||
|
# };'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# action_time_spent_in_db = {
|
||||||
|
# :map =>
|
||||||
|
# "function(doc) {
|
||||||
|
# if(doc['queries']){
|
||||||
|
# var totalDuration = 0;
|
||||||
|
# doc.queries.forEach(function(query){
|
||||||
|
# totalDuration += query['duration']
|
||||||
|
# })
|
||||||
|
# emit(doc['url'], totalDuration)
|
||||||
|
# };
|
||||||
|
# }",
|
||||||
|
# :reduce =>
|
||||||
|
# 'function (key, values, rereduce) {
|
||||||
|
# return(sum(values)/values.length);
|
||||||
|
# };'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# show_queries = %Q~function(doc, req) {
|
||||||
|
# var body = ""
|
||||||
|
# body += "<h1>" + doc['url'] + "</h1>"
|
||||||
|
# body += "<h2>Request duration in seconds: " + doc['duration'] + "</h2>"
|
||||||
|
# body += "<h3>" + doc['queries'].length + " queries</h3><ul>"
|
||||||
|
# if (doc.queries){
|
||||||
|
# doc.queries.forEach(function(query){
|
||||||
|
# body += "<li>"+ query['uri'] +"</li>"
|
||||||
|
# });
|
||||||
|
# };
|
||||||
|
# body += "</ul>"
|
||||||
|
# if(doc){ return { body: body} }
|
||||||
|
# }~
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# couch = CouchRest.new
|
||||||
|
# LOG_DB = couch.database!('couchrest-logger')
|
||||||
|
# design_doc = LOG_DB.get("_design/stats") rescue nil
|
||||||
|
# LOG_DB.delete_doc design_doc rescue nil
|
||||||
|
# LOG_DB.save_doc({
|
||||||
|
# "_id" => "_design/stats",
|
||||||
|
# :views => {
|
||||||
|
# :by_url => by_url,
|
||||||
|
# :request_duration => req_duration,
|
||||||
|
# :query_duration => query_duration,
|
||||||
|
# :action_queries => action_queries,
|
||||||
|
# :action_time_spent_in_db => action_time_spent_in_db
|
||||||
|
# },
|
||||||
|
# :shows => {
|
||||||
|
# :queries => show_queries
|
||||||
|
# }
|
||||||
|
# })
|
||||||
|
#
|
||||||
|
# module CouchRest
|
||||||
|
# class Logger
|
||||||
|
#
|
||||||
|
# def self.roundup(value)
|
||||||
|
# begin
|
||||||
|
# value = Float(value)
|
||||||
|
# (value * 100).round.to_f / 100
|
||||||
|
# rescue
|
||||||
|
# value
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Usage example:
|
||||||
|
# # CouchRest::Logger.average_request_duration(LOG_DB)['rows'].first['value']
|
||||||
|
# def self.average_request_duration(db)
|
||||||
|
# raw = db.view('stats/request_duration', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.average_query_duration(db)
|
||||||
|
# raw = db.view('stats/query_duration', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.average_get_query_duration(db)
|
||||||
|
# raw = db.view('stats/query_duration', :key => 'get', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.average_post_query_duration(db)
|
||||||
|
# raw = db.view('stats/query_duration', :key => 'post', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.average_queries_per_action(db)
|
||||||
|
# raw = db.view('stats/action_queries', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.average_db_time_per_action(db)
|
||||||
|
# raw = db.view('stats/action_time_spent_in_db', :reduce => true)
|
||||||
|
# (raw.has_key?('rows') && !raw['rows'].empty?) ? roundup(raw['rows'].first['value']) : 'not available yet'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# def self.stats(db)
|
||||||
|
# Thread.new(db){|db|
|
||||||
|
# puts "=== STATS ===\n"
|
||||||
|
# puts "average request duration: #{average_request_duration(db)}\n"
|
||||||
|
# puts "average query duration: #{average_query_duration(db)}\n"
|
||||||
|
# puts "average queries per action : #{average_queries_per_action(db)}\n"
|
||||||
|
# puts "average time spent in DB (per action): #{average_db_time_per_action(db)}\n"
|
||||||
|
# puts "===============\n"
|
||||||
|
# }
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# end
|
||||||
|
# end
|
Loading…
Reference in a new issue