analogger 0.5.0 → 0.9.1
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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +24 -0
- data/INSTALL +1 -1
- data/Rakefile +11 -0
- data/analogger.gemspec +37 -35
- data/bin/analogger +66 -60
- data/external/package.rb +12 -11
- data/external/test_support.rb +3 -6
- data/lib/swiftcore/Analogger.rb +308 -0
- data/{src/swiftcore/Analogger.rb → lib/swiftcore/Analogger.rb.orig} +64 -66
- data/lib/swiftcore/Analogger/AnaloggerProtocol.rb +78 -0
- data/lib/swiftcore/Analogger/Client.rb +406 -0
- data/lib/swiftcore/Analogger/EMClient.rb +100 -0
- data/lib/swiftcore/Analogger/version.rb +5 -0
- data/lib/swiftcore/LoggerInterface.rb +316 -0
- data/setup.rb +11 -10
- metadata +117 -70
- data/src/swiftcore/Analogger/Client.rb +0 -103
- data/test/TC_Analogger.rb +0 -151
- data/test/analogger.cnf +0 -22
- data/test/analogger2.cnf +0 -9
- data/test/tc_template.rb +0 -15
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b7e212d5f72a1ed3e46b15cc9967b0b4558b22808215e3a7b3c213fb7ad8aa83
|
4
|
+
data.tar.gz: 41937ddf5b49f6eb741ded33748ea90ecb82139846fc4b002f90495b99b19cb6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5edca0ff4568f72164b08e9b2ff87415f56edeadbb308308ef9cc882902e6f1c03ed2a13fd0f0059b69d6f84d0d35a52032af6858c3c6ff4df3f731b8e632555
|
7
|
+
data.tar.gz: 1339888e798a75186b11d8aa27db42ef6ab97b133dc9b4e2c24dcbc486e2f707fe3a762197d6f412654261ce0bdc7b365ef60849fc8463fb2f590054115887bc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
analogger (0.9.0)
|
5
|
+
eventmachine (~> 1.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
eventmachine (1.2.5)
|
11
|
+
minitest (5.11.1)
|
12
|
+
rake (11.3.0)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
analogger!
|
19
|
+
bundler (~> 1.10)
|
20
|
+
minitest (~> 5)
|
21
|
+
rake (~> 11.0)
|
22
|
+
|
23
|
+
BUNDLED WITH
|
24
|
+
1.16.1
|
data/INSTALL
CHANGED
@@ -55,6 +55,6 @@ logging messages to it:
|
|
55
55
|
|
56
56
|
require 'swiftcore/Analogger/Client'
|
57
57
|
|
58
|
-
logger =
|
58
|
+
logger = Swiftcore::Analogger::Client.new('smallapp1','127.0.0.1','6766')
|
59
59
|
|
60
60
|
logger.log('info','This is a log message.')
|
data/Rakefile
ADDED
data/analogger.gemspec
CHANGED
@@ -1,41 +1,43 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
# Licensed under the Ruby License. See the README for details.
|
7
|
-
#
|
8
|
-
#####
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'swiftcore/Analogger/version'
|
9
5
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
s.summary = %q(A fast asynchronous logging service and client for Ruby.)
|
16
|
-
s.platform = Gem::Platform::RUBY
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "analogger"
|
8
|
+
spec.version = Swiftcore::Analogger::VERSION
|
9
|
+
spec.authors = ["Kirk Haines"]
|
10
|
+
spec.email = ["wyhaines@gmail.com"]
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
spec.summary = %q{Analogger is a fast, stable, simple central asynchronous logging service/client. }
|
13
|
+
spec.description = %q{Analogger provides a fast and very stable asynchronous central logging service capable of handling heavy logging loads. It has been in production use for almost a decade.}
|
14
|
+
spec.homepage = "https://github.com/wyhaines/analogger"
|
15
|
+
spec.license = "MIT"
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
spec.metadata = {
|
18
|
+
"bug_tracker_uri" => "https://github.com/wyhaines/analogger/issues",
|
19
|
+
"documentation_uri" => "https://github.com/wyhaines/analogger",
|
20
|
+
"homepage_uri" => "https://github.com/wyhaines/analogger",
|
21
|
+
"source_code_uri" => "https://github.com/wyhaines/analogger",
|
22
|
+
}
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
description = []
|
33
|
-
File.open("README") do |file|
|
34
|
-
file.each do |line|
|
35
|
-
line.chomp!
|
36
|
-
break if line.empty?
|
37
|
-
description << "#{line.gsub(/\[\d\]/, '')}"
|
38
|
-
end
|
24
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
25
|
+
# delete this section to allow pushing this gem to any host.
|
26
|
+
if spec.respond_to?(:metadata)
|
27
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
28
|
+
else
|
29
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
39
30
|
end
|
40
|
-
|
31
|
+
|
32
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
33
|
+
spec.bindir = "bin"
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
35
|
+
spec.extensions = %w[]
|
36
|
+
spec.require_paths = ["lib"]
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
39
|
+
spec.add_development_dependency "rake", "~> 11.0"
|
40
|
+
spec.add_development_dependency "minitest", "~> 5"
|
41
|
+
spec.add_runtime_dependency "eventmachine", "~> 1.2"
|
41
42
|
end
|
43
|
+
|
data/bin/analogger
CHANGED
@@ -3,15 +3,16 @@
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'optparse'
|
5
5
|
begin
|
6
|
-
|
7
|
-
|
6
|
+
load_attempted ||= false
|
7
|
+
require 'swiftcore/Analogger'
|
8
8
|
rescue LoadError => e
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
unless load_attempted
|
10
|
+
load_attempted = true
|
11
|
+
require 'rubygems'
|
12
|
+
require 'bundler/setup'
|
13
|
+
retry
|
14
|
+
end
|
15
|
+
raise e
|
15
16
|
end
|
16
17
|
|
17
18
|
#####
|
@@ -31,64 +32,69 @@ end
|
|
31
32
|
#####
|
32
33
|
|
33
34
|
module Swiftcore
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
35
|
+
class AnaloggerExec
|
36
|
+
def self.parse_options(config = {})
|
37
|
+
# Config file to read
|
38
|
+
#
|
39
|
+
# port: 12345
|
40
|
+
# secret: abcdef
|
41
|
+
# interval: 1
|
42
|
+
# default_log: /var/log/emlogger
|
43
|
+
# logs:
|
44
|
+
# - service: client1
|
45
|
+
# levels: info
|
46
|
+
# logfile: /foo/bar.txt
|
47
|
+
# cull: true
|
48
|
+
#
|
49
|
+
OptionParser.new do |opts|
|
50
|
+
opts.banner = "Analogger v#{Swiftcore::Analogger::VERSION}\nUsage: analogger.rb [options]"
|
51
|
+
opts.separator ''
|
52
|
+
opts.on('-c', '--config CONFFILE', "The configuration file to read.") do |conf|
|
53
|
+
config = YAML.load(File.read(conf))
|
54
|
+
end
|
55
|
+
opts.on('-p', '--port [PORT]', Integer, "The port to receive connections on.") do |port|
|
56
|
+
config[Swiftcore::Analogger::Cport] = port
|
57
|
+
end
|
58
|
+
opts.on('-h', '--host [HOST]', String, "The host to bind the connection to.") do |host|
|
59
|
+
config[Swiftcore::Analogger::Chost] = host
|
60
|
+
end
|
60
61
|
# opts.on('-r','--controlkey [KEY]',String,"The secret key that authenticates a control session.") do |secret|
|
61
62
|
# config[Swiftcore::Analogger::Csecret] = secret
|
62
63
|
# end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
64
|
+
opts.on('-k', '--key [KEY]', String, "The secret key that authenticates a valid client session.") do |secret|
|
65
|
+
config[Swiftcore::Analogger::Ckey] = secret
|
66
|
+
end
|
67
|
+
opts.on('-i', '--interval [INTERVAL]', Integer, "The interval between queue writes. Defaults to 1 second.") do |interval|
|
68
|
+
config[Swiftcore::Analogger::Cinterval] = interval
|
69
|
+
end
|
70
|
+
opts.on('-s', '--syncinterval [INTERVAL]', Integer, "The interval between queue syncs. Defaults to 60 seconds.") do |interval|
|
71
|
+
config[Swiftcore::Analogger::Csyncinterval] = interval
|
72
|
+
end
|
73
|
+
opts.on('-d', '--default [PATH]', String, "The default log destination. Defaults to stdout.") do |default|
|
74
|
+
config[Swiftcore::Analogger::Cdefault_log] = default
|
75
|
+
end
|
76
|
+
opts.on('-x', '--daemonize', "Tell the Analogger to daemonize itself.") do
|
77
|
+
config[Swiftcore::Analogger::Cdaemonize] = true
|
78
|
+
end
|
79
|
+
opts.on('-w', '--writepid [FILENAME]', "The filename to write a PID file to.") do |pidfile|
|
80
|
+
config[Swiftcore::Analogger::Cpidfile] = pidfile || 'analogger.pid'
|
81
|
+
end
|
82
|
+
opts.on('-v', '--version', "Show the current version of Analogger.") do
|
83
|
+
puts "Analogger v#{Swiftcore::Analogger::VERSION}"
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
end.parse!
|
87
|
+
config
|
88
|
+
end
|
84
89
|
|
85
|
-
|
86
|
-
|
87
|
-
|
90
|
+
def self.run
|
91
|
+
@@parsed_options ||= parse_options
|
92
|
+
Swiftcore::Analogger.start(@@parsed_options)
|
93
|
+
end
|
88
94
|
|
89
|
-
|
95
|
+
end
|
90
96
|
end
|
91
97
|
|
92
98
|
loop do
|
93
|
-
|
99
|
+
catch(:hup) {Swiftcore::AnaloggerExec.run}
|
94
100
|
end
|
data/external/package.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'rbconfig'
|
2
3
|
require 'fileutils'
|
3
4
|
require 'optparse'
|
@@ -36,9 +37,9 @@ end
|
|
36
37
|
|
37
38
|
def self.config(name)
|
38
39
|
# XXX use pathname
|
39
|
-
prefix = Regexp.quote(
|
40
|
-
exec_prefix = Regexp.quote(
|
41
|
-
|
40
|
+
prefix = Regexp.quote(RbConfig::CONFIG["prefix"])
|
41
|
+
exec_prefix = Regexp.quote(RbConfig::CONFIG["exec_prefix"])
|
42
|
+
RbConfig::CONFIG[name].gsub(/\A\/?(#{prefix}|#{exec_prefix})\/?/, '')
|
42
43
|
end
|
43
44
|
|
44
45
|
SITE_DIRS = {
|
@@ -70,7 +71,7 @@ MODES = {
|
|
70
71
|
|
71
72
|
|
72
73
|
SETUP_OPTIONS = {:parse_cmdline => true, :load_conf => true, :run_tasks => true}
|
73
|
-
RUBY_BIN = File.join(::
|
74
|
+
RUBY_BIN = File.join(::RbConfig::CONFIG['bindir'],::RbConfig::CONFIG['ruby_install_name']) << ::RbConfig::CONFIG['EXEEXT']
|
74
75
|
|
75
76
|
def self.setup(version, options = {}, &instructions)
|
76
77
|
prefixes = dirs = nil
|
@@ -197,9 +198,9 @@ module Actions
|
|
197
198
|
File.open(tmpfile, 'w', 0755) do |w|
|
198
199
|
first = r.gets
|
199
200
|
return unless SHEBANG_RE =~ first
|
200
|
-
ruby = File.join(::
|
201
|
-
ruby << ::
|
202
|
-
#w.print first.sub(SHEBANG_RE, '#!' +
|
201
|
+
ruby = File.join(::RbConfig::CONFIG['bindir'],::RbConfig::CONFIG['ruby_install_name'])
|
202
|
+
ruby << ::RbConfig::CONFIG['EXEEXT']
|
203
|
+
#w.print first.sub(SHEBANG_RE, '#!' + RbConfig::CONFIG['ruby-prog'])
|
203
204
|
w.print first.sub(SHEBANG_RE, '#!' + ruby)
|
204
205
|
w.write r.read
|
205
206
|
end
|
@@ -308,7 +309,7 @@ class PackageSpecification_1_0
|
|
308
309
|
end
|
309
310
|
#TODO: refactor
|
310
311
|
self.class.declare_file_type(args) do |files, ignore_p, opt_rename_info|
|
311
|
-
files.each do |file|
|
312
|
+
(Array === files ? files : [files]).each do |file|
|
312
313
|
next if ignore_p && IGNORE_FILES.any?{|re| re.match(file)}
|
313
314
|
add_file(kind, file, opt_rename_info, &bin_callback)
|
314
315
|
end
|
@@ -397,7 +398,7 @@ class PackageSpecification_1_0
|
|
397
398
|
end
|
398
399
|
|
399
400
|
def initialize(prefixes = nil, dirs = nil)
|
400
|
-
@prefix =
|
401
|
+
@prefix = RbConfig::CONFIG["prefix"].gsub(/\A\//, '')
|
401
402
|
@translate = {}
|
402
403
|
@prefixes = (prefixes || {}).dup
|
403
404
|
KINDS.each do |kind|
|
@@ -429,7 +430,7 @@ class PackageSpecification_1_0
|
|
429
430
|
__send__ kind, Dir["#{kind}/**/*"]
|
430
431
|
end
|
431
432
|
translate(:ext, "ext/*" => "", :inherit => true)
|
432
|
-
ext Dir["ext/**/*.#{
|
433
|
+
ext Dir["ext/**/*.#{RbConfig::CONFIG['DLEXT']}"]
|
433
434
|
end
|
434
435
|
|
435
436
|
# Builds any needed extensions.
|
@@ -668,5 +669,5 @@ end # module Package
|
|
668
669
|
|
669
670
|
require 'rbconfig'
|
670
671
|
def config(x)
|
671
|
-
|
672
|
+
RbConfig::CONFIG[x]
|
672
673
|
end
|
data/external/test_support.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "rubygems"
|
1
2
|
# A support module for the test suite. This provides a win32 aware
|
2
3
|
# mechanism for doing fork/exec operations. It requires win32/process
|
3
4
|
# to be installed, however.
|
@@ -6,7 +7,7 @@ module SwiftcoreTestSupport
|
|
6
7
|
@run_modes = []
|
7
8
|
|
8
9
|
def self.create_process(args)
|
9
|
-
@fork_ok =
|
10
|
+
@fork_ok = instance_variable_defined?(:@fork_ok) && @fork_ok == false ? false : true
|
10
11
|
pid = nil
|
11
12
|
begin
|
12
13
|
raise NotImplementedError unless @fork_ok
|
@@ -16,13 +17,9 @@ module SwiftcoreTestSupport
|
|
16
17
|
end
|
17
18
|
rescue NotImplementedError
|
18
19
|
@fork_ok = false
|
19
|
-
begin
|
20
|
-
require 'rubygems'
|
21
|
-
rescue LoadError
|
22
|
-
end
|
23
20
|
|
24
21
|
begin
|
25
|
-
require
|
22
|
+
require "win32/process"
|
26
23
|
rescue LoadError
|
27
24
|
raise "Please install win32-process to run all tests on a Win32 platform. 'gem install win32-process' or http://rubyforge.org/projects/win32utils"
|
28
25
|
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'swiftcore/Analogger/version'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'benchmark'
|
5
|
+
require 'swiftcore/Analogger/AnaloggerProtocol'
|
6
|
+
|
7
|
+
module Swiftcore
|
8
|
+
class Analogger
|
9
|
+
C_colon = ':'.freeze
|
10
|
+
C_bar = '|'.freeze
|
11
|
+
Ccull = 'cull'.freeze
|
12
|
+
Cdaemonize = 'daemonize'.freeze
|
13
|
+
Cdefault = 'default'.freeze
|
14
|
+
Cdefault_log = 'default_log'.freeze
|
15
|
+
Chost = 'host'.freeze
|
16
|
+
Cinterval = 'interval'.freeze
|
17
|
+
Ckey = 'key'.freeze
|
18
|
+
Clogfile = 'logfile'.freeze
|
19
|
+
Clogs = 'logs'.freeze
|
20
|
+
Cport = 'port'.freeze
|
21
|
+
Csecret = 'secret'.freeze
|
22
|
+
Cservice = 'service'.freeze
|
23
|
+
Clevels = 'levels'.freeze
|
24
|
+
Csyncinterval = 'syncinterval'.freeze
|
25
|
+
Cpidfile = 'pidfile'.freeze
|
26
|
+
DefaultSeverityLevels = ['debug','info','warn','error','fatal'].inject({}){|h,k|h[k]=true;h}
|
27
|
+
TimeFormat = '%Y/%m/%d %H:%M:%S'.freeze
|
28
|
+
|
29
|
+
class NoPortProvided < Exception; def to_s; "The port to bind to was not provided."; end; end
|
30
|
+
class BadPort < Exception
|
31
|
+
def initialize(port)
|
32
|
+
@port = port
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s; "The port provided (#{@port}) is invalid."; end
|
36
|
+
end
|
37
|
+
|
38
|
+
EXIT_SIGNALS = %w[INT TERM]
|
39
|
+
RELOAD_SIGNALS = %w[HUP]
|
40
|
+
|
41
|
+
class << self
|
42
|
+
def safe_trap(siglist, &operation)
|
43
|
+
(Signal.list.keys & siglist).each {|sig| trap(sig, &operation)}
|
44
|
+
end
|
45
|
+
|
46
|
+
def start(config,protocol = AnaloggerProtocol)
|
47
|
+
@config = config
|
48
|
+
daemonize if @config[Cdaemonize]
|
49
|
+
File.open(@config[Cpidfile],'w+') {|fh| fh.puts $$} if @config[Cpidfile]
|
50
|
+
@logs = Hash.new {|h,k| h[k] = new_log(k)}
|
51
|
+
@queue = Hash.new {|h,k| h[k] = []}
|
52
|
+
postprocess_config_load
|
53
|
+
check_config_settings
|
54
|
+
populate_logs
|
55
|
+
set_config_defaults
|
56
|
+
@rcount = 0
|
57
|
+
@wcount = 0
|
58
|
+
@server = nil
|
59
|
+
safe_trap(EXIT_SIGNALS) {handle_pending_and_exit}
|
60
|
+
safe_trap(RELOAD_SIGNALS) {cleanup;throw :hup}
|
61
|
+
|
62
|
+
|
63
|
+
#####
|
64
|
+
# This is gross. EM needs to change so that it defaults to the faster
|
65
|
+
# platform specific methods, allowing the user the option to downgrade
|
66
|
+
# to a simple select() loop if they have a good reason for it.
|
67
|
+
#
|
68
|
+
EventMachine.epoll rescue nil
|
69
|
+
EventMachine.kqueue rescue nil
|
70
|
+
#
|
71
|
+
# End of gross.
|
72
|
+
#
|
73
|
+
# TODO: The above was written YEARS ago. See if EventMachine is smarter, now.
|
74
|
+
#####
|
75
|
+
|
76
|
+
EventMachine.set_descriptor_table_size(4096)
|
77
|
+
EventMachine.run {
|
78
|
+
EventMachine.add_shutdown_hook do
|
79
|
+
write_queue
|
80
|
+
flush_queue
|
81
|
+
cleanup
|
82
|
+
end
|
83
|
+
@server = EventMachine.start_server @config[Chost], @config[Cport], protocol
|
84
|
+
EventMachine.add_periodic_timer(1) {Analogger.update_now}
|
85
|
+
EventMachine.add_periodic_timer(@config[Cinterval]) {write_queue}
|
86
|
+
EventMachine.add_periodic_timer(@config[Csyncinterval]) {flush_queue}
|
87
|
+
}
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
def daemonize
|
92
|
+
if (child_pid = fork)
|
93
|
+
puts "PID #{child_pid}" unless @config[Cpidfile]
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
Process.setsid
|
97
|
+
|
98
|
+
exit if fork
|
99
|
+
|
100
|
+
rescue NotImplementedError
|
101
|
+
puts "Platform (#{RUBY_PLATFORM}) does not appear to support fork/setsid; skipping"
|
102
|
+
end
|
103
|
+
|
104
|
+
def new_log(facility = Cdefault, levels = @config[Clevels] || DefaultSeverityLevels, log = @config[Cdefault_log], cull = true)
|
105
|
+
Log.new({Cservice => facility, Clevels => levels, Clogfile => log, Ccull => cull})
|
106
|
+
end
|
107
|
+
|
108
|
+
# Before exiting, try to get any logs that are still in memory handled and written to disk.
|
109
|
+
def handle_pending_and_exit
|
110
|
+
EventMachine.stop_server(@server)
|
111
|
+
EventMachine.add_timer(1) do
|
112
|
+
_handle_pending_and_exit
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def _handle_pending_and_exit
|
117
|
+
if any_in_queue?
|
118
|
+
write_queue
|
119
|
+
EventMachine.next_tick {_handle_pending_and_exit}
|
120
|
+
else
|
121
|
+
EventMachine.stop
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def cleanup
|
126
|
+
@logs.each do |service,l|
|
127
|
+
l.logfile.fsync if !l.logfile.closed? and l.logfile.fileno > 2
|
128
|
+
l.logfile.close unless l.logfile.closed? or l.logfile.fileno < 3
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def update_now
|
133
|
+
@now = Time.now.strftime(TimeFormat)
|
134
|
+
end
|
135
|
+
|
136
|
+
def config
|
137
|
+
@config
|
138
|
+
end
|
139
|
+
|
140
|
+
def config=(conf)
|
141
|
+
@config = conf
|
142
|
+
end
|
143
|
+
|
144
|
+
def populate_logs
|
145
|
+
@config[Clogs].each do |log|
|
146
|
+
next unless log[Cservice]
|
147
|
+
if Array === log[Cservice]
|
148
|
+
log[Cservice].each do |loglog|
|
149
|
+
@logs[loglog] = new_log(loglog,log[Clevels],logfile_destination(log[Clogfile]),log[Ccull])
|
150
|
+
end
|
151
|
+
else
|
152
|
+
@logs[log[Cservice]] = new_log(log[Cservice],log[Clevels],logfile_destination(log[Clogfile]),log[Ccull])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def postprocess_config_load
|
158
|
+
@config[Clogs] ||= []
|
159
|
+
if @config[Clevels]
|
160
|
+
@config[Clevels] = normalize_levels(@config[Clevels])
|
161
|
+
end
|
162
|
+
|
163
|
+
@config[Clogs].each do |log|
|
164
|
+
log[Clevels] = normalize_levels(log[Clevels])
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def normalize_levels(levels)
|
169
|
+
if String === levels and levels =~ /,/
|
170
|
+
levels.split(/,/).inject({}) {|h,k| h[k.to_s] = true; h}
|
171
|
+
elsif Array === levels
|
172
|
+
levels.inject({}) {|h,k| h[k.to_s] = true; h}
|
173
|
+
elsif levels.nil?
|
174
|
+
DefaultSeverityLevels
|
175
|
+
elsif !(Hash === levels)
|
176
|
+
[levels.to_s => true]
|
177
|
+
else
|
178
|
+
levels
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def check_config_settings
|
183
|
+
raise NoPortProvided unless @config[Cport]
|
184
|
+
raise BadPort.new(@config[Cport]) unless @config[Cport].to_i > 0
|
185
|
+
end
|
186
|
+
|
187
|
+
def set_config_defaults
|
188
|
+
@config[Chost] ||= '127.0.0.1'
|
189
|
+
@config[Cinterval] ||= 1
|
190
|
+
@config[Csyncinterval] ||= 60
|
191
|
+
@config[Csyncinterval] = nil if @config[Csyncinterval] == 0
|
192
|
+
@config[Cdefault_log] = @config[Cdefault_log].nil? || @config[Cdefault_log] == '-' ? 'STDOUT' : @config[Cdefault_log]
|
193
|
+
@config[Cdefault_log] = logfile_destination(@config[Cdefault_log])
|
194
|
+
@logs['default'] = new_log
|
195
|
+
end
|
196
|
+
|
197
|
+
def logfile_destination(logfile)
|
198
|
+
# We're reloading if it's already an IO.
|
199
|
+
if logfile.is_a?(IO)
|
200
|
+
return $stdout if logfile == $stdout
|
201
|
+
return $stderr if logfile == $stderr
|
202
|
+
return logfile.reopen(logfile.path, 'ab+')
|
203
|
+
end
|
204
|
+
|
205
|
+
if logfile =~ /^STDOUT$/i
|
206
|
+
$stdout
|
207
|
+
elsif logfile =~ /^STDERR$/i
|
208
|
+
$stderr
|
209
|
+
else
|
210
|
+
File.open(logfile,'ab+')
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def add_log(log)
|
215
|
+
@queue[log.first] << log
|
216
|
+
@rcount += 1
|
217
|
+
end
|
218
|
+
|
219
|
+
def any_in_queue?
|
220
|
+
any = 0
|
221
|
+
@queue.each do |service, q|
|
222
|
+
q.each do |m|
|
223
|
+
next unless levels.has_key?(m[1])
|
224
|
+
any += 1
|
225
|
+
end
|
226
|
+
end
|
227
|
+
any > 0 ? any : false
|
228
|
+
end
|
229
|
+
|
230
|
+
def write_queue
|
231
|
+
@queue.each do |service, q|
|
232
|
+
last_sv = nil
|
233
|
+
last_m = nil
|
234
|
+
last_count = 0
|
235
|
+
next unless log = @logs[service]
|
236
|
+
lf = log.logfile
|
237
|
+
cull = log.cull
|
238
|
+
levels = log.levels
|
239
|
+
q.each do |m|
|
240
|
+
next unless levels.has_key?(m[1])
|
241
|
+
if cull
|
242
|
+
if m.last == last_m and m[0..1] == last_sv
|
243
|
+
last_count += 1
|
244
|
+
next
|
245
|
+
elsif last_count > 0
|
246
|
+
lf.write_nonblock "#{@now}|#{last_sv.join(C_bar)}|Last message repeated #{last_count} times\n"
|
247
|
+
last_sv = last_m = nil
|
248
|
+
last_count = 0
|
249
|
+
end
|
250
|
+
lf.write_nonblock "#{@now}|#{m.join(C_bar)}\n"
|
251
|
+
last_m = m.last
|
252
|
+
last_sv = m[0..1]
|
253
|
+
else
|
254
|
+
lf.write_nonblock "#{@now}|#{m.join(C_bar)}\n"
|
255
|
+
end
|
256
|
+
@wcount += 1
|
257
|
+
end
|
258
|
+
lf.write_nonblock "#{@now}|#{last_sv.join(C_bar)}|Last message repeated #{last_count} times\n" if cull and last_count > 0
|
259
|
+
end
|
260
|
+
@queue.each {|service,q| q.clear}
|
261
|
+
end
|
262
|
+
|
263
|
+
def flush_queue
|
264
|
+
@logs.each_value do |l|
|
265
|
+
if l.logfile.fileno > 2
|
266
|
+
l.logfile.fdatasync rescue l.logfile.fsync
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def key
|
272
|
+
@config[Ckey].to_s
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
class Log
|
280
|
+
attr_reader :service, :levels, :logfile, :cull
|
281
|
+
|
282
|
+
def initialize(spec)
|
283
|
+
@service = spec[Analogger::Cservice]
|
284
|
+
@levels = spec[Analogger::Clevels]
|
285
|
+
@logfile = spec[Analogger::Clogfile]
|
286
|
+
@cull = spec[Analogger::Ccull]
|
287
|
+
end
|
288
|
+
|
289
|
+
def to_s
|
290
|
+
"service: #{@service}\nlevels: #{@levels.inspect}\nlogfile: #{@logfile}\ncull: #{@cull}\n"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class AnaloggerProtocol < EventMachine::Connection
|
295
|
+
Ci = 'i'.freeze
|
296
|
+
Rcolon = /:/
|
297
|
+
|
298
|
+
LoggerClass = Analogger
|
299
|
+
|
300
|
+
def post_init
|
301
|
+
setup
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
|