tapsilog 0.1.0 → 0.2.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 +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
|