analogger 0.5.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in iowa.gemspec
6
+ gemspec
@@ -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 = Swiftcdore::Analogger::Client.new('smallapp1','127.0.0.1','6766')
58
+ logger = Swiftcore::Analogger::Client.new('smallapp1','127.0.0.1','6766')
59
59
 
60
60
  logger.log('info','This is a log message.')
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.libs << "ext"
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ end
10
+
11
+ task :default => :test
@@ -1,41 +1,43 @@
1
- #####
2
- # Swiftcore Analogger
3
- # http://analogger.swiftcore.org
4
- # Copyright 2007 Kirk Haines
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
- spec = Gem::Specification.new do |s|
11
- s.name = 'analogger'
12
- s.author = %q(Kirk Haines)
13
- s.email = %q(wyhaines@gmail.com)
14
- s.version = '0.5.0'
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
- s.has_rdoc = true
19
- s.rdoc_options = %w(--title Swiftcore::Analogger --main README --line-numbers)
20
- s.extra_rdoc_files = %w(README)
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
- s.files = Dir['**/*']
23
- s.executables = %w(analogger)
24
- s.require_paths = %w(src)
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
- s.requirements << "Eventmachine 0.7.0 or higher."
27
- s.add_dependency('eventmachine')
28
- s.test_files = ['test/TC_Analogger.rb']
29
-
30
- s.rubyforge_project = %q(analogger)
31
- s.homepage = %q(http://analogger.swiftcore.org/)
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
- s.description = description[1..-1].join(" ")
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
+
@@ -3,15 +3,16 @@
3
3
  require 'yaml'
4
4
  require 'optparse'
5
5
  begin
6
- load_attempted ||= false
7
- require 'swiftcore/Analogger'
6
+ load_attempted ||= false
7
+ require 'swiftcore/Analogger'
8
8
  rescue LoadError => e
9
- unless load_attempted
10
- load_attempted = true
11
- require 'rubygems'
12
- retry
13
- end
14
- raise e
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
- class AnaloggerExec
35
- def self.parse_options(config = {})
36
- # Config file to read
37
- #
38
- # port: 12345
39
- # secret: abcdef
40
- # interval: 1
41
- # default_log: /var/log/emlogger
42
- # logs:
43
- # - service: client1
44
- # levels: info
45
- # logfile: /foo/bar.txt
46
- # cull: true
47
- #
48
- OptionParser.new do |opts|
49
- opts.banner = 'Usage: analogger.rb [options]'
50
- opts.separator ''
51
- opts.on('-c','--config CONFFILE',"The configuration file to read.") do |conf|
52
- config = YAML.load(File.read(conf))
53
- end
54
- opts.on('-p','--port [PORT]',Integer,"The port to receive connections on.") do |port|
55
- config[Swiftcore::Analogger::Cport] = port
56
- end
57
- opts.on('-h','--host [HOST]',String,"The host to bind the connection to.") do |host|
58
- config[Swiftcore::Analogger::Chost] = host
59
- end
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
- opts.on('-k','--key [KEY]',String,"The secret key that authenticates a valid client session.") do |secret|
64
- config[Swiftcore::Analogger::Ckey] = secret
65
- end
66
- opts.on('-i','--interval [INTERVAL]',String,"The interval between queue writes. Defaults to 1 second.") do |interval|
67
- config[Swiftcore::Analogger::Cinterval] = interval
68
- end
69
- opts.on('-s','--syncinterval [INTERVAL]',String,"The interval between queue syncs. Defaults to 60 seconds.") do |interval|
70
- config[Swiftcore::Analogger::Csyncinterval] = interval
71
- end
72
- opts.on('-d','--default [PATH]',String,"The default log destination. Defaults to stdout.") do |default|
73
- config[Swiftcore::Analogger::Cdefault_log] = default
74
- end
75
- opts.on('-x','--daemonize',"Tell the Analogger to daemonize itself.") do
76
- config[Swiftcore::Analogger::Cdaemonize] = true
77
- end
78
- opts.on('-w','--writepid [FILENAME]',"The filename to write a PID file to.") do |pidfile|
79
- config[Swiftcore::Analogger::Cpidfile] = pidfile || 'analogger.pid'
80
- end
81
- end.parse!
82
- config
83
- end
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
- def self.run
86
- Swiftcore::Analogger.start(parse_options)
87
- end
90
+ def self.run
91
+ @@parsed_options ||= parse_options
92
+ Swiftcore::Analogger.start(@@parsed_options)
93
+ end
88
94
 
89
- end
95
+ end
90
96
  end
91
97
 
92
98
  loop do
93
- catch(:hup) {Swiftcore::AnaloggerExec.run}
99
+ catch(:hup) {Swiftcore::AnaloggerExec.run}
94
100
  end
@@ -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(Config::CONFIG["prefix"])
40
- exec_prefix = Regexp.quote(Config::CONFIG["exec_prefix"])
41
- Config::CONFIG[name].gsub(/\A\/?(#{prefix}|#{exec_prefix})\/?/, '')
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(::Config::CONFIG['bindir'],::Config::CONFIG['ruby_install_name']) << ::Config::CONFIG['EXEEXT']
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(::Config::CONFIG['bindir'],::Config::CONFIG['ruby_install_name'])
201
- ruby << ::Config::CONFIG['EXEEXT']
202
- #w.print first.sub(SHEBANG_RE, '#!' + Config::CONFIG['ruby-prog'])
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 = Config::CONFIG["prefix"].gsub(/\A\//, '')
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/**/*.#{Config::CONFIG['DLEXT']}"]
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
- Config::CONFIG[x]
672
+ RbConfig::CONFIG[x]
672
673
  end
@@ -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 = true unless @fork_ok == false
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 'win32/process'
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
+