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.
@@ -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
+