tapsilog 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -1
- data/INSTALL +60 -0
- data/Manifest +3 -0
- data/README.md +52 -20
- data/Rakefile +7 -1
- data/bin/tapsilog +79 -46
- data/bin/tapsilog_tail +147 -0
- data/lib/palmade/tapsilog/adapters.rb +1 -0
- data/lib/palmade/tapsilog/adapters/base_adapter.rb +3 -1
- data/lib/palmade/tapsilog/adapters/file_adapter.rb +23 -3
- data/lib/palmade/tapsilog/adapters/mongo_adapter.rb +22 -3
- data/lib/palmade/tapsilog/adapters/proxy_adapter.rb +42 -0
- data/lib/palmade/tapsilog/conn.rb +8 -7
- data/lib/palmade/tapsilog/protocol.rb +4 -0
- data/lib/palmade/tapsilog/server.rb +47 -40
- data/lib/palmade/tapsilog/utils.rb +74 -1
- data/tapsilog.gemspec +5 -6
- metadata +8 -6
data/CHANGELOG
CHANGED
data/INSTALL
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Swiftcore Analogger 0.5
|
2
|
+
|
3
|
+
Homepage:: http://analogger.swiftcore.org
|
4
|
+
Copyright:: (C) 2007 by Kirk Haines. All Rights Reserved.
|
5
|
+
Email:: wyhaines@gmail.com
|
6
|
+
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
To install analogger:
|
12
|
+
|
13
|
+
ruby setup.rb
|
14
|
+
|
15
|
+
The analogger executable will be installed into the ruby installation's
|
16
|
+
bindir, along with server and client libraries into the site_lib. The
|
17
|
+
rdoc documentation will also be generated.
|
18
|
+
|
19
|
+
ruby setup.rb --help
|
20
|
+
|
21
|
+
to se a full list of options.
|
22
|
+
|
23
|
+
|
24
|
+
Quickstart
|
25
|
+
----------
|
26
|
+
|
27
|
+
To start an Analogger instance, first create a configuration file:
|
28
|
+
|
29
|
+
port: 6766
|
30
|
+
host: 127.0.0.1
|
31
|
+
default_log: /var/log/weblogs/default
|
32
|
+
daemonize: true
|
33
|
+
syncinterval: 60
|
34
|
+
logs:
|
35
|
+
- service: bigapp
|
36
|
+
logfile: /var/log/bigapp
|
37
|
+
cull: true
|
38
|
+
- service:
|
39
|
+
- smallapp1
|
40
|
+
- smallapp2
|
41
|
+
logfile: /var/log/smallapps
|
42
|
+
cull: true
|
43
|
+
- service: newsletter_sender
|
44
|
+
logfile: /var/log/newsletter.log
|
45
|
+
cull: false
|
46
|
+
|
47
|
+
|
48
|
+
Then start the analogger:
|
49
|
+
|
50
|
+
/usr/bin/env analogger -c config_file
|
51
|
+
|
52
|
+
|
53
|
+
To use the client library to connect to an Analogger instance and send
|
54
|
+
logging messages to it:
|
55
|
+
|
56
|
+
require 'swiftcore/Analogger/Client'
|
57
|
+
|
58
|
+
logger = Swiftcdore::Analogger::Client.new('smallapp1','127.0.0.1','6766')
|
59
|
+
|
60
|
+
logger.log('info','This is a log message.')
|
data/Manifest
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
CHANGELOG
|
2
|
+
INSTALL
|
2
3
|
Manifest
|
3
4
|
README.md
|
4
5
|
Rakefile
|
5
6
|
bin/tapsilog
|
7
|
+
bin/tapsilog_tail
|
6
8
|
lib/palmade/tapsilog.rb
|
7
9
|
lib/palmade/tapsilog/adapters.rb
|
8
10
|
lib/palmade/tapsilog/adapters/base_adapter.rb
|
9
11
|
lib/palmade/tapsilog/adapters/file_adapter.rb
|
10
12
|
lib/palmade/tapsilog/adapters/mongo_adapter.rb
|
13
|
+
lib/palmade/tapsilog/adapters/proxy_adapter.rb
|
11
14
|
lib/palmade/tapsilog/client.rb
|
12
15
|
lib/palmade/tapsilog/conn.rb
|
13
16
|
lib/palmade/tapsilog/logger.rb
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# Tapsilog, an asynchronous logging service
|
2
2
|
|
3
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
4
|
Currently, Tapsilog supports files and mongodb as storage backend.
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
- file - Logs to files, STDOUT or STDERR
|
9
9
|
- mongo - Logs to mongoDB
|
10
|
+
- proxy - Forwards logs to another tapsilog server
|
10
11
|
|
11
12
|
**Gems required for mongoDB support**
|
12
13
|
|
@@ -14,48 +15,79 @@
|
|
14
15
|
- bson
|
15
16
|
- bson_ext
|
16
17
|
|
18
|
+
**Compatibility with analogger**
|
19
|
+
|
20
|
+
Tapsilog is mostly compatible with analogger client. Though there is a known quirk.
|
21
|
+
When using the analogger client, text after a colon will be interpreted as a tag.
|
22
|
+
Tapsilog URL encodes and decodes messages to circumvent this.
|
23
|
+
|
17
24
|
## Usage
|
18
25
|
|
19
26
|
**Tapsilog Server**
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
tapsilog -c /path/to/config_file.yml
|
28
|
+
See tapsilog --help for details
|
24
29
|
|
25
|
-
**Sample Config**
|
30
|
+
**Sample File/Mongo Config**
|
26
31
|
|
27
32
|
port: 19080
|
28
33
|
host:
|
29
34
|
- 127.0.0.1
|
30
35
|
socket:
|
31
36
|
- /tmp/tapsilog.sock
|
32
|
-
daemonize:
|
33
|
-
key:
|
37
|
+
daemonize: true
|
38
|
+
key: the_real_logger
|
34
39
|
|
35
40
|
syncinterval: 1
|
36
41
|
|
37
|
-
|
38
|
-
#
|
42
|
+
backend:
|
43
|
+
# Can be mongo or file
|
39
44
|
adapter: mongo
|
40
45
|
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
46
|
+
# Services not listed in logs section below will automatically be created under this collection (autocreate_namespace.service_name)
|
47
|
+
# If autocreate is off and an unknown service is requested, tapsilog uses the service named 'default'.
|
48
|
+
# If the service 'default' is not specified, tapsilog ignores the request
|
49
|
+
#
|
50
|
+
# If file adapter is used, this is used to specify the directory where log files named by the service name are created.
|
51
|
+
#autocreate: development
|
52
|
+
|
53
|
+
# You can leave these blank and tapsilog connects using mongodb connection defaults
|
44
54
|
#host: 127.0.0.1
|
45
55
|
#port: 1234
|
46
56
|
#user: root
|
47
57
|
#password: somepassword
|
48
|
-
database: tapsilog
|
58
|
+
#database: tapsilog
|
49
59
|
|
50
|
-
|
51
|
-
|
52
|
-
|
60
|
+
# For mongo adapter, target refers to the mongodb collection
|
61
|
+
# For file adapter, specify the path of the log file. You can also use stdout and stderr
|
62
|
+
logs:
|
63
|
+
- service: default
|
64
|
+
target: default
|
65
|
+
|
66
|
+
- service: access
|
67
|
+
target: access
|
68
|
+
|
69
|
+
- service: bizsupport
|
70
|
+
target: bizsupport
|
53
71
|
|
54
|
-
|
55
|
-
|
72
|
+
**Sample Proxy Config**
|
73
|
+
|
74
|
+
socket:
|
75
|
+
- /tmp/tapsilog_proxy.sock
|
76
|
+
daemonize: true
|
77
|
+
key: some_serious_key
|
78
|
+
|
79
|
+
syncinterval: 1
|
80
|
+
|
81
|
+
backend:
|
82
|
+
adapter: proxy
|
83
|
+
|
84
|
+
# You can connect to the destination tapsilog instance via tcpip or unix domain socket
|
85
|
+
#host: 127.0.0.1
|
86
|
+
#port: 19080
|
87
|
+
#socket: /tmp/tapsilog.sock
|
56
88
|
|
57
|
-
|
58
|
-
|
89
|
+
# Specify the authorization key of the tapsilog server to connect to
|
90
|
+
key: the_real_logger
|
59
91
|
|
60
92
|
**Tapsilog Client**
|
61
93
|
|
data/Rakefile
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'echoe'
|
2
2
|
|
3
|
-
Echoe.new('tapsilog', '0.
|
3
|
+
Echoe.new('tapsilog', '0.2.0') do |p|
|
4
4
|
p.author = "Palmade"
|
5
5
|
p.project = "tapsilog"
|
6
6
|
p.summary = "Hydrid app-level logger from Palmade. Analogger fork."
|
7
7
|
|
8
8
|
p.dependencies = ["eventmachine"]
|
9
9
|
p.ignore_pattern = ["tmp/*"]
|
10
|
+
|
11
|
+
p.need_tar_gz = false
|
12
|
+
p.need_tgz = true
|
13
|
+
|
14
|
+
p.clean_pattern += [ "pkg", "lib/*.bundle", "*.gem", ".config" ]
|
15
|
+
p.rdoc_pattern = [ 'README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc' ]
|
10
16
|
end
|
data/bin/tapsilog
CHANGED
@@ -2,68 +2,101 @@
|
|
2
2
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'optparse'
|
5
|
-
require '
|
5
|
+
require 'rubygems'
|
6
6
|
|
7
|
-
|
7
|
+
relative_tapsilog_path = File.expand_path(File.join(File.dirname(__FILE__), '../lib/palmade'))
|
8
|
+
if File.exists?(relative_tapsilog_path)
|
9
|
+
require File.join(relative_tapsilog_path, 'tapsilog')
|
10
|
+
else
|
11
|
+
gem 'tapsilog'
|
12
|
+
require 'palmade/tapsilog'
|
13
|
+
end
|
14
|
+
|
15
|
+
module Palmade::Tapsilog
|
8
16
|
class TapsilogExec
|
9
17
|
|
10
|
-
|
11
|
-
|
12
|
-
|
18
|
+
@@config = {}
|
19
|
+
|
20
|
+
def self.start
|
21
|
+
loop do
|
22
|
+
catch(:hup) {
|
23
|
+
if File.exists?(@@config[:configfile])
|
24
|
+
config = Utils.symbolize_keys(YAML.load(File.read(@@config[:configfile])))
|
25
|
+
@@config.merge!(config)
|
26
|
+
end
|
27
|
+
Palmade::Tapsilog::Server.start(@@config)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.stop
|
33
|
+
if Utils.pidf_running?(@@config[:pidfile])
|
34
|
+
pid = Utils.pidf_read(@@config[:pidfile])
|
35
|
+
puts "Sending QUIT to #{pid}"
|
36
|
+
Utils.pidf_kill(@@config[:pidfile])
|
37
|
+
else
|
38
|
+
puts "Tapsilog is not running"
|
39
|
+
Utils.pidf_clean(@@config[:pidfile])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.restart
|
44
|
+
stop
|
45
|
+
Utils.pidf_clean(@@config[:pidfile])
|
46
|
+
start
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.status
|
50
|
+
if Utils.pidf_running?(@@config[:pidfile])
|
51
|
+
pid = Utils.pidf_read(@@config[:pidfile])
|
52
|
+
puts "Tapsilog is running with pid #{pid}"
|
53
|
+
else
|
54
|
+
puts "Tapsilog is not running"
|
55
|
+
Utils.pidf_clean(@@config[:pidfile])
|
56
|
+
end
|
13
57
|
end
|
14
58
|
|
15
59
|
def self.parse_options(config = {})
|
60
|
+
option_parser.parse!
|
61
|
+
@@config
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.option_parser
|
16
65
|
OptionParser.new do |opts|
|
17
|
-
opts.banner = 'Usage: tapsilog [options]'
|
66
|
+
opts.banner = 'Usage: tapsilog [options] {start|stop|restart|status}'
|
67
|
+
|
18
68
|
opts.separator ''
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
69
|
+
|
70
|
+
@@config[:configfile] = "/etc/tapsilog.yml"
|
71
|
+
opts.on('-c', '--config CONFFILE', "The configuration file to read. (/etc/tapsilog.yml)") do |conf|
|
72
|
+
@@config[:configfile] = conf
|
36
73
|
end
|
37
|
-
|
38
|
-
|
74
|
+
|
75
|
+
@@config[:pidfile] = "/var/run/tapsilog.pid"
|
76
|
+
opts.on('-w', '--writepid FILENAME', "The filename to write a PID file to. (/var/run/tapsilog.pid)") do |pidfile|
|
77
|
+
@@config[:pidfile] = pidfile
|
39
78
|
end
|
40
|
-
|
41
|
-
|
79
|
+
|
80
|
+
opts.on('-h', '--help', "Show this message") do
|
81
|
+
puts opts
|
82
|
+
exit
|
42
83
|
end
|
43
|
-
opts.on('-
|
44
|
-
|
84
|
+
opts.on('-v', '--version', "Show version") do
|
85
|
+
puts 'tapsilog 0.2.0'
|
86
|
+
exit
|
45
87
|
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
88
|
|
53
|
-
|
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
|
-
}
|
89
|
+
end
|
60
90
|
end
|
61
91
|
|
62
92
|
end
|
63
93
|
end
|
64
94
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
95
|
+
config = Palmade::Tapsilog::TapsilogExec.parse_options
|
96
|
+
|
97
|
+
if ARGV.length == 0
|
98
|
+
puts Palmade::Tapsilog::TapsilogExec.option_parser.help
|
99
|
+
else
|
100
|
+
action = ARGV.shift
|
101
|
+
Palmade::Tapsilog::TapsilogExec.send(action)
|
69
102
|
end
|
data/bin/tapsilog_tail
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'optparse'
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
config = {}
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = 'Usage: tapsilog_tail [options] ServiceName'
|
11
|
+
|
12
|
+
opts.separator ''
|
13
|
+
|
14
|
+
config[:configfile] = "/etc/tapsilog.yml"
|
15
|
+
opts.on('-c', '--config CONFFILE', "The configuration file to read. (/etc/tapsilog.yml)") do |conf|
|
16
|
+
config[:configfile] = conf
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('-f', '--follow', "Output appended data as the file grows.") do
|
20
|
+
config[:follow] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
config[:lines] = 10
|
24
|
+
opts.on('-n', '--lines LINES', "Output the last N lines, instead of the last 10.") do |lines|
|
25
|
+
config[:lines] = lines
|
26
|
+
end
|
27
|
+
|
28
|
+
config[:sleep] = 1
|
29
|
+
opts.on('-s', '--sleep S', "With -f, sleep for approximately S seconds. (default 1.0)") do |sec|
|
30
|
+
config[:sleep] = sec
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-h', '--help', "Show this message") do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
opts.on('-v', '--version', "Show version") do
|
38
|
+
puts 'tapsilog_tail 0.2.0'
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
optparse.parse!
|
43
|
+
|
44
|
+
if ARGV.length == 0
|
45
|
+
puts optparse.help
|
46
|
+
else
|
47
|
+
tapsilog_config = YAML.load(File.read(config[:configfile]))
|
48
|
+
service = ARGV.shift
|
49
|
+
|
50
|
+
tapsilog_config['backend'] ||= {}
|
51
|
+
tapsilog_backend = tapsilog_config['backend']
|
52
|
+
tapsilog_config['logs'] ||= []
|
53
|
+
adapter = tapsilog_config['backend']['adapter'] || 'file'
|
54
|
+
|
55
|
+
if tapsilog_config['default_log']
|
56
|
+
tapsilog_config['logs'].push({'service' => 'default', 'target' => tapsilog_config['default_log']})
|
57
|
+
end
|
58
|
+
|
59
|
+
services = {}
|
60
|
+
tapsilog_config['logs'].each do |srv|
|
61
|
+
service_name = srv['service']
|
62
|
+
target = srv['target'] || srv['logfile']
|
63
|
+
|
64
|
+
services[service_name] = target
|
65
|
+
end
|
66
|
+
|
67
|
+
if adapter == 'file'
|
68
|
+
command = "tail"
|
69
|
+
command += " -f " if config[:follow]
|
70
|
+
command += " -n #{config[:lines]} " if config[:lines]
|
71
|
+
|
72
|
+
if services[service]
|
73
|
+
tail_target = services[service]
|
74
|
+
else
|
75
|
+
if autocreate = tapsilog_config['autocreate'] || tapsilog_backend['autocreate']
|
76
|
+
tail_target = File.join(autocreate, service)
|
77
|
+
else
|
78
|
+
tail_target = services['default']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if tail_target.nil?
|
83
|
+
puts "Uknown service #{service}!"
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
if tail_target == "stdout" or tail_target == "stderr"
|
88
|
+
puts "Cannot tail #{tail_target}!"
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
|
92
|
+
command += " #{tail_target} "
|
93
|
+
system command
|
94
|
+
elsif adapter == 'mongo'
|
95
|
+
require 'mongo'
|
96
|
+
|
97
|
+
mongo_conn = Mongo::Connection.new(tapsilog_backend['host'], tapsilog_backend['port'])
|
98
|
+
db_name = tapsilog_backend['database'] || 'tapsilog'
|
99
|
+
|
100
|
+
db = mongo_conn.db(db_name)
|
101
|
+
if tapsilog_backend['user'] and tapsilog_backend['password']
|
102
|
+
db.authenticate(tapsilog_backend['user'], tapsilog_backend['password'])
|
103
|
+
end
|
104
|
+
|
105
|
+
if services[service]
|
106
|
+
tail_target = services[service]
|
107
|
+
else
|
108
|
+
if autocreate = tapsilog_config['autocreate'] || tapsilog_backend['autocreate']
|
109
|
+
tail_target = "#{autocreate}.#{service}"
|
110
|
+
else
|
111
|
+
tail_target = services['default']
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
if tail_target.nil?
|
116
|
+
puts "Unknown service #{service}!"
|
117
|
+
exit
|
118
|
+
end
|
119
|
+
|
120
|
+
last_id = nil
|
121
|
+
print_entries = lambda do |log_entries|
|
122
|
+
log_entries.each do |log|
|
123
|
+
last_id = log['_id']
|
124
|
+
|
125
|
+
message = [log['timestamp'], log['service'], log['pid'], log['message']]
|
126
|
+
message.push(log['tags'].inspect) unless log['tags'].nil?
|
127
|
+
puts message.join("|")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
last_entries = db[tail_target].find({}, :sort => ['_id', :desc], :limit => config[:lines].to_i).to_a.reverse
|
132
|
+
print_entries.call(last_entries)
|
133
|
+
|
134
|
+
if config[:follow]
|
135
|
+
trap("INT") { exit }
|
136
|
+
|
137
|
+
loop do
|
138
|
+
query = last_id ? {'_id' => {'$gt' => last_id}} : {}
|
139
|
+
new_entries = db[tail_target].find(query).to_a
|
140
|
+
print_entries.call(new_entries)
|
141
|
+
|
142
|
+
sleep(config[:sleep].to_i)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
@@ -4,6 +4,7 @@ module Palmade::Tapsilog
|
|
4
4
|
autoload :BaseAdapter, File.join(File.dirname(__FILE__), 'adapters/base_adapter')
|
5
5
|
autoload :FileAdapter, File.join(File.dirname(__FILE__), 'adapters/file_adapter')
|
6
6
|
autoload :MongoAdapter, File.join(File.dirname(__FILE__), 'adapters/mongo_adapter')
|
7
|
+
autoload :ProxyAdapter, File.join(File.dirname(__FILE__), 'adapters/proxy_adapter')
|
7
8
|
|
8
9
|
end
|
9
10
|
end
|
@@ -22,8 +22,10 @@ module Palmade::Tapsilog::Adapters
|
|
22
22
|
|
23
23
|
@config[:services].each do |service|
|
24
24
|
service_name = service['service']
|
25
|
+
target = service['target'] || service['logfile']
|
26
|
+
|
25
27
|
@services[service_name] = {
|
26
|
-
:target =>
|
28
|
+
:target => target
|
27
29
|
}
|
28
30
|
end
|
29
31
|
end
|
@@ -3,8 +3,14 @@ module Palmade::Tapsilog::Adapters
|
|
3
3
|
|
4
4
|
def write(log_message)
|
5
5
|
service = log_message[1]
|
6
|
+
log_message[5] = Palmade::Tapsilog::Utils.hash_to_query_string(log_message[5])
|
7
|
+
|
6
8
|
file = get_file_descriptor(service)
|
7
|
-
file
|
9
|
+
if file
|
10
|
+
file.puts(log_message.join("|"))
|
11
|
+
else
|
12
|
+
STDERR.puts "Unknown service: #{service}"
|
13
|
+
end
|
8
14
|
end
|
9
15
|
|
10
16
|
def flush
|
@@ -28,16 +34,30 @@ module Palmade::Tapsilog::Adapters
|
|
28
34
|
protected
|
29
35
|
|
30
36
|
def get_file_descriptor(service_name)
|
31
|
-
service_name = (
|
37
|
+
service_name = resolve_service_name(service_name)
|
32
38
|
service = @services[service_name]
|
33
39
|
|
40
|
+
return nil if service.nil?
|
41
|
+
|
34
42
|
if service[:file].nil?
|
35
43
|
service[:file] = open_file_descriptor(service)
|
36
44
|
end
|
37
|
-
|
38
45
|
service[:file]
|
39
46
|
end
|
40
47
|
|
48
|
+
def resolve_service_name(service_name)
|
49
|
+
if @services[service_name].nil?
|
50
|
+
if @config[:autocreate]
|
51
|
+
@services[service_name] = {
|
52
|
+
:target => File.join(@config[:autocreate], service_name)
|
53
|
+
}
|
54
|
+
else
|
55
|
+
return 'default'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
service_name
|
59
|
+
end
|
60
|
+
|
41
61
|
def open_file_descriptor(service)
|
42
62
|
logfile = service[:target]
|
43
63
|
|
@@ -7,7 +7,11 @@ module Palmade::Tapsilog::Adapters
|
|
7
7
|
service = log_message[1]
|
8
8
|
coll = get_collection(service)
|
9
9
|
|
10
|
-
coll
|
10
|
+
if coll
|
11
|
+
coll.insert(log_to_hash(log_message))
|
12
|
+
else
|
13
|
+
STDERR.puts "Unknown service: #{service}"
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
def close
|
@@ -19,12 +23,27 @@ module Palmade::Tapsilog::Adapters
|
|
19
23
|
protected
|
20
24
|
|
21
25
|
def get_collection(service_name)
|
22
|
-
service_name = (
|
26
|
+
service_name = resolve_service_name(service_name)
|
23
27
|
service = @services[service_name]
|
24
28
|
|
29
|
+
return nil if service.nil?
|
30
|
+
|
25
31
|
db_conn[service[:target]]
|
26
32
|
end
|
27
33
|
|
34
|
+
def resolve_service_name(service_name)
|
35
|
+
if @services[service_name].nil?
|
36
|
+
if @config[:autocreate]
|
37
|
+
@services[service_name] = {
|
38
|
+
:target => "#{@config[:autocreate]}.#{service_name}"
|
39
|
+
}
|
40
|
+
else
|
41
|
+
return 'default'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
service_name
|
45
|
+
end
|
46
|
+
|
28
47
|
def log_to_hash(log_message)
|
29
48
|
timestamp, service, pid, severity, message, tags = log_message
|
30
49
|
log_hash = {
|
@@ -36,7 +55,7 @@ module Palmade::Tapsilog::Adapters
|
|
36
55
|
:created_at => Time.now
|
37
56
|
}
|
38
57
|
unless tags.nil? or tags.empty?
|
39
|
-
log_hash[:tags] =
|
58
|
+
log_hash[:tags] = tags
|
40
59
|
end
|
41
60
|
|
42
61
|
log_hash
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Palmade::Tapsilog::Adapters
|
2
|
+
class ProxyAdapter < BaseAdapter
|
3
|
+
|
4
|
+
def initialize(config)
|
5
|
+
super(config)
|
6
|
+
end
|
7
|
+
|
8
|
+
def write(log_message)
|
9
|
+
service = log_message[1]
|
10
|
+
instance_key = log_message[2]
|
11
|
+
severity = log_message[3]
|
12
|
+
message = log_message[4]
|
13
|
+
tags = log_message[5]
|
14
|
+
|
15
|
+
conn.log(service, instance_key, severity, message, tags)
|
16
|
+
end
|
17
|
+
|
18
|
+
def flush
|
19
|
+
conn.flush
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
conn.close
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def conn
|
29
|
+
if @conn.nil?
|
30
|
+
if @config[:socket]
|
31
|
+
target = @config[:socket]
|
32
|
+
else
|
33
|
+
target = "#{@config[:host]}:#{@config[:port]}"
|
34
|
+
end
|
35
|
+
@conn = Palmade::Tapsilog::Conn.new(target, @config[:key])
|
36
|
+
@conn.max_tries = -1
|
37
|
+
end
|
38
|
+
@conn
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -3,8 +3,7 @@ require 'cgi'
|
|
3
3
|
module Palmade::Tapsilog
|
4
4
|
class Conn
|
5
5
|
attr_reader :socket
|
6
|
-
|
7
|
-
MAX_TRIES = 6
|
6
|
+
attr_accessor :max_tries
|
8
7
|
|
9
8
|
def initialize(target, key, buffered = false)
|
10
9
|
if target =~ /(.+)\:(\d+)$/i
|
@@ -17,6 +16,7 @@ module Palmade::Tapsilog
|
|
17
16
|
@key = key
|
18
17
|
@socket = nil
|
19
18
|
@buffered = buffered
|
19
|
+
@max_tries = 6
|
20
20
|
end
|
21
21
|
|
22
22
|
def connect(host, port)
|
@@ -33,7 +33,8 @@ module Palmade::Tapsilog
|
|
33
33
|
connect(@host, @port)
|
34
34
|
|
35
35
|
ts = Time.now if ts.nil?
|
36
|
-
tag_string = Utils
|
36
|
+
tag_string = Utils.hash_to_query_string(tags)
|
37
|
+
message = CGI.escape(message)
|
37
38
|
|
38
39
|
fullmsg = ":#{service}:#{instance_key}:#{severity}:#{message}:#{tag_string}"
|
39
40
|
|
@@ -47,9 +48,9 @@ module Palmade::Tapsilog
|
|
47
48
|
write "#{len}#{len}#{fullmsg}"
|
48
49
|
rescue Exception => e
|
49
50
|
STDERR.puts "Failed to write to server! Retrying... (#{tries})" +
|
50
|
-
"#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}"
|
51
|
+
"#{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}" unless @max_tries == -1
|
51
52
|
|
52
|
-
if tries <
|
53
|
+
if @max_tries == -1 || tries < @max_tries
|
53
54
|
tries += 1
|
54
55
|
close_possibly_dead_conn(tries)
|
55
56
|
reconnect
|
@@ -94,9 +95,9 @@ module Palmade::Tapsilog
|
|
94
95
|
raise "Unable to create socket!" if @socket.nil?
|
95
96
|
rescue Exception => e
|
96
97
|
STDERR.puts "Failed to establish connection with server! Retrying... (#{tries})" +
|
97
|
-
" #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}"
|
98
|
+
" #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}" unless @max_tries == -1
|
98
99
|
|
99
|
-
if tries <
|
100
|
+
if @max_tries == -1 || tries < @max_tries
|
100
101
|
tries += 1
|
101
102
|
close_possibly_dead_conn(tries)
|
102
103
|
retry
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
1
3
|
module Palmade::Tapsilog
|
2
4
|
class Protocol < EventMachine::Connection
|
3
5
|
Ci = 'i'.freeze
|
@@ -64,6 +66,8 @@ module Palmade::Tapsilog
|
|
64
66
|
msg.shift
|
65
67
|
|
66
68
|
msg[0] = msg[0].to_s.gsub(/[^a-zA-Z0-9\-\_\.]\s/, '').strip
|
69
|
+
msg[3] = CGI.unescape(msg[3].to_s)
|
70
|
+
msg[4] = Utils::query_string_to_hash(msg[4].to_s)
|
67
71
|
|
68
72
|
LoggerClass.add_log(msg)
|
69
73
|
@length = nil
|
@@ -25,31 +25,54 @@ module Palmade::Tapsilog
|
|
25
25
|
protected
|
26
26
|
|
27
27
|
def self.boot
|
28
|
-
write_pid_file if @config[:pidfile]
|
29
|
-
daemonize if @config[:daemonize]
|
30
|
-
|
31
28
|
load_adapter
|
32
29
|
|
33
30
|
trap("INT") { exit }
|
34
31
|
trap("TERM") { exit }
|
35
32
|
trap("HUP") { throw :hup }
|
36
33
|
|
34
|
+
prepare_server
|
35
|
+
daemonize if @config[:daemonize]
|
36
|
+
write_pid_file if @config[:pidfile]
|
37
37
|
start_server
|
38
38
|
end
|
39
39
|
|
40
|
-
def self.
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
def self.load_adapter
|
41
|
+
@config[:backend] ||= {}
|
42
|
+
|
43
|
+
adapter_name = @config[:backend][:adapter] || "file"
|
44
|
+
class_name = "#{adapter_name.capitalize}Adapter"
|
45
|
+
adapter = Palmade::Tapsilog::Adapters.const_get(class_name)
|
46
|
+
|
47
|
+
adapter_config = @config[:backend]
|
48
|
+
adapter_config[:autocreate] = @config[:autocreate] if @config[:autocreate]
|
49
|
+
adapter_config[:services] = @config[:logs] || []
|
50
|
+
|
51
|
+
if @config[:default_log]
|
52
|
+
adapter_config[:services].push({'service' => 'default', 'target' => @config[:default_log]})
|
53
|
+
end
|
54
|
+
|
55
|
+
@adapter = adapter.new(adapter_config)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.prepare_server
|
59
|
+
if @config[:socket]
|
60
|
+
@usocks = @config[:socket]
|
61
|
+
@usocks = [ @usocks ] unless @usocks.is_a? Array
|
62
|
+
|
63
|
+
@usocks.each do |usock|
|
64
|
+
raise "Socket file already exists! (#{usock})" if File.exists?(usock)
|
49
65
|
end
|
50
66
|
end
|
51
67
|
|
52
|
-
|
68
|
+
if @config[:host]
|
69
|
+
@tsocks = @config[:host]
|
70
|
+
@tsocks = [ @tsocks ] unless @tsocks.is_a? Array
|
71
|
+
|
72
|
+
@tsocks.each do |tsock|
|
73
|
+
raise "Port already in use! #{tsock}:#{@config[:port]}" if Utils.is_port_open?(tsock, @config[:port])
|
74
|
+
end
|
75
|
+
end
|
53
76
|
end
|
54
77
|
|
55
78
|
def self.daemonize
|
@@ -63,19 +86,23 @@ module Palmade::Tapsilog
|
|
63
86
|
puts "Platform (#{RUBY_PLATFORM}) does not appear to support fork/setsid; skipping"
|
64
87
|
end
|
65
88
|
|
66
|
-
def self.
|
67
|
-
|
89
|
+
def self.write_pid_file
|
90
|
+
if pid = Utils.pidf_read(@config[:pidfile])
|
91
|
+
if Utils.process_running?(pid)
|
92
|
+
raise "Another instance of tapsilog seems to be running! (#{pid})"
|
93
|
+
else
|
94
|
+
Utils.pidf_clean(@config[:pidfile])
|
95
|
+
STDERR.puts "Stale PID (#{pid}) removed"
|
96
|
+
end
|
97
|
+
end
|
68
98
|
|
69
|
-
|
70
|
-
class_name = "#{adapter_name.capitalize}Adapter"
|
71
|
-
adapter = Palmade::Tapsilog::Adapters.const_get(class_name)
|
72
|
-
@adapter = adapter.new(@config[:logs])
|
99
|
+
File.open(@config[:pidfile],'w+') {|fh| fh.puts $$}
|
73
100
|
end
|
74
101
|
|
75
102
|
def self.start_server
|
76
|
-
prepare_server
|
77
103
|
begin
|
78
104
|
EventMachine.run {
|
105
|
+
update_now
|
79
106
|
start_servers
|
80
107
|
EventMachine.add_periodic_timer(1) { update_now }
|
81
108
|
EventMachine.add_periodic_timer(@config[:interval]) { write_queue }
|
@@ -86,26 +113,6 @@ module Palmade::Tapsilog
|
|
86
113
|
end
|
87
114
|
end
|
88
115
|
|
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
116
|
def self.start_servers
|
110
117
|
@usocks.each do |usock|
|
111
118
|
raise "Socket file already exists! (#{usock})" if File.exists?(usock)
|
@@ -2,7 +2,16 @@ require 'cgi'
|
|
2
2
|
|
3
3
|
module Palmade::Tapsilog
|
4
4
|
class Utils
|
5
|
-
|
5
|
+
|
6
|
+
def self.symbolize_keys(hash)
|
7
|
+
hash.inject({}){|result, (key, value)|
|
8
|
+
new_key = key.kind_of?(String) ? key.to_sym : key
|
9
|
+
new_value = value.kind_of?(Hash) ? symbolize_keys(value) : value
|
10
|
+
result[new_key] = new_value
|
11
|
+
result
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
6
15
|
def self.is_port_open?(ip, port)
|
7
16
|
begin
|
8
17
|
::Timeout::timeout(1) do
|
@@ -20,6 +29,70 @@ module Palmade::Tapsilog
|
|
20
29
|
return false
|
21
30
|
end
|
22
31
|
|
32
|
+
def self.process_running?(pid)
|
33
|
+
Process.getpgid(pid) != -1
|
34
|
+
rescue Errno::ESRCH
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.pidf_running?(pid_file)
|
39
|
+
if pid = pidf_read(pid_file)
|
40
|
+
process_running?(pid) ? pid : false
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.pidf_read(pid_file)
|
47
|
+
if File.exists?(pid_file) && File.file?(pid_file) && pid = File.read(pid_file)
|
48
|
+
pid.to_i
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.pidf_kill(pid_file, timeout = 30)
|
55
|
+
if timeout == 0
|
56
|
+
pidf_send_signal('INT', pid_file, timeout)
|
57
|
+
else
|
58
|
+
pidf_send_signal('QUIT', pid_file, timeout)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.pidf_send_signal(signal, pid_file, timeout = 30)
|
63
|
+
if pid = pidf_read(pid_file)
|
64
|
+
Process.kill(signal, pid)
|
65
|
+
::Timeout.timeout(timeout) do
|
66
|
+
sleep 0.1 while process_running?(pid)
|
67
|
+
end
|
68
|
+
pid
|
69
|
+
else
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
rescue ::Timeout::Error
|
73
|
+
pidf_force_kill pid_file
|
74
|
+
rescue Interrupt
|
75
|
+
pidf_force_kill pid_file
|
76
|
+
rescue Errno::ESRCH # No such process
|
77
|
+
pidf_force_kill pid_file
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.pidf_force_kill(pid_file)
|
81
|
+
if pid = pidf_read(pid_file)
|
82
|
+
Process.kill("KILL", pid)
|
83
|
+
File.delete(pid_file) if File.exist?(pid_file)
|
84
|
+
pid
|
85
|
+
else
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.pidf_clean(pid_file)
|
91
|
+
unless pidf_running?(pid_file)
|
92
|
+
File.delete(pid_file) if File.exists?(pid_file)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
23
96
|
# Taken from github accumulator/uri
|
24
97
|
|
25
98
|
def self.query_string_to_hash(query_string, options={})
|
data/tapsilog.gemspec
CHANGED
@@ -2,17 +2,16 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{tapsilog}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.2.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Palmade"]
|
9
|
-
s.date = %q{2010-09-
|
10
|
-
s.default_executable = %q{tapsilog}
|
9
|
+
s.date = %q{2010-09-08}
|
11
10
|
s.description = %q{Hydrid app-level logger from Palmade. Analogger fork.}
|
12
11
|
s.email = %q{}
|
13
|
-
s.executables = ["tapsilog"]
|
14
|
-
s.extra_rdoc_files = ["
|
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"]
|
12
|
+
s.executables = ["tapsilog", "tapsilog_tail"]
|
13
|
+
s.extra_rdoc_files = ["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/adapters/proxy_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"]
|
14
|
+
s.files = ["CHANGELOG", "INSTALL", "Manifest", "README.md", "Rakefile", "bin/tapsilog", "bin/tapsilog_tail", "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/adapters/proxy_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
15
|
s.homepage = %q{}
|
17
16
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Tapsilog", "--main", "README.md"]
|
18
17
|
s.require_paths = ["lib"]
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Palmade
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-09-
|
17
|
+
date: 2010-09-08 00:00:00 +08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -34,17 +34,16 @@ description: Hydrid app-level logger from Palmade. Analogger fork.
|
|
34
34
|
email: ""
|
35
35
|
executables:
|
36
36
|
- tapsilog
|
37
|
+
- tapsilog_tail
|
37
38
|
extensions: []
|
38
39
|
|
39
40
|
extra_rdoc_files:
|
40
|
-
- CHANGELOG
|
41
|
-
- README.md
|
42
|
-
- bin/tapsilog
|
43
41
|
- lib/palmade/tapsilog.rb
|
44
42
|
- lib/palmade/tapsilog/adapters.rb
|
45
43
|
- lib/palmade/tapsilog/adapters/base_adapter.rb
|
46
44
|
- lib/palmade/tapsilog/adapters/file_adapter.rb
|
47
45
|
- lib/palmade/tapsilog/adapters/mongo_adapter.rb
|
46
|
+
- lib/palmade/tapsilog/adapters/proxy_adapter.rb
|
48
47
|
- lib/palmade/tapsilog/client.rb
|
49
48
|
- lib/palmade/tapsilog/conn.rb
|
50
49
|
- lib/palmade/tapsilog/logger.rb
|
@@ -53,15 +52,18 @@ extra_rdoc_files:
|
|
53
52
|
- lib/palmade/tapsilog/utils.rb
|
54
53
|
files:
|
55
54
|
- CHANGELOG
|
55
|
+
- INSTALL
|
56
56
|
- Manifest
|
57
57
|
- README.md
|
58
58
|
- Rakefile
|
59
59
|
- bin/tapsilog
|
60
|
+
- bin/tapsilog_tail
|
60
61
|
- lib/palmade/tapsilog.rb
|
61
62
|
- lib/palmade/tapsilog/adapters.rb
|
62
63
|
- lib/palmade/tapsilog/adapters/base_adapter.rb
|
63
64
|
- lib/palmade/tapsilog/adapters/file_adapter.rb
|
64
65
|
- lib/palmade/tapsilog/adapters/mongo_adapter.rb
|
66
|
+
- lib/palmade/tapsilog/adapters/proxy_adapter.rb
|
65
67
|
- lib/palmade/tapsilog/client.rb
|
66
68
|
- lib/palmade/tapsilog/conn.rb
|
67
69
|
- lib/palmade/tapsilog/logger.rb
|