antisyslog 0.0.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/lib/antisyslog.rb +270 -0
- metadata +43 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 75e7ccc2cef4565f778fed74829c3f2302d0e7fb
|
4
|
+
data.tar.gz: 144ac2818818f2a46c27624cf97689fa6a882a92
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b9f68d92d762190f29d5dd1ca8e479314a592cc4615dcec538decd592eaf69ca0b9291d5492082f1f2c3e29dd91b470098f91d3cdf4b65523b62c7c228a01a21
|
7
|
+
data.tar.gz: e46b01f10b1aee6469959c101a7b207448c551d43e98e8da22c1c86f94748a0b8cfbc9cee98db27208bc72bc650686a3f85968b590d9b123d97d8bec6cc96320
|
data/lib/antisyslog.rb
ADDED
@@ -0,0 +1,270 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
FACILITIES = {
|
5
|
+
'kern' => 0,
|
6
|
+
'user' => 1,
|
7
|
+
'mail' => 2,
|
8
|
+
'daemon' => 3,
|
9
|
+
'auth' => 4,
|
10
|
+
'syslog' => 5,
|
11
|
+
'lpr' => 6,
|
12
|
+
'news' => 7,
|
13
|
+
'uucp' => 8,
|
14
|
+
'cron' => 9,
|
15
|
+
'authpriv' => 10,
|
16
|
+
'ftp' => 11,
|
17
|
+
'ntp' => 12,
|
18
|
+
'audit' => 13,
|
19
|
+
'alert' => 14,
|
20
|
+
'at' => 15,
|
21
|
+
'local0' => 16,
|
22
|
+
'local1' => 17,
|
23
|
+
'local2' => 18,
|
24
|
+
'local3' => 19,
|
25
|
+
'local4' => 20,
|
26
|
+
'local5' => 21,
|
27
|
+
'local6' => 22,
|
28
|
+
'local7' => 23
|
29
|
+
}
|
30
|
+
|
31
|
+
FACILITY_INDEX = {
|
32
|
+
0 => 'kern',
|
33
|
+
1 => 'user',
|
34
|
+
2 => 'mail',
|
35
|
+
3 => 'daemon',
|
36
|
+
4 => 'auth',
|
37
|
+
5 => 'syslog',
|
38
|
+
6 => 'lpr',
|
39
|
+
7 => 'news',
|
40
|
+
8 => 'uucp',
|
41
|
+
9 => 'cron',
|
42
|
+
10 => 'authpriv',
|
43
|
+
11 => 'ftp',
|
44
|
+
12 => 'ntp',
|
45
|
+
13 => 'audit',
|
46
|
+
14 => 'alert',
|
47
|
+
15 => 'at',
|
48
|
+
16 => 'local0',
|
49
|
+
17 => 'local1',
|
50
|
+
18 => 'local2',
|
51
|
+
19 => 'local3',
|
52
|
+
20 => 'local4',
|
53
|
+
21 => 'local5',
|
54
|
+
22 => 'local6',
|
55
|
+
23 => 'local7'
|
56
|
+
}
|
57
|
+
|
58
|
+
SEVERITIES = {
|
59
|
+
'emerg' => 0,
|
60
|
+
'alert' => 1,
|
61
|
+
'crit' => 2,
|
62
|
+
'err' => 3,
|
63
|
+
'warn' => 4,
|
64
|
+
'notice' => 5,
|
65
|
+
'info' => 6,
|
66
|
+
'debug' => 7
|
67
|
+
}
|
68
|
+
|
69
|
+
SEVERITY_INDEX = {
|
70
|
+
0 => 'emerg',
|
71
|
+
1 => 'alert',
|
72
|
+
2 => 'crit',
|
73
|
+
3 => 'err',
|
74
|
+
4 => 'warn',
|
75
|
+
5 => 'notice',
|
76
|
+
6 => 'info',
|
77
|
+
7 => 'debug'
|
78
|
+
}
|
79
|
+
|
80
|
+
class AntisyslogPacket
|
81
|
+
attr_reader :facility, :severity, :hostname, :tag
|
82
|
+
attr_accessor :time, :content
|
83
|
+
|
84
|
+
def to_s
|
85
|
+
assemble
|
86
|
+
end
|
87
|
+
|
88
|
+
def assemble()
|
89
|
+
unless @hostname and @facility and @severity and @tag
|
90
|
+
raise "Could not assemble packet without hostname, tag, facility, and severity"
|
91
|
+
end
|
92
|
+
"<#{pri}>#{generate_timestamp} #{@hostname} #{@tag}: #{@content}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def facility=(f)
|
96
|
+
if f.is_a? Integer
|
97
|
+
if (0..23).include?(f)
|
98
|
+
@facility = f
|
99
|
+
else
|
100
|
+
raise ArgumentError.new "Facility must be within 0-23"
|
101
|
+
end
|
102
|
+
elsif f.is_a? String
|
103
|
+
if facility = FACILITIES[f]
|
104
|
+
@facility = facility
|
105
|
+
else
|
106
|
+
raise ArgumentError.new "'#{f}' is not a designated facility"
|
107
|
+
end
|
108
|
+
else
|
109
|
+
raise ArgumentError.new "Facility must be a designated number or string"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def tag=(t)
|
114
|
+
unless t && t.is_a?(String) && t.length > 0
|
115
|
+
raise ArgumentError, "Tag must not be omitted"
|
116
|
+
end
|
117
|
+
if t.length > 32
|
118
|
+
raise ArgumentError, "Tag must not be longer than 32 characters"
|
119
|
+
end
|
120
|
+
if t =~ /\s/
|
121
|
+
raise ArgumentError, "Tag may not contain spaces"
|
122
|
+
end
|
123
|
+
if t =~ /[^\x21-\x7E]/
|
124
|
+
raise ArgumentError, "Tag may only contain ASCII characters 33-126"
|
125
|
+
end
|
126
|
+
|
127
|
+
@tag = t
|
128
|
+
end
|
129
|
+
|
130
|
+
def severity=(s)
|
131
|
+
if s.is_a? Integer
|
132
|
+
if (0..7).include?(s)
|
133
|
+
@severity = s
|
134
|
+
else
|
135
|
+
raise ArgumentError.new "Severity must be within 0-7"
|
136
|
+
end
|
137
|
+
elsif s.is_a? String
|
138
|
+
if severity = SEVERITIES[s]
|
139
|
+
@severity = severity
|
140
|
+
else
|
141
|
+
raise ArgumentError.new "'#{s}' is not a designated severity"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
raise ArgumentError.new "Severity must be a designated number or string"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def hostname=(h)
|
149
|
+
unless h and h.is_a? String and h.length > 0
|
150
|
+
raise ArgumentError.new("Hostname may not be omitted")
|
151
|
+
end
|
152
|
+
if h =~ /\s/
|
153
|
+
raise ArgumentError.new("Hostname may not contain spaces")
|
154
|
+
end
|
155
|
+
if h =~ /[^\x21-\x7E]/
|
156
|
+
raise ArgumentError.new("Hostname may only contain ASCII characters 33-126")
|
157
|
+
end
|
158
|
+
@hostname = h
|
159
|
+
end
|
160
|
+
|
161
|
+
def facility_name
|
162
|
+
FACILITY_INDEX[@facility]
|
163
|
+
end
|
164
|
+
|
165
|
+
def severity_name
|
166
|
+
SEVERITY_INDEX[@severity]
|
167
|
+
end
|
168
|
+
|
169
|
+
def pri
|
170
|
+
(@facility * 8) + @severity
|
171
|
+
end
|
172
|
+
|
173
|
+
def pri=(p)
|
174
|
+
unless p.is_a? Integer and (0..191).include?(p)
|
175
|
+
raise ArgumentError.new "PRI must be a number between 0 and 191"
|
176
|
+
end
|
177
|
+
@facility = p / 8
|
178
|
+
@severity = p - (@facility * 8)
|
179
|
+
end
|
180
|
+
|
181
|
+
def generate_timestamp
|
182
|
+
time = @time || Time.now
|
183
|
+
# The timestamp format requires that a day with fewer than 2 digits have
|
184
|
+
# what would normally be a preceding zero, be instead an extra space.
|
185
|
+
day = time.strftime("%d")
|
186
|
+
day = day.sub(/^0/, ' ') if day =~ /^0\d/
|
187
|
+
time.strftime("%b #{day} %H:%M:%S")
|
188
|
+
end
|
189
|
+
|
190
|
+
if "".respond_to?(:bytesize)
|
191
|
+
def string_bytesize(string)
|
192
|
+
string.bytesize
|
193
|
+
end
|
194
|
+
else
|
195
|
+
def string_bytesize(string)
|
196
|
+
string.length
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
SEVERITIES.each do |k,v|
|
201
|
+
define_method("#{k}?") { SEVERITIES[k] == @severity }
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class AntisyslogSender
|
206
|
+
def initialize(remote_hostname, remote_port, options = {})
|
207
|
+
@remote_hostname = remote_hostname
|
208
|
+
@remote_port = remote_port
|
209
|
+
@whinyerrors = options[:whinyerrors]
|
210
|
+
@protocol = options[:protocol] || 'tcp'
|
211
|
+
@splitlines = options[:split_lines] || false
|
212
|
+
|
213
|
+
@socket = @protocol == 'tcp' ? TCPSocket.new(@remote_hostname, @remote_port) : UDPSocket.new
|
214
|
+
@packet = AntisyslogPacket.new
|
215
|
+
|
216
|
+
local_hostname = options[:local_hostname] || (Socket.gethostname rescue `hostname`.chomp)
|
217
|
+
local_hostname = 'localhost' if local_hostname.nil? || local_hostname.empty?
|
218
|
+
@packet.hostname = local_hostname
|
219
|
+
|
220
|
+
@packet.facility = options[:facility] || 'user'
|
221
|
+
@packet.severity = options[:severity] || 'notice'
|
222
|
+
@packet.tag = options[:program] || "#{File.basename($0)}[#{$$}]"
|
223
|
+
end
|
224
|
+
|
225
|
+
def send_msg(msg)
|
226
|
+
packet = @packet.dup
|
227
|
+
packet.content = msg
|
228
|
+
if @protocol == 'tcp'
|
229
|
+
@socket.send(packet.assemble, 0)
|
230
|
+
else
|
231
|
+
@socket.send(packet.assemble, 0, @remote_hostname, @remote_port)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def transmit(message)
|
236
|
+
if @splitlines
|
237
|
+
message.split(/\r?\n/).each do |line|
|
238
|
+
begin
|
239
|
+
next if line =~ /^\s*$/
|
240
|
+
send_msg(line)
|
241
|
+
rescue
|
242
|
+
$stderr.puts "#{self.class} error: #{$!.class}: #{$!}\nOriginal message: #{line}"
|
243
|
+
raise if @whinyerrors
|
244
|
+
end
|
245
|
+
end
|
246
|
+
else
|
247
|
+
begin
|
248
|
+
send_msg(message)
|
249
|
+
rescue
|
250
|
+
$stderr.puts "#{self.class} error: #{$!.class}: #{$!}\nOriginal message: #{message}"
|
251
|
+
raise if @whinyerrors
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Make this act a little bit like an `IO` object
|
257
|
+
alias_method :write, :transmit
|
258
|
+
|
259
|
+
def close
|
260
|
+
@socket.close
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
module AntiSyslogLogger
|
265
|
+
VERSION = '0.0.1'
|
266
|
+
|
267
|
+
def self.new(remote_hostname, remote_port, options = {})
|
268
|
+
Logger.new(AntisyslogSender.new(remote_hostname, remote_port, options))
|
269
|
+
end
|
270
|
+
end
|
metadata
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: antisyslog
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamen Keroack
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A totally non-compliant syslog Rails logger
|
14
|
+
email: benjamen@dollarshaveclub.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/antisyslog.rb
|
20
|
+
homepage: https://github.com/dollarshaveclub/antisyslog
|
21
|
+
licenses: []
|
22
|
+
metadata: {}
|
23
|
+
post_install_message:
|
24
|
+
rdoc_options: []
|
25
|
+
require_paths:
|
26
|
+
- lib
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
requirements: []
|
38
|
+
rubyforge_project:
|
39
|
+
rubygems_version: 2.4.3
|
40
|
+
signing_key:
|
41
|
+
specification_version: 4
|
42
|
+
summary: A totally non-compliant syslog Rails logger
|
43
|
+
test_files: []
|