em-syslog-logger 0.0.1 → 0.0.3

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.
@@ -1,220 +1,29 @@
1
1
 
2
2
  require 'socket'
3
- require 'rubygems'
4
3
  require 'eventmachine'
4
+ require 'em-syslog/syslog.rb'
5
+ require 'em-syslog/connection_tcp.rb'
6
+ require 'em-syslog/connection_udp.rb'
7
+ require 'em-syslog/connection_unix.rb'
8
+ require 'em-syslog/logger.rb'
5
9
 
6
10
  module EventMachine
7
11
  module Syslog
8
- # THIEVERY: http://github.com/kpumuk/ruby_syslog
9
- SEVERITIES = {
10
- :emergency => 0, # system is unusable
11
- :alert => 1, # action must be taken immediately
12
- :critical => 2, # critical conditions
13
- :error => 3, # error conditions
14
- :warning => 4, # warning conditions
15
- :notice => 5, # normal but significant condition
16
- :informational => 6, # informational messages
17
- :info => 6, # informational messages (short name for the previous)
18
- :debug => 7 # debug-level messages
19
- }
20
- # THIEVERY: http://github.com/kpumuk/ruby_syslog
21
- FACILITIES = {
22
- :kernel => 0, # kernel messages
23
- :user_level => 1, # user-level messages
24
- :mail => 2, # mail system
25
- :daemons => 3, # system daemons
26
- :security => 4, # security/authorization messages
27
- :internal => 5, # messages generated internally by syslogd
28
- :printer => 6, # line printer subsystem
29
- :network => 7, # network news subsystem
30
- :uucp => 8, # UUCP subsystem
31
- :clock => 9, # clock daemon
32
- :security1 => 10, # security/authorization messages
33
- :ftp => 11, # FTP daemon
34
- :ntp => 12, # NTP subsystem
35
- :log_audit => 13, # log audit
36
- :log_alert => 14, # log alert
37
- :clock1 => 15, # clock daemon
38
- :local0 => 16, # local use 0
39
- :local1 => 17, # local use 1
40
- :local2 => 18, # local use 2
41
- :local3 => 19, # local use 3
42
- :local4 => 20, # local use 4
43
- :local5 => 21, # local use 5
44
- :local6 => 22, # local use 6
45
- :local7 => 23 # local use 7
46
- }
12
+ ##
13
+ # Candy for creating a new Logger object or returned a cached one
47
14
 
48
15
  def self.logger( *a)
49
16
  EventMachine::Logger.new( *a)
50
17
  end
