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 CHANGED
@@ -1,2 +1,2 @@
1
1
  source "https://rubygems.org"
2
- gemspec
2
+ gemspec :name => 'log2json'
@@ -1,21 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- log2json (0.1.0)
4
+ log2json (0.1.22)
5
5
  jls-grok (~> 0.10.10)
6
- persistent_http (~> 1.0.5)
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.5.0)
12
+ cabin (0.6.1)
13
13
  gene_pool (1.3.0)
14
- jls-grok (0.10.10)
15
- cabin (~> 0.5.0)
16
- persistent_http (1.0.5)
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.3)
18
+ redis (3.0.6)
19
19
 
20
20
  PLATFORMS
21
21
  ruby
@@ -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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'log2json'
3
- s.version = '0.1.21'
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.21
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-07 00:00:00.000000000 Z
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