tapsilog 0.1.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,2 @@
1
+ v0.0.1 Initial fork from internally forked analogger.
2
+
data/Manifest ADDED
@@ -0,0 +1,17 @@
1
+ CHANGELOG
2
+ Manifest
3
+ README.md
4
+ Rakefile
5
+ bin/tapsilog
6
+ lib/palmade/tapsilog.rb
7
+ lib/palmade/tapsilog/adapters.rb
8
+ lib/palmade/tapsilog/adapters/base_adapter.rb
9
+ lib/palmade/tapsilog/adapters/file_adapter.rb
10
+ lib/palmade/tapsilog/adapters/mongo_adapter.rb
11
+ lib/palmade/tapsilog/client.rb
12
+ lib/palmade/tapsilog/conn.rb
13
+ lib/palmade/tapsilog/logger.rb
14
+ lib/palmade/tapsilog/protocol.rb
15
+ lib/palmade/tapsilog/server.rb
16
+ lib/palmade/tapsilog/utils.rb
17
+ tapsilog.gemspec
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ ## Tapsilog, an asynchronous logging service
2
+
3
+ Tapsilog is a super customized fork of Analogger. Tapsilog allows you to attach tags to log messages so that it can be searched easily.
4
+ Currently, Tapsilog supports files and mongodb as storage backend.
5
+
6
+ **Supported adapters**
7
+
8
+ - file - Logs to files, STDOUT or STDERR
9
+ - mongo - Logs to mongoDB
10
+
11
+ **Gems required for mongoDB support**
12
+
13
+ - mongo
14
+ - bson
15
+ - bson_ext
16
+
17
+ ## Usage
18
+
19
+ **Tapsilog Server**
20
+
21
+ The best way to run tapsilog is to write a config file and call:
22
+
23
+ tapsilog -c /path/to/config_file.yml
24
+
25
+ **Sample Config**
26
+
27
+ port: 19080
28
+ host:
29
+ - 127.0.0.1
30
+ socket:
31
+ - /tmp/tapsilog.sock
32
+ daemonize: false
33
+ key: some_serious_key
34
+
35
+ syncinterval: 1
36
+
37
+ logs:
38
+ #Currently supports file or mongo
39
+ adapter: mongo
40
+
41
+ #These options are used for mongo backend.
42
+ #You can leave the host, port, user and password blank and tapsilog connects to your local mongo installation by default
43
+
44
+ #host: 127.0.0.1
45
+ #port: 1234
46
+ #user: root
47
+ #password: somepassword
48
+ database: tapsilog
49
+
50
+ services:
51
+ - service: default
52
+ target: default # This is the mongodb namespace. For file backend, use the path of log file
53
+
54
+ - service: dev.access
55
+ target: dev.access
56
+
57
+ - service: dev.bizsupport
58
+ target: dev.bizsupport
59
+
60
+ **Tapsilog Client**
61
+
62
+ The tapsilog Logger class quacks like the ruby standard Logger.
63
+
64
+ **Sample**
65
+
66
+ logger = Palmade::Tapsilog::Logger.new('default', '/tmp/tapsilog.sock', 'some_serious_key')
67
+ logger.level = Palmade::Tapsilog::Logger::DEBUG # defaults to INFO
68
+ logger.info("I am logging a message.", {:my_name => "tapsilog", :my_number => 2})
69
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'echoe'
2
+
3
+ Echoe.new('tapsilog', '0.1.0') do |p|
4
+ p.author = "Palmade"
5
+ p.project = "tapsilog"
6
+ p.summary = "Hydrid app-level logger from Palmade. Analogger fork."
7
+
8
+ p.dependencies = ["eventmachine"]
9
+ p.ignore_pattern = ["tmp/*"]
10
+ end
data/bin/tapsilog ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'optparse'
5
+ require 'palmade/tapsilog'
6
+
7
+ module Palmade
8
+ class TapsilogExec
9
+
10
+ def self.run
11
+ @@parsed_options ||= parse_options
12
+ Palmade::Tapsilog::Server.start(@@parsed_options)
13
+ end
14
+
15
+ def self.parse_options(config = {})
16
+ OptionParser.new do |opts|
17
+ opts.banner = 'Usage: tapsilog [options]'
18
+ opts.separator ''
19
+ opts.on('-c','--config CONFFILE',"The configuration file to read.") do |conf|
20
+ config = symbolize_keys(YAML.load(File.read(conf)))
21
+ end
22
+ opts.on('-p','--port [PORT]',Integer,"The port to receive connections on.") do |port|
23
+ config[:port] = port
24
+ end
25
+ opts.on('-h','--host [HOST]',String,"The host to bind the connection to.") do |host|
26
+ config[:host] = host
27
+ end
28
+ opts.on('-t','--socket [SOCKET]',String,"The unix domain socket to bind connection to.") do |socket|
29
+ config[:socket] = socket
30
+ end
31
+ opts.on('-k','--key [KEY]',String,"The secret key that authenticates a valid client session.") do |secret|
32
+ config[:key] = secret
33
+ end
34
+ opts.on('-i','--interval [INTERVAL]',String,"The interval between queue writes. Defaults to 1 second.") do |interval|
35
+ config[:interval] = interval
36
+ end
37
+ opts.on('-s','--syncinterval [INTERVAL]',String,"The interval between queue syncs. Defaults to 60 seconds.") do |interval|
38
+ config[:syncinterval] = interval
39
+ end
40
+ opts.on('-d','--default [PATH]',String,"The default log destination. Defaults to stdout.") do |default|
41
+ config[:default_log] = default
42
+ end
43
+ opts.on('-x','--daemonize',"Tell the Analogger to daemonize itself.") do
44
+ config[:daemonize] = true
45
+ end
46
+ opts.on('-w','--writepid [FILENAME]',"The filename to write a PID file to.") do |pidfile|
47
+ config[:pidfile] = pidfile
48
+ end.parse!
49
+ end
50
+ config
51
+ end
52
+
53
+ def self.symbolize_keys(hash)
54
+ hash.inject({}){|result, (key, value)|
55
+ new_key = key.kind_of?(String) ? key.to_sym : key
56
+ new_value = value.kind_of?(Hash) ? symbolize_keys(value) : value
57
+ result[new_key] = new_value
58
+ result
59
+ }
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ loop do
66
+ catch(:hup) {
67
+ Palmade::TapsilogExec.run
68
+ }
69
+ end
@@ -0,0 +1,19 @@
1
+ require 'socket'
2
+ require 'timeout'
3
+ require 'eventmachine'
4
+
5
+ module Palmade
6
+ module Tapsilog
7
+ class Timeout < Exception; end
8
+
9
+ autoload :Protocol, File.join(File.dirname(__FILE__), 'tapsilog/protocol')
10
+ autoload :Server, File.join(File.dirname(__FILE__), 'tapsilog/server')
11
+ autoload :Adapters, File.join(File.dirname(__FILE__), 'tapsilog/adapters')
12
+ autoload :Utils, File.join(File.dirname(__FILE__), 'tapsilog/utils')
13
+
14
+ autoload :Conn, File.join(File.dirname(__FILE__), 'tapsilog/conn')
15
+ autoload :Client, File.join(File.dirname(__FILE__), 'tapsilog/client')
16
+ autoload :Logger, File.join(File.dirname(__FILE__), 'tapsilog/logger')
17
+
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module Palmade::Tapsilog
2
+ module Adapters
3
+
4
+ autoload :BaseAdapter, File.join(File.dirname(__FILE__), 'adapters/base_adapter')
5
+ autoload :FileAdapter, File.join(File.dirname(__FILE__), 'adapters/file_adapter')
6
+ autoload :MongoAdapter, File.join(File.dirname(__FILE__), 'adapters/mongo_adapter')
7
+
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ module Palmade::Tapsilog::Adapters
2
+ class BaseAdapter
3
+
4
+ def initialize(config)
5
+ @config = config
6
+ initialize_services
7
+ end
8
+
9
+ def write(log_message)
10
+ end
11
+
12
+ def flush
13
+ end
14
+
15
+ def close
16
+ end
17
+
18
+ protected
19
+
20
+ def initialize_services
21
+ @services = {}
22
+
23
+ @config[:services].each do |service|
24
+ service_name = service['service']
25
+ @services[service_name] = {
26
+ :target => service['target']
27
+ }
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ module Palmade::Tapsilog::Adapters
2
+ class FileAdapter < BaseAdapter
3
+
4
+ def write(log_message)
5
+ service = log_message[1]
6
+ file = get_file_descriptor(service)
7
+ file.puts(log_message.join("|"))
8
+ end
9
+
10
+ def flush
11
+ @services.each do |name, service|
12
+ fd = service[:file]
13
+ unless fd.nil?
14
+ fd.fsync if fd.fileno > 2
15
+ end
16
+ end
17
+ end
18
+
19
+ def close
20
+ @services.each do |name, service|
21
+ fd = service[:file]
22
+ unless fd.nil?
23
+ fd.close unless fd.closed?
24
+ end
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def get_file_descriptor(service_name)
31
+ service_name = (@services[service_name].nil?) ? 'default' : service_name
32
+ service = @services[service_name]
33
+
34
+ if service[:file].nil?
35
+ service[:file] = open_file_descriptor(service)
36
+ end
37
+
38
+ service[:file]
39
+ end
40
+
41
+ def open_file_descriptor(service)
42
+ logfile = service[:target]
43
+
44
+ if logfile =~ /^STDOUT$/i
45
+ $stdout
46
+ elsif logfile =~ /^STDERR$/i
47
+ $stderr
48
+ else
49
+ File.open(logfile, 'ab+')
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,61 @@
1
+ require 'mongo'
2
+
3
+ module Palmade::Tapsilog::Adapters
4
+ class MongoAdapter < BaseAdapter
5
+
6
+ def write(log_message)
7
+ service = log_message[1]
8
+ coll = get_collection(service)
9
+
10
+ coll.insert(log_to_hash(log_message))
11
+ end
12
+
13
+ def close
14
+ unless @conn.nil?
15
+ @conn.close if @conn.connected?
16
+ end
17
+ end
18
+
19
+ protected
20
+
21
+ def get_collection(service_name)
22
+ service_name = (@services[service_name].nil?) ? 'default' : service_name
23
+ service = @services[service_name]
24
+
25
+ db_conn[service[:target]]
26
+ end
27
+
28
+ def log_to_hash(log_message)
29
+ timestamp, service, pid, severity, message, tags = log_message
30
+ log_hash = {
31
+ :timestamp => timestamp,
32
+ :service => service,
33
+ :pid => pid,
34
+ :severity => severity,
35
+ :message => message,
36
+ :created_at => Time.now
37
+ }
38
+ unless tags.nil? or tags.empty?
39
+ log_hash[:tags] = Palmade::Tapsilog::Utils::query_string_to_hash(tags)
40
+ end
41
+
42
+ log_hash
43
+ end
44
+
45
+ def db_conn
46
+ if @db.nil?
47
+ mongo_conn = Mongo::Connection.new(@config[:host], @config[:port])
48
+ db_name = @config[:database] || 'tapsilog'
49
+
50
+ @db = mongo_conn.db(db_name)
51
+ if @config[:user] and @config[:password]
52
+ @db.authenticate(@config[:user], @config[:password])
53
+ end
54
+ end
55
+
56
+ @db
57
+ end
58
+
59
+
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ module Palmade::Tapsilog
2
+ class Client
3
+
4
+ DEFAULT_OPTIONS = {
5
+ :async => false
6
+ }
7
+
8
+ def initialize(service = 'default', target = '127.0.0.1:19070', key = nil, instance_key = nil, options = { })
9
+ @service = service.to_s
10
+ @instance_key = instance_key
11
+
12
+ @target = target
13
+ @key = key
14
+
15
+ @options = DEFAULT_OPTIONS.merge(options)
16
+ @conn = Palmade::Tapsilog::Conn.new(@target, @key, @options[:async])
17
+ end
18
+
19
+ def log(severity, msg, tags = {}, ts = nil)
20
+ ts = Time.now if ts.nil?
21
+ conn_log(severity, msg, tags, ts)
22
+ self
23
+ end
24
+
25
+ def flush
26
+ @conn.flush
27
+ end
28
+
29
+ def close
30
+ @conn.close
31
+ end
32
+
33
+ def closed?
34
+ @conn.closed?
35
+ end
36
+
37
+ def reconnect
38
+ @conn.reconnect!
39
+ end
40
+
41
+ protected
42
+
43
+ def conn_log(severity, msg, tags = {}, ts = nil)
44
+ ts = Time.now if ts.nil?
45
+ @conn.log(@service, @instance_key || $$, severity, msg, tags, ts)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,132 @@
1
+ require 'cgi'
2
+
3
+ module Palmade::Tapsilog
4
+ class Conn
5
+ attr_reader :socket
6
+
7
+ MAX_TRIES = 6
8
+
9
+ def initialize(target, key, buffered = false)
10
+ if target =~ /(.+)\:(\d+)$/i
11
+ @host = $~[1]
12
+ @port = $~[2]
13
+ else
14
+ @host = target
15
+ @port = nil
16
+ end
17
+ @key = key
18
+ @socket = nil
19
+ @buffered = buffered
20
+ end
21
+
22
+ def connect(host, port)
23
+ if @socket.nil? || @socket.closed?
24
+ real_connect(host, port)
25
+ log('default', $$, 'authentication', @key)
26
+ else
27
+ @socket
28
+ end
29
+ end
30
+
31
+ def log(service, instance_key, severity, message, tags = {}, ts = nil)
32
+ tries = 0
33
+ connect(@host, @port)
34
+
35
+ ts = Time.now if ts.nil?
36
+ tag_string = Utils::hash_to_query_string(tags)
37
+
38
+ fullmsg = ":#{service}:#{instance_key}:#{severity}:#{message}:#{tag_string}"
39
+
40
+ # Truncate below the 8192 limit on Tapsilog service
41
+ fullmsg = fullmsg[0,8190] if fullmsg.size > 8190
42
+
43
+ len = [fullmsg.length].pack('i')
44
+
45
+ begin
46
+ # first 8-bytes, is len and checksum
47
+ write "#{len}#{len}#{fullmsg}"
48
+ rescue Exception => e
49
+ STDERR.puts "Failed to write to server! Retrying... (#{tries})" +
50
+ "#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}"
51
+
52
+ if tries < MAX_TRIES
53
+ tries += 1
54
+ close_possibly_dead_conn(tries)
55
+ reconnect
56
+ retry
57
+ else
58
+ raise e
59
+ end
60
+ end
61
+
62
+ len
63
+ end
64
+
65
+ def flush
66
+ @socket.flush unless @socket.nil?
67
+ end
68
+
69
+ def close
70
+ @socket.nil? ? nil : @socket.close rescue nil
71
+ end
72
+
73
+ def closed?
74
+ @socket.nil? ? true : @socket.closed?
75
+ end
76
+
77
+ def reconnect!
78
+ close unless closed?
79
+ connect(@host, @port)
80
+ end
81
+ alias :reconnect :reconnect!
82
+
83
+ protected
84
+
85
+ def real_connect(host, port)
86
+ tries = 0
87
+ begin
88
+ if host =~ /^\/(.+)/
89
+ @socket = UNIXSocket.new(host)
90
+ else
91
+ @socket = TCPSocket.new(host, port)
92
+ @socket.sync = !@buffered
93
+ end
94
+ raise "Unable to create socket!" if @socket.nil?
95
+ rescue Exception => e
96
+ STDERR.puts "Failed to establish connection with server! Retrying... (#{tries})" +
97
+ " #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}"
98
+
99
+ if tries < MAX_TRIES
100
+ tries += 1
101
+ close_possibly_dead_conn(tries)
102
+ retry
103
+ else
104
+ raise e
105
+ end
106
+ end
107
+
108
+ @socket
109
+ end
110
+
111
+ def write(msg, flush = false)
112
+ conn_timeout do
113
+ wrtlen = @socket.write(msg)
114
+ end
115
+ self.flush if flush
116
+ @socket
117
+ end
118
+
119
+ def close_possibly_dead_conn(tries = 0)
120
+ close unless @socket.nil? || closed?
121
+
122
+ @socket = nil
123
+ select(nil,nil,nil, tries * 0.2) if tries > 0
124
+ @socket
125
+ end
126
+
127
+ def conn_timeout(&block)
128
+ ::Timeout::timeout(6, Palmade::Tapsilog::Timeout, &block)
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,87 @@
1
+ module Palmade::Tapsilog
2
+ class Logger < Client
3
+ LOG_LEVEL_TEXT = [ 'debug', 'info', 'warn', 'error', 'fatal', 'unknown' ]
4
+
5
+ DEBUG, INFO, WARN, ERROR, FATAL, UNKNOWN = (0..5).to_a
6
+
7
+ attr_accessor :level
8
+
9
+ def initialize(*args, &block)
10
+ super(*args, &block)
11
+ @level = INFO
12
+ end
13
+
14
+ def info?
15
+ @level <= INFO
16
+ end
17
+
18
+ def info(progname = nil, tags = {}, &block)
19
+ add(INFO, nil, progname, tags, &block)
20
+ end
21
+
22
+ def debug?
23
+ @level <= DEBUG
24
+ end
25
+
26
+ def debug(progname = nil, tags = {}, &block)
27
+ add(DEBUG, nil, progname, tags, &block)
28
+ end
29
+
30
+ def error?
31
+ @level <= ERROR
32
+ end
33
+
34
+ def error(progname = nil, tags = {}, &block)
35
+ add(ERROR, nil, progname, tags, &block)
36
+ end
37
+
38
+ def fatal?
39
+ @level <= FATAL
40
+ end
41
+
42
+ def fatal(progname = nil, tags = {}, &block)
43
+ add(FATAL, nil, progname, tags, &block)
44
+ end
45
+
46
+ def warn?
47
+ @level <= WARN
48
+ end
49
+
50
+ def warn(progname = nil, tags = {}, &block)
51
+ add(WARN, nil, progname, tags, &block)
52
+ end
53
+
54
+ def add(severity, message = nil, progname = nil, tags = {}, &block)
55
+ case severity
56
+ when 'authentication'
57
+ return log_without_rails_extensions(severity, message)
58
+ when String, Symbol
59
+ severity = LOG_LEVEL_TEXT.index(severity.to_s.downcase) || UNKNOWN
60
+ when nil
61
+ severity = UNKNOWN
62
+ end
63
+
64
+ if severity < @level
65
+ return true
66
+ end
67
+
68
+ log_level_text = LOG_LEVEL_TEXT[severity]
69
+ progname ||= @service
70
+ message = if message.nil?
71
+ if block_given?
72
+ message = yield
73
+ else
74
+ progname
75
+ end
76
+ else
77
+ message.to_s
78
+ end
79
+
80
+ log_without_rails_extensions(log_level_text, message, tags)
81
+ end
82
+
83
+ alias_method :log_without_rails_extensions, :log
84
+ alias_method :log, :add
85
+ end
86
+ end
87
+
@@ -0,0 +1,87 @@
1
+ module Palmade::Tapsilog
2
+ class Protocol < EventMachine::Connection
3
+ Ci = 'i'.freeze
4
+ Rcolon = /:/
5
+ MaxMessageLength = 8192
6
+
7
+ LoggerClass = Palmade::Tapsilog::Server
8
+
9
+ def post_init
10
+ setup
11
+ end
12
+
13
+ def setup
14
+ @length = nil
15
+ @logchunk = ''
16
+ @authenticated = nil
17
+ end
18
+
19
+ def receive_data(data)
20
+ @logchunk << data
21
+
22
+ while @length.nil? and @logchunk.length > 7
23
+ return false unless get_length
24
+
25
+ if @length and @logchunk.length > @length
26
+ get_message
27
+ end
28
+ end
29
+ end
30
+
31
+ protected
32
+
33
+ def get_length
34
+ l = @logchunk[0..3].unpack(Ci).first
35
+ ck = @logchunk[4..7].unpack(Ci).first
36
+
37
+ if l == ck and l < MaxMessageLength
38
+ @length = l +7
39
+ return true
40
+ else
41
+ peer = get_peername
42
+ peer = (peer ? ::Socket.unpack_sockaddr_in(peer)[1] : 'UNK') rescue 'UNK'
43
+
44
+ if l == ck
45
+ LoggerClass.add_log([:default, $$.to_s, :error, "Max Length Exceeded from #{peer} -- #{l}/#{MaxMessageLength}"])
46
+ else
47
+ LoggerClass.add_log([:default, $$.to_s, :error, "Checksum failed from #{peer} -- #{l}/#{ck}"])
48
+ end
49
+
50
+ close_connection
51
+ return false
52
+ end
53
+ end
54
+
55
+ def get_message
56
+ msg = @logchunk.slice!(0..@length).split(Rcolon, 6)
57
+
58
+ unless @authenticated
59
+ @authenticated = authenticate_message(msg)
60
+ end
61
+
62
+ if @authenticated
63
+ msg[0] = nil
64
+ msg.shift
65
+
66
+ msg[0] = msg[0].to_s.gsub(/[^a-zA-Z0-9\-\_\.]\s/, '').strip
67
+
68
+ LoggerClass.add_log(msg)
69
+ @length = nil
70
+ end
71
+ end
72
+
73
+ def authenticate_message(msg)
74
+ if msg[4] == LoggerClass.key
75
+ return true
76
+ else
77
+ peer = get_peername
78
+ peer = (peer ? ::Socket.unpack_sockaddr_in(peer)[1] : 'UNK') rescue 'UNK'
79
+
80
+ LoggerClass.add_log([:default, $$.to_s, :error, "Invalid key from #{peer} -- #{msg.last}"])
81
+ close_connection
82
+ return false
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,151 @@
1
+ module Palmade::Tapsilog
2
+ class Server
3
+
4
+ SeverityLevels = [:debug, :info, :warn, :error, :fatal]
5
+
6
+ attr_reader :now
7
+
8
+ def self.start(config, protocol = Palmade::Tapsilog::Protocol)
9
+ @config = config
10
+ @protocol = protocol
11
+ @tsocks = []
12
+ @usocks = []
13
+ @queue = []
14
+ boot
15
+ end
16
+
17
+ def self.add_log(log)
18
+ @queue << ([@now] + log)
19
+ end
20
+
21
+ def self.key
22
+ @config[:key].to_s
23
+ end
24
+
25
+ protected
26
+
27
+ def self.boot
28
+ write_pid_file if @config[:pidfile]
29
+ daemonize if @config[:daemonize]
30
+
31
+ load_adapter
32
+
33
+ trap("INT") { exit }
34
+ trap("TERM") { exit }
35
+ trap("HUP") { throw :hup }
36
+
37
+ start_server
38
+ end
39
+
40
+ def self.write_pid_file
41
+ if File.exists?(@config[:pidfile])
42
+ begin
43
+ pid = File.read(@config[:pidfile]).strip
44
+ Process.kill(0, pid.to_i)
45
+ raise "Another instance of tapsilog seems to be running! (#{pid})"
46
+ rescue Errno::ESRCH
47
+ File.delete(@config[Cpidfile])
48
+ STDERR.puts "Stale PID (#{pid}) removed"
49
+ end
50
+ end
51
+
52
+ File.open(@config[Cpidfile],'w+') {|fh| fh.puts $$}
53
+ end
54
+
55
+ def self.daemonize
56
+ if (child_pid = fork)
57
+ puts "Forked PID #{child_pid}"
58
+ exit!
59
+ end
60
+ Process.setsid
61
+
62
+ rescue Exception
63
+ puts "Platform (#{RUBY_PLATFORM}) does not appear to support fork/setsid; skipping"
64
+ end
65
+
66
+ def self.load_adapter
67
+ raise "Missing logs section in config file" unless @config[:logs]
68
+
69
+ adapter_name = @config[:logs][:adapter] || "file"
70
+ class_name = "#{adapter_name.capitalize}Adapter"
71
+ adapter = Palmade::Tapsilog::Adapters.const_get(class_name)
72
+ @adapter = adapter.new(@config[:logs])
73
+ end
74
+
75
+ def self.start_server
76
+ prepare_server
77
+ begin
78
+ EventMachine.run {
79
+ start_servers
80
+ EventMachine.add_periodic_timer(1) { update_now }
81
+ EventMachine.add_periodic_timer(@config[:interval]) { write_queue }
82
+ EventMachine.add_periodic_timer(@config[:syncinterval]) { flush_queue }
83
+ }
84
+ ensure
85
+ cleanup
86
+ end
87
+ end
88
+
89
+ def self.prepare_server
90
+ if @config[:socket]
91
+ @usocks = @config[:socket]
92
+ @usocks = [ @usocks ] unless @usocks.is_a? Array
93
+
94
+ @usocks.each do |usock|
95
+ raise "Socket file already exists! (#{usock})" if File.exists?(usock)
96
+ end
97
+ end
98
+
99
+ if @config[:host]
100
+ @tsocks = @config[:host]
101
+ @tsocks = [ @tsocks ] unless @tsocks.is_a? Array
102
+
103
+ @tsocks.each do |tsock|
104
+ raise "Port already in use! #{tsock}:#{@config[:port]}" if Utils.is_port_open?(tsock, @config[:port])
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.start_servers
110
+ @usocks.each do |usock|
111
+ raise "Socket file already exists! (#{usock})" if File.exists?(usock)
112
+
113
+ STDERR.puts "Listening to: #{usock}"
114
+ EventMachine.start_unix_domain_server(usock, @protocol)
115
+ File.chmod(0777, usock)
116
+ end
117
+
118
+ @tsocks.each do |tsock|
119
+ raise "Port already in use! #{tsock}:#{@config[:port]}" if Utils.is_port_open?(tsock, @config[:port])
120
+
121
+ STDERR.puts "Listening to: #{tsock}:#{@config[:port]}"
122
+ EventMachine.start_server(tsock, @config[:port], @protocol)
123
+ end
124
+ end
125
+
126
+ def self.cleanup
127
+ @adapter.close unless @adapter.nil?
128
+ @usocks.each do |usock|
129
+ File.delete(usock) if File.exists?(usock)
130
+ end
131
+ File.delete(@config[:pidfile]) if @config[:pidfile] and File.exists?(@config[:pidfile])
132
+ end
133
+
134
+ def self.update_now
135
+ @now = Time.now.strftime('%Y/%m/%d %H:%M:%S')
136
+ end
137
+
138
+ def self.write_queue
139
+ @queue.each do |log_message|
140
+ next unless SeverityLevels.include?(log_message[3].to_sym)
141
+ @adapter.write(log_message)
142
+ end
143
+ @queue.clear
144
+ end
145
+
146
+ def self.flush_queue
147
+ @adapter.flush
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,138 @@
1
+ require 'cgi'
2
+
3
+ module Palmade::Tapsilog
4
+ class Utils
5
+
6
+ def self.is_port_open?(ip, port)
7
+ begin
8
+ ::Timeout::timeout(1) do
9
+ begin
10
+ s = TCPSocket.new(ip, port)
11
+ s.close
12
+ return true
13
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
14
+ return false
15
+ end
16
+ end
17
+ rescue ::Timeout::Error
18
+ end
19
+
20
+ return false
21
+ end
22
+
23
+ # Taken from github accumulator/uri
24
+
25
+ def self.query_string_to_hash(query_string, options={})
26
+ defaults = {:notation => :subscript}
27
+ options = defaults.merge(options)
28
+ if ![:flat, :dot, :subscript].include?(options[:notation])
29
+ raise ArgumentError,
30
+ "Invalid notation. Must be one of: [:flat, :dot, :subscript]."
31
+ end
32
+ dehash = lambda do |hash|
33
+ hash.each do |(key, value)|
34
+ if value.kind_of?(Hash)
35
+ hash[key] = dehash.call(value)
36
+ end
37
+ end
38
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
39
+ hash.sort.inject([]) do |accu, (key, value)|
40
+ accu << value; accu
41
+ end
42
+ else
43
+ hash
44
+ end
45
+ end
46
+ return nil if query_string == nil
47
+ return ((query_string.split("&").map do |pair|
48
+ pair.split("=", -1) if pair && pair != ""
49
+ end).compact.inject({}) do |accumulator, (key, value)|
50
+ value = true if value.nil?
51
+ key = CGI::unescape(key)
52
+ if value != true
53
+ value = CGI::unescape(value).gsub(/\+/, " ")
54
+ end
55
+ if options[:notation] == :flat
56
+ if accumulator[key]
57
+ raise ArgumentError, "Key was repeated: #{key.inspect}"
58
+ end
59
+ accumulator[key] = value
60
+ else
61
+ if options[:notation] == :dot
62
+ array_value = false
63
+ subkeys = key.split(".")
64
+ elsif options[:notation] == :subscript
65
+ array_value = !!(key =~ /\[\]$/)
66
+ subkeys = key.split(/[\[\]]+/)
67
+ end
68
+ current_hash = accumulator
69
+ for i in 0...(subkeys.size - 1)
70
+ subkey = subkeys[i]
71
+ current_hash[subkey] = {} unless current_hash[subkey]
72
+ current_hash = current_hash[subkey]
73
+ end
74
+ if array_value
75
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
76
+ current_hash[subkeys.last] << value
77
+ else
78
+ current_hash[subkeys.last] = value
79
+ end
80
+ end
81
+ accumulator
82
+ end).inject({}) do |accumulator, (key, value)|
83
+ accumulator[key] = value.kind_of?(Hash) ? dehash.call(value) : value
84
+ accumulator
85
+ end
86
+ end
87
+
88
+ def self.hash_to_query_string(hash)
89
+ # Check for frozenness
90
+ if hash == nil
91
+ return nil
92
+ end
93
+ if !hash.respond_to?(:to_hash)
94
+ raise TypeError, "Can't convert #{hash.class} into Hash."
95
+ end
96
+ hash = hash.to_hash
97
+ hash = hash.map do |key, value|
98
+ key = key.to_s if key.kind_of?(Symbol)
99
+ [key, value]
100
+ end
101
+ hash.sort! # Useful default for OAuth and caching
102
+
103
+ # Algorithm shamelessly stolen from Julien Genestoux, slightly modified
104
+ buffer = ""
105
+ stack = []
106
+ e = lambda do |component|
107
+ CGI::escape(component.to_s)
108
+ end
109
+ hash.each do |key, value|
110
+ if value.kind_of?(Hash)
111
+ stack << [key, value]
112
+ elsif value.kind_of?(Array)
113
+ stack << [
114
+ key,
115
+ value.inject({}) { |accu, x| accu[accu.size.to_s] = x; accu }
116
+ ]
117
+ elsif value == true
118
+ buffer << "#{e.call(key)}&"
119
+ else
120
+ buffer << "#{e.call(key)}=#{e.call(value)}&"
121
+ end
122
+ end
123
+ stack.each do |(parent, hash)|
124
+ (hash.sort_by { |key| key.to_s }).each do |(key, value)|
125
+ if value.kind_of?(Hash)
126
+ stack << ["#{parent}[#{key}]", value]
127
+ elsif value == true
128
+ buffer << "#{parent}[#{e.call(key)}]&"
129
+ else
130
+ buffer << "#{parent}[#{e.call(key)}]=#{e.call(value)}&"
131
+ end
132
+ end
133
+ end
134
+ buffer.chop
135
+ end
136
+
137
+ end
138
+ end
data/tapsilog.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{tapsilog}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Palmade"]
9
+ s.date = %q{2010-09-03}
10
+ s.default_executable = %q{tapsilog}
11
+ s.description = %q{Hydrid app-level logger from Palmade. Analogger fork.}
12
+ s.email = %q{}
13
+ s.executables = ["tapsilog"]
14
+ s.extra_rdoc_files = ["CHANGELOG", "README.md", "bin/tapsilog", "lib/palmade/tapsilog.rb", "lib/palmade/tapsilog/adapters.rb", "lib/palmade/tapsilog/adapters/base_adapter.rb", "lib/palmade/tapsilog/adapters/file_adapter.rb", "lib/palmade/tapsilog/adapters/mongo_adapter.rb", "lib/palmade/tapsilog/client.rb", "lib/palmade/tapsilog/conn.rb", "lib/palmade/tapsilog/logger.rb", "lib/palmade/tapsilog/protocol.rb", "lib/palmade/tapsilog/server.rb", "lib/palmade/tapsilog/utils.rb"]
15
+ s.files = ["CHANGELOG", "Manifest", "README.md", "Rakefile", "bin/tapsilog", "lib/palmade/tapsilog.rb", "lib/palmade/tapsilog/adapters.rb", "lib/palmade/tapsilog/adapters/base_adapter.rb", "lib/palmade/tapsilog/adapters/file_adapter.rb", "lib/palmade/tapsilog/adapters/mongo_adapter.rb", "lib/palmade/tapsilog/client.rb", "lib/palmade/tapsilog/conn.rb", "lib/palmade/tapsilog/logger.rb", "lib/palmade/tapsilog/protocol.rb", "lib/palmade/tapsilog/server.rb", "lib/palmade/tapsilog/utils.rb", "tapsilog.gemspec"]
16
+ s.homepage = %q{}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Tapsilog", "--main", "README.md"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{tapsilog}
20
+ s.rubygems_version = %q{1.3.7}
21
+ s.summary = %q{Hydrid app-level logger from Palmade. Analogger fork.}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
29
+ else
30
+ s.add_dependency(%q<eventmachine>, [">= 0"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<eventmachine>, [">= 0"])
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tapsilog
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Palmade
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-03 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: eventmachine
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Hydrid app-level logger from Palmade. Analogger fork.
34
+ email: ""
35
+ executables:
36
+ - tapsilog
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - CHANGELOG
41
+ - README.md
42
+ - bin/tapsilog
43
+ - lib/palmade/tapsilog.rb
44
+ - lib/palmade/tapsilog/adapters.rb
45
+ - lib/palmade/tapsilog/adapters/base_adapter.rb
46
+ - lib/palmade/tapsilog/adapters/file_adapter.rb
47
+ - lib/palmade/tapsilog/adapters/mongo_adapter.rb
48
+ - lib/palmade/tapsilog/client.rb
49
+ - lib/palmade/tapsilog/conn.rb
50
+ - lib/palmade/tapsilog/logger.rb
51
+ - lib/palmade/tapsilog/protocol.rb
52
+ - lib/palmade/tapsilog/server.rb
53
+ - lib/palmade/tapsilog/utils.rb
54
+ files:
55
+ - CHANGELOG
56
+ - Manifest
57
+ - README.md
58
+ - Rakefile
59
+ - bin/tapsilog
60
+ - lib/palmade/tapsilog.rb
61
+ - lib/palmade/tapsilog/adapters.rb
62
+ - lib/palmade/tapsilog/adapters/base_adapter.rb
63
+ - lib/palmade/tapsilog/adapters/file_adapter.rb
64
+ - lib/palmade/tapsilog/adapters/mongo_adapter.rb
65
+ - lib/palmade/tapsilog/client.rb
66
+ - lib/palmade/tapsilog/conn.rb
67
+ - lib/palmade/tapsilog/logger.rb
68
+ - lib/palmade/tapsilog/protocol.rb
69
+ - lib/palmade/tapsilog/server.rb
70
+ - lib/palmade/tapsilog/utils.rb
71
+ - tapsilog.gemspec
72
+ has_rdoc: true
73
+ homepage: ""
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options:
78
+ - --line-numbers
79
+ - --inline-source
80
+ - --title
81
+ - Tapsilog
82
+ - --main
83
+ - README.md
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ segments:
100
+ - 1
101
+ - 2
102
+ version: "1.2"
103
+ requirements: []
104
+
105
+ rubyforge_project: tapsilog
106
+ rubygems_version: 1.3.7
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Hydrid app-level logger from Palmade. Analogger fork.
110
+ test_files: []
111
+