51
-
52
- module ConnectionUDP
53
- # EM does not support doing UDP over unix domain sockets, so we have to manually handle it.
54
- def self.create_unix
55
- ::Socket.new(
56
- ::Socket::PF_UNIX,
57
- ::Socket::SOCK_DGRAM
58
- )
59
- end
60
-
61
- def self.pack_unix( path)
62
- ::Socket.pack_sockaddr_un( path)
63
- end
64
-
65
- def setup( host, port, unix_connection=nil)
66
- @host = unix_connection.nil? ? host : ::Socket.pack_sockaddr_un( host)
67
- @port = port
68
- @unix_connection = unix_connection
69
- @unix_connection.connect( @host) unless @unix_connection.nil?
70
- end
71
-
72
- # Should not be needed
73
- def notify_readable
74
- read_packet
75
- end
76
-
77
- def read_packet
78
- data, sender = @unix_connection.recvfrom( 1024)
79
- true
80
- end
81
-
82
- def send_msg( msg)
83
- if @unix_connection.nil?
84
- send_datagram( msg, @host, @port)
85
- else
86
- @unix_connection.send( msg, 0)
87
- end
88
- end
89
- end
90
-
91
- module ConnectionTCP
92
- def setup( host, port)
93
- @host = host
94
- @port = port
95
- @queue = Array.new
96
- @connected = false
97
- end
98
-
99
- def post_init
100
- @connected = true
101
- @queue.size.times {
102
- send_msg( @queue.shift)
103
- }
104
- end
105
-
106
- def send_msg( msg)
107
- if @connected
108
- send_data( msg)
109
- else
110
- @queue.push( msg)
111
- end
112
- end
113
-
114
- def unbind
115
- @connected = false
116
- reconnect( @host, @port)
117
- end
118
- end
119
- end
120
-
121
- class Logger
122
- @@connection_cache = Hash.new
123
- attr_reader :idenity, :resource
124
-
125
- # Yup hack new class method for cache candy
126
- def self.new( idenity, resource="unix://dev/log")
127
- # See if we have a connection already in our cache
128
- key = self.mk_cache_index_key( idenity, resource)
129
- return @@connection_cache[key] if @@connection_cache.has_key? key and @@connection_cache[key].error? == false
130
-
131
- # Otherwise allocate a new object to do the work
132
- instance = self.allocate
133
- instance.send( :initialize, idenity, resource)
134
- @@connection_cache[key] = instance
135
- end
136
-
137
- def initialize( idenity, resource)
138
- @idenity = idenity.to_s + "[" + Process.pid.to_s + "]"
139
- @resource = resource
140
- resource = self.class.parse_resource( resource)
141
-
142
- case resource[0]
143
- when :unix
144
- # need better checking here
145
- raise "unix domain socket #{resource[1]} does not exist!" unless ::File.exists?( resource[1])
146
- @connection = Syslog::ConnectionUDP.create_unix
147
- resource << @connection
148
- @connection = EM.watch( @connection, Syslog::ConnectionUDP)
149
- when :tcp
150
- @connection = EM.connect( resource[1], resource[2], Syslog::ConnectionTCP)
151
- else
152
- @connection = EM.open_datagram_socket( '0.0.0.0', 0, Syslog::ConnectionUDP)
153
- end
154
- raise "unable to create connection" if @connection.nil?
155
- resource.shift
156
- @connection.setup( *resource)
157
- end
158
-
159
- def log( msg, facility, severity, debug=true)
160
- m = String.new
161
- if debug
162
- raise "Invalid log severity!" unless Syslog::SEVERITIES.has_key? severity
163
- raise "Invalid log facility!" unless Syslog::FACILITIES.has_key? facility
164
- end
165
- m += "<" + self.class.class_variable_get("@@syskey_#{facility}_#{severity}".to_sym).to_s + ">"
166
- m += self.class.timestamp + " " + ::Socket.gethostname + " #{@idenity} " + msg.to_s
167
- @connection.send_msg( m)
168
- end
169
-
170
- #Meta program our facility/severity keys and methods
171
- Syslog::FACILITIES.each {|facility,facility_int|
172
- Syslog::SEVERITIES.each {|severity,severity_int|
173
- define_method( "#{facility}_#{severity}".to_sym) do |msg|
174
- log( msg, facility, severity, false)
175
- end
176
- self.class_variable_set("@@syskey_#{facility}_#{severity}".to_sym, (facility_int * 8 + severity_int))
177
- }
178
- }
179
-
180
- private
181
- def self.timestamp( time=Time.now)
182
- day = time.strftime("%d")
183
- day = day.sub(/^0/, ' ') if day =~ /^0\d/
184
- time.strftime("%b #{day} %H:%M:%S")
185
- end
186
-
187
- # Likely not the fastest and best way to make a cache index
188
- def self.mk_cache_index_key( idenity, resource)
189
- idenity.to_s + resource.split(':').each {|i| i.gsub(/\./,'') }.join
190
- end
191
-
192
- def self.parse_resource( resource)
193
- split_point = resource.index(':')
194
- answer = [ resource[0..(split_point-1)].to_sym, resource[(split_point+1)..-1]]
195
- split_point = answer[1].index(':')
196
- if split_point.nil? == false and split_point > 0
197
- answer << answer[1][(split_point+1)..-1].to_i
198
- answer[1] = answer[1][0..(split_point-1)]
199
- elsif split_point.nil? and answer[0] != :unix
200
- answer << 514
201
- elsif split_point.nil? == false
202
- raise "Resource parse error"
203
- else
204
- answer[1].slice!(0)
205
- answer << nil
206
- end
207
- answer
208
- end
209
18
  end
210
- end
19
+ end
211
20
 
212
21
  if __FILE__ == $0
