log2json 0.1.21 → 0.1.22
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/lib/log2json.rb +6 -3
- data/lib/log2json/mlogbuffer.rb +134 -0
- data/log2json.gemspec +1 -1
- metadata +3 -2
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
|
-
gemspec
|
2
|
+
gemspec :name => 'log2json'
|
data/Gemfile.lock
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
log2json (0.1.
|
4
|
+
log2json (0.1.22)
|
5
5
|
jls-grok (~> 0.10.10)
|
6
|
-
persistent_http (~> 1.0.
|
6
|
+
persistent_http (~> 1.0.6)
|
7
7
|
redis (~> 3.0.2)
|
8
8
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
cabin (0.
|
12
|
+
cabin (0.6.1)
|
13
13
|
gene_pool (1.3.0)
|
14
|
-
jls-grok (0.10.
|
15
|
-
cabin (
|
16
|
-
persistent_http (1.0.
|
14
|
+
jls-grok (0.10.12)
|
15
|
+
cabin (>= 0.6.0)
|
16
|
+
persistent_http (1.0.6)
|
17
17
|
gene_pool (>= 1.3)
|
18
|
-
redis (3.0.
|
18
|
+
redis (3.0.6)
|
19
19
|
|
20
20
|
PLATFORMS
|
21
21
|
ruby
|
data/lib/log2json.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'grok-pure'
|
3
3
|
|
4
|
+
require 'log2json/mlogbuffer'
|
5
|
+
|
4
6
|
module Log2Json
|
5
7
|
|
6
8
|
def self.main(filters, opts={}, &block)
|
@@ -13,7 +15,7 @@ def self.main(filters, opts={}, &block)
|
|
13
15
|
key = name.to_s.downcase
|
14
16
|
config[name] = ENV[key] if ENV.member?(key)
|
15
17
|
end
|
16
|
-
spitter = ::Log2Json::Spitter.new(STDIN, ENV['type'], config)
|
18
|
+
spitter = ::Log2Json::Spitter.new(STDIN, ENV['type'], config.merge(opts))
|
17
19
|
end
|
18
20
|
if block.nil?
|
19
21
|
block = proc do |rec|
|
@@ -63,7 +65,8 @@ class Spitter
|
|
63
65
|
UTC_TIMESTAMP_FORMAT: "%FT%T.%6NZ",
|
64
66
|
SOURCE_SEPERATOR_REGEX: Regexp.new("^==> (.+) <=="),
|
65
67
|
# because /.../ screws up syntax highlighting in vim so I use Regexp.new(...)
|
66
|
-
|
68
|
+
|
69
|
+
KEEP_EMTPY_LINES: false,
|
67
70
|
TAGS: '',
|
68
71
|
FIELDS: '',
|
69
72
|
}
|
@@ -96,7 +99,7 @@ class Spitter
|
|
96
99
|
@input.each_line do |line|
|
97
100
|
line.force_encoding(options[:LOG_INPUT_ENCODING])
|
98
101
|
line.chomp!
|
99
|
-
next if line.empty?
|
102
|
+
next if line.empty? unless @options[:KEEP_EMPTY_LINES]
|
100
103
|
if line =~ options[:SOURCE_SEPERATOR_REGEX]
|
101
104
|
@source_path = $1
|
102
105
|
next
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Log2Json
|
4
|
+
|
5
|
+
|
6
|
+
class MultilineLogBuffer
|
7
|
+
|
8
|
+
LOG = Logger.new(STDERR)
|
9
|
+
|
10
|
+
# This class workaround the flaw that for some applications a log record may consist
|
11
|
+
# of mulitple lines without an end-of-record marker.
|
12
|
+
#
|
13
|
+
# Example 1:
|
14
|
+
#
|
15
|
+
# [ERROR:12980 2013-11-05 09:46:29] IO_ENCODER
|
16
|
+
# Traceback (most recent call last):
|
17
|
+
# File "/opt/score_feeder/feeds/rotowire/worker.py", line 59, in process_queue
|
18
|
+
# (errorlog, rs), processed = method(xmldoc), True
|
19
|
+
# File "/opt/score_feeder/feeds/__init__.py", line 206, in handle
|
20
|
+
# results = fn(*args, **kwargs)
|
21
|
+
# File "/opt/score_feeder/feeds/__init__.py", line 229, in handle
|
22
|
+
# results = fn(*args, **kwargs)
|
23
|
+
# File "/opt/score_feeder/feeds/__init__.py", line 141, in handle
|
24
|
+
# results = fn(*args, **kwargs)
|
25
|
+
# File "/opt/score_feeder/feeds/__init__.py", line 261, in handle
|
26
|
+
# results = fn(*args, **kwargs)
|
27
|
+
# File "/opt/score_feeder/feeds/__init__.py", line 174, in handle
|
28
|
+
# results = fn(*args, **kwargs)
|
29
|
+
# File "/opt/score_feeder/feeds/rotowire/leagues/nfl/player_news.py", line 40, in NFL_PLAYER_NEWS
|
30
|
+
# snippet = xml_parse(xml_serialize(snippet))
|
31
|
+
# File "/opt/score_feeder/lib/utils.py", line 239, in xml_serialize
|
32
|
+
# xmlstr = lxml.etree.tostring(node).strip()
|
33
|
+
# File "lxml.etree.pyx", line 3119, in lxml.etree.tostring (src/lxml/lxml.etree.c:64447)
|
34
|
+
# File "serializer.pxi", line 132, in lxml.etree._tostring (src/lxml/lxml.etree.c:101602)
|
35
|
+
# File "serializer.pxi", line 192, in lxml.etree._raiseSerialisationError (src/lxml/lxml.etree.c:102217)
|
36
|
+
# SerialisationError: IO_ENCODER
|
37
|
+
#
|
38
|
+
# Example 2:
|
39
|
+
#
|
40
|
+
# [INFO:7091 2013-11-05 09:32:46] [job 6, pri 0, req 0] NBA_MOVEMENTS finished. [c 1, u 0, d 1]
|
41
|
+
# No players that match global id [2] and name [Abdul-Wahad, Tariq]. Skipping.
|
42
|
+
# league: nba, method: NBA_MOVEMENTS, filename: TPnBh5movements_basketball_ab.xml
|
43
|
+
#
|
44
|
+
# No players that match global id [3] and name [Abdur-Rahim, Shareef]. Skipping.
|
45
|
+
# league: nba, method: NBA_MOVEMENTS, filename: TPnBh5movements_basketball_ab.xml
|
46
|
+
#
|
47
|
+
# No players that match global id [3358] and name [Abromaitis, Tim]. Skipping.
|
48
|
+
# league: nba, method: NBA_MOVEMENTS, filename: TPnBh5movements_basketball_ab.xml#
|
49
|
+
#
|
50
|
+
# This problem is addressed by accumulating log lines in the buffer until another start-of-log-
|
51
|
+
# record line appears. In the case that a multi-line log record is the last record, it will
|
52
|
+
# be flushed after a timeout. This assumes that even for a multi-line log record, it's often
|
53
|
+
# that all the lines are written out by the application together as one log record.
|
54
|
+
#
|
55
|
+
|
56
|
+
STDOUT_LOCK = Mutex.new
|
57
|
+
def self.write_log_record(rec)
|
58
|
+
if not rec.nil?
|
59
|
+
STDOUT_LOCK.synchronize do
|
60
|
+
STDOUT.write(rec.to_json << "\n")
|
61
|
+
STDOUT.flush()
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize(timeout=5, &block)
|
67
|
+
@lock = Mutex.new
|
68
|
+
@flush_timeout = timeout
|
69
|
+
|
70
|
+
# buffer to aggregate a multi-line log record
|
71
|
+
# The first item will be a log record and the rest will
|
72
|
+
# be lines that are part of the @message field of the log record.
|
73
|
+
@log_buffer = []
|
74
|
+
|
75
|
+
@last_popped_record = nil
|
76
|
+
|
77
|
+
if not block.nil?
|
78
|
+
Thread.new do
|
79
|
+
begin
|
80
|
+
loop do
|
81
|
+
sleep @flush_timeout
|
82
|
+
rec = pop_push
|
83
|
+
block.call(rec) if not rec.nil?
|
84
|
+
end
|
85
|
+
rescue => e
|
86
|
+
LOG.error(e.backtrace.join("\n"))
|
87
|
+
retry
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# push a log record or part of its message to the buffer.
|
94
|
+
def push_part(part)
|
95
|
+
@lock.synchronize do
|
96
|
+
# LOG.debug("pushing part: #{part}") unless @log_buffer.empty?
|
97
|
+
@log_buffer << part unless @log_buffer.empty?
|
98
|
+
# Note: we skip pushing any partial lines before there's a log record.
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# pops the log record off the buffer and then optionally push a log record into the buffer.
|
103
|
+
# returns the popped record.
|
104
|
+
def pop_push(in_record=nil)
|
105
|
+
rec = nil
|
106
|
+
@lock.synchronize do
|
107
|
+
rec = @log_buffer.shift()
|
108
|
+
unless rec.nil?
|
109
|
+
if not rec.is_a?(Hash)
|
110
|
+
# the only way this can happen is that a timeout happened
|
111
|
+
# and the buffered multi-line log record got popped off before
|
112
|
+
# all its lines were pushed to the buffer.
|
113
|
+
@log_buffer.unshift(rec)
|
114
|
+
rec = @last_popped_record
|
115
|
+
end
|
116
|
+
rest = @log_buffer.join("\n")
|
117
|
+
if not rest.empty?
|
118
|
+
rec['@message'] += "\n" + rest
|
119
|
+
rec['@tags'] << 'multiline'
|
120
|
+
end
|
121
|
+
@log_buffer.clear()
|
122
|
+
@last_popped_record = rec
|
123
|
+
end
|
124
|
+
@log_buffer << in_record unless in_record.nil?
|
125
|
+
end
|
126
|
+
# LOG.debug("Popped #{rec}")
|
127
|
+
# LOG.debug("Pushed #{in_record}")
|
128
|
+
rec
|
129
|
+
end
|
130
|
+
|
131
|
+
end # end MultilineLogBuffer
|
132
|
+
|
133
|
+
|
134
|
+
end # end Log2Json module
|
data/log2json.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'log2json'
|
3
|
-
s.version = '0.1.
|
3
|
+
s.version = '0.1.22'
|
4
4
|
s.summary = "Read, filter and ship logs. ie, poor man's roll-your-own, light-weight logstash replacement."
|
5
5
|
s.description = IO.read(File.join(File.dirname(__FILE__), 'README'))
|
6
6
|
s.authors = ['Jack Kuan']
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: log2json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.22
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: jls-grok
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- lib/log2json/filters/base.patterns
|
126
126
|
- lib/log2json/filters/nginx_access.rb
|
127
127
|
- lib/log2json/filters/syslog.rb
|
128
|
+
- lib/log2json/mlogbuffer.rb
|
128
129
|
- lib/log2json/railslogger.rb
|
129
130
|
- log2json-loggers.gemspec
|
130
131
|
- log2json.gemspec
|