213
22
  EM.run {
214
23
  EM.kqueue if EM.kqueue?
215
24
  EM.epoll if EM.epoll?
216
25
 
217
- logger = EM::Syslog.logger( "em-syslog-test")
26
+ logger = EM::Syslog.logger( {:idenity => "em-syslog-test" })
218
27
  EM.next_tick {
219
28
  logger.log( "TEST INFO", :daemons, :info)
220
29
  logger.mail_error( "MAIL ERROR")
@@ -222,3 +31,5 @@ if __FILE__ == $0
222
31
  }
223
32
  end
224
33
 
34
+ require 'em-syslog/version.rb'
35
+
@@ -0,0 +1,84 @@
1
+
2
+ module EventMachine
3
+ module Syslog
4
+ ##
5
+ # Our module to pass to EventMachine to create the connection handler
6
+
7
+ module ConnectionTCP
8
+ DEFAULTS = {:queue_max_size => 500,
9
+ :queue_batch_send => 20,
10
+ :on_hangup => nil,
11
+ :reconnect => true
12
+ }
13
+ ##
14
+ # setup our connection with remote/local information, with a hash config merged against ConnectionTCP::DEFAULTS
15
+
16
+ def setup( host, port, config={})
17
+ config = DEFAULTS.merge( config)
18
+ @host = host
19
+ @port = port
20
+ @do_reconnect = config[:on_hangup]
21
+ @reconnect = config[:reconnect]
22
+ @queue = Array.new
23
+ @connected = false
24
+ true
25
+ end
26
+
27
+ ##
28
+ # we got our connection, time to start sending any items we have queued up
29
+
30
+ def post_init
31
+ @connected = true
32
+ queue_send_batch
33
+ end
34
+
35
+ ##
36
+ # queue messages if we are not connected.
37
+
38
+ def send_msg( msg)
39
+ if @connected
40
+ send_data( msg)
41
+ else
42
+ queue_push( msg)
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Do you want to be told when it disconnects and/or do you want to reconnect right away config[:reconnect] = true
48
+ # passing config[:on_hangup] provide a proc.
49
+
50
+ def unbind
51
+ @connected = false
52
+ reconnect( @host, @port) if @do_reconnect
53
+ @on_hangup.call unless @on_hangup.nil?
54
+ end
55
+
56
+ private
57
+ ##
58
+ # Rotate out old messages for newer messages.
59
+
60
+ def queue_push( msg)
61
+ old_message = (@queue.length >= @queue_max_size) ? @queue.shift : nil
62
+ @queue << msg
63
+ old_message
64
+ end
65
+
66
+ ##
67
+ # loop batch send on each tick sending at least config[:queue_batch_size] times
68
+
69
+ def queue_send_batch
70
+ run_list = @queue.slice!(0..@queue_batch_size)
71
+ run_list.each {|msg|
72
+ send_msg( msg)
73
+ }
74
+ unless @queue.empty?
75
+ EM.next_tick {
76
+ queue_send_batch
77
+ }
78
+ end
79
+ true
80
+ end
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1,37 @@
1
+
2
+ module EventMachine
3
+ module Syslog
4
+ ##
5
+ # Our module to pass to EventMachine to handle the UDP Connection over IP Socket
6
+
7
+ module ConnectionUDP
8
+
9
+ def setup( host, port)
10
+ @host = host
11
+ @port = port
12
+ end
13
+
14
+ ##
15
+ # Should not be needed
16
+
17
+ def notify_readable
18
+ read_packet
19
+ end
20
+
21
+ ##
22
+ # Should not be needed
23
+
24
+ def read_packet
25
+ data, sender = @unix_connection.recvfrom( 1024)
26
+ true
27
+ end
28
+
29
+ ##
30
+ # Handle no buffering just send it
31
+
32
+ def send_msg( msg)
33
+ send_datagram( msg, @host, @port)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+
2
+ module EventMachine
3
+ module Syslog
4
+ module ConnectionUDP
5
+ ##
6
+ # Our module to pass to EventMachine to handle the UDP Connection over IPC Socket
7
+
8
+ module UNIX
9
+ include ConnectionUDP
10
+ ##
11
+ # we also have to code around the difference of Socket.new in ruby 1.8 and 1.9
12
+
13
+ def self.create_unix
14
+ case RUBY_VERSION.split('.')[1].to_i
15
+ when 8
16
+ ::Socket.new(
17
+ ::Socket::PF_UNIX,
18
+ ::Socket::SOCK_DGRAM,
19
+ 0
20
+ )
21
+ else
22
+ ::Socket.new(
23
+ ::Socket::PF_UNIX,
24
+ ::Socket::SOCK_DGRAM
25
+ )
26
+ end
27
+ end
28
+
29
+ def setup( ipc, path)
30
+ ipc_address = ::Socket.pack_sockaddr_un( path)
31
+ @unix_connection = ipc
32
+ @unix_connection.connect( ipc_address)
33
+ end
34
+
35
+ def send_msg( msg)
36
+ @unix_connection.send( msg, 0)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,150 @@
1
+
2
+ module EventMachine
3
+ ##
4
+ # Generic Logger Class, providing simple to use statements
5
+ # auto fills in the most likely config DEFAULTS
6
+
7
+ class Logger
8
+ @@connection_cache = Hash.new
9
+ begin
10
+ @@hostname = ::Socket.gethostname.split('.')[0]
11
+ rescue Exception
12
+ @@hostname = 'localhost'
13
+ end
14
+ attr_reader :idenity, :resource, :include_hostname
15
+ DEFAULTS = {:idenity => $PROGRAM_NAME,
16
+ :include_hostname => false,
17
+ :resource => "udp:/dev/log"
18
+ }
19
+ ##
20
+ # hack new class method for caching connections, making it safe to keep variables out of scope.
21
+
22
+ def self.new( config = {})
23
+ config = DEFAULTS.merge config
24
+ # See if we have a connection already in our cache
25
+ key = self.mk_cache_index_key( config[:idenity], config[:resource])
26
+ return @@connection_cache[key] if @@connection_cache.has_key? key and @@connection_cache[key].error? == false
27
+
28
+ # Otherwise allocate a new object to do the work
29
+ instance = self.allocate
30
+ instance.send( :initialize, config)
31
+ @@connection_cache[key] = instance
32
+ end
33
+
34
+ ##
35
+ # save out needed information from config, and start up the connection
36
+ TYPE = 0
37
+ HOST = 1
38
+ IPC = 1
39
+ PORT = 2
40
+
41
+ def initialize( config)
42
+ @idenity = config[:idenity].to_s + "[" + Process.pid.to_s + "]"
43
+ @resource = config[:resource].dup
44
+ @include_hostname = config[:include_hostname]
45
+ resource = self.class.parse_resource( config[:resource])
46
+
47
+ @connection = nil
48
+ if resource[TYPE] == :tcp
49
+ # resource[PORT] should return nil considering we only define a pair (resource_type|path)
50
+ @connection = EM.connect( resource[HOST], resource[PORT], Syslog::ConnectionTCP)
51
+ raise "unable to create connection" if @connection.nil?
52
+ @connection.setup( resource[HOST], resource[PORT])
53
+ elsif resource.length == 3
54
+ @connection = EM.open_datagram_socket( '0.0.0.0', 0, Syslog::ConnectionUDP)
55
+ raise "unable to create connection" if @connection.nil?
56
+ @connection.setup( resource[HOST], resource[PORT])
57
+ else
58
+ # need better checking here
59
+ raise "unix domain socket #{resource[IPC]} does not exist!" unless ::File.exists?( resource[IPC])
60
+ c = Syslog::ConnectionUDP::UNIX.create_unix
61
+ @connection = EM.watch( c, Syslog::ConnectionUDP::UNIX)
62
+ raise "unable to create connection" if @connection.nil?
63
+ @connection.setup( c, resource[IPC])
64
+ end
65
+ end
66
+
67
+ ##
68
+ # When called directly we need to make sure we were given valid facilities and severity
69
+
70
+ def log( msg, facility, severity)
71
+ raise "Invalid log severity!" unless Syslog::SEVERITIES.has_key? severity
72
+ raise "Invalid log facility!" unless Syslog::FACILITIES.has_key? facility
73
+ send_log( msg, facility, severity)
74
+ end
75
+
76
+ ##
77
+ #Meta program our facility/severity keys and methods
78
+
79
+ Syslog::FACILITIES.each {|facility,facility_int|
80
+ Syslog::SEVERITIES.each {|severity,severity_int|
81
+ define_method( "#{facility}_#{severity}".to_sym) do |msg|
82
+ send_log( msg, facility, severity)
83
+ end
84
+ class_variable_set("@@syskey_#{facility}_#{severity}".to_sym, (facility_int * 8 + severity_int))
85
+ }
86
+ }
87
+ Syslog::SEVERITIES.each {|severity,severity_int|
88
+ define_method( severity) do |msg,facility|
89
+ send_log( msg, ((facility.nil?) ? :daemons : facility), severity)
90
+ end
91
+ }
92
+
93
+ ##
94
+ # you must fix up the timestamp so that a space is injected in place of a leading 0
95
+ # - returns "Dec 6 12:12:12"
96
+
97
+ def self.timestamp( time=Time.now)
98
+ day = time.strftime("%d")
99
+ day = day.sub(/^0/, ' ') if day =~ /^0\d/
100
+ time.strftime("%b #{day} %H:%M:%S")
101
+ end
102
+
103
+ private
104
+ ##
105
+ # Format and send message to syslog
106
+
107
+ def send_log( msg, facility, severity)
108
+ m = String.new
109
+ m += "<" + self.class.send( :class_variable_get, "@@syskey_#{facility}_#{severity}".to_sym).to_s + ">"
110
+ m += self.class.timestamp + " "
111
+ m += @@hostname + " " if include_hostname
112
+ m += "#{@idenity}: " + msg.to_s
113
+ @connection.send_msg( m)
114
+ end
115
+
116
+ ##
117
+ # Likely not the fastest and best way to make a cache index
118
+ # - returns "#{idenitiy}#{ip|hostname|path.without_dots}"
119
+
120
+ def self.mk_cache_index_key( idenity, resource)
121
+ idenity.to_s + resource.split(':').each {|i| i.gsub(/\./,'') }.join
122
+ end
123
+
124
+ ##
125
+ # Likely not the fastest and best way to parse the syslog resource idenity.
126
+ # unix will always return a set length of 2 and host/port at length 3
127
+ # - returns [ resource_type (:tcp|:udp), ipc_path|ip_address|hostname [, port]]
128
+
129
+ def self.parse_resource( resource)
130
+ split_point = resource.index(':')
131
+ resource_type = resource[0..(split_point-1)].to_sym
132
+ resource = resource[(split_point+1)..-1]
133
+ if resource_type != :tcp and resource_type != :udp
134
+ raise "Invalid resource syntax, protocol must be :tcp|:udp"
135
+ elsif resource[0..1] == "//"
136
+ resource_parts = resource.split('/')
137
+ target_parts = resource_parts[2].split(':')
138
+ raise "Invalid resource syntax: #{resource}" if target_parts.length > 2 or resource_parts.length > 3
139
+ target_parts << 514 if target_parts.length == 1
140
+ target_parts.unshift resource_type
141
+ elsif resource[0,1] == "/"
142
+ target_parts = [ resource_type, resource]
143
+ else
144
+ raise "Invalid resource syntax: can't detect connection type #{resource}"
145
+ end
146
+ target_parts
147
+ end
148
+ end
149
+ end
150
+
@@ -0,0 +1,49 @@
1
+
2
+ module EventMachine
3
+ module Syslog
4
+ ##
5
+ # THIEVERY: http://github.com/kpumuk/ruby_syslog
6
+
7
+ SEVERITIES = {
8
+ :emergency => 0, # system is unusable
9
+ :alert => 1, # action must be taken immediately
10
+ :critical => 2, # critical conditions
11
+ :error => 3, # error conditions
12
+ :warning => 4, # warning conditions
13
+ :notice => 5, # normal but significant condition
14
+ :informational => 6, # informational messages
15
+ :info => 6, # informational messages (short name for the previous)
16
+ :debug => 7 # debug-level messages
17
+ }
18
+
19
+ ##
20
+ # THIEVERY: http://github.com/kpumuk/ruby_syslog
21
+
22
+ FACILITIES = {
23
+ :kernel => 0, # kernel messages
24
+ :user_level => 1, # user-level messages
25
+ :mail => 2, # mail system
26
+ :daemons => 3, # system daemons
27
+ :security => 4, # security/authorization messages
28
+ :internal => 5, # messages generated internally by syslogd
29
+ :printer => 6, # line printer subsystem
30
+ :network => 7, # network news subsystem
31
+ :uucp => 8, # UUCP subsystem
32
+ :clock => 9, # clock daemon
33
+ :security1 => 10, # security/authorization messages
34
+ :ftp => 11, # FTP daemon
35
+ :ntp => 12, # NTP subsystem
36
+ :log_audit => 13, # log audit
37
+ :log_alert => 14, # log alert
38
+ :clock1 => 15, # clock daemon
39
+ :local0 => 16, # local use 0
40
+ :local1 => 17, # local use 1
41
+ :local2 => 18, # local use 2
42
+ :local3 => 19, # local use 3
43
+ :local4 => 20, # local use 4
44
+ :local5 => 21, # local use 5
45
+ :local6 => 22, # local use 6
46
+ :local7 => 23 # local use 7
47
+ }
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ module EventMachine
2
+ module Syslog
3
+ VERSION = "0.0.3"
4
+ end
5
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 1
9
- version: 0.0.1
8
+ - 3
9
+ version: 0.0.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Digital Akasha
@@ -40,6 +40,12 @@ extensions: []
40
40
  extra_rdoc_files: []
41
41
 
42
42
  files:
43
+ - lib/em-syslog/connection_tcp.rb
44
+ - lib/em-syslog/connection_udp.rb
45
+ - lib/em-syslog/connection_unix.rb
46
+ - lib/em-syslog/logger.rb
47
+ - lib/em-syslog/syslog.rb
48
+ - lib/em-syslog/version.rb
43
49
  - lib/em-syslog.rb
44
50
  has_rdoc: true
45
51
  homepage: https://github.com/tormenta/em-syslog