fluent-plugin-anomalydetect 0.1.0 → 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 00950f80d0252742704254c65f1f20a145fe967d
4
+ data.tar.gz: 7c9fd9003e576cbb8a51006ef7c5fad2a8b8d626
5
+ SHA512:
6
+ metadata.gz: e0ca5a907d709856442ba77c26315bcb3d8777484e1920389f0cd1c20889bbd8625a4ad9087dd6ddaf8b03cf38815f64f84a368d2577d9a7c3e02451e4c16da8
7
+ data.tar.gz: 89b8175c1797a2800e92710fd9f84b0177239e8ab40dd27b7e9624a3786bb81bc813b98b0011d33503f3f09fac5c1301ed9f17fb401194417bfc922cb86668fc
data/README.rdoc CHANGED
@@ -73,6 +73,15 @@ If you want to know detail of these parameters, see "Theory".
73
73
 
74
74
  If "store_file" option was specified, a historical stat will be stored to the file at shutdown, and it will be restored on started.
75
75
 
76
+
77
+ <match access.**>
78
+ type anomalydetect
79
+ ...
80
+ threshold 3
81
+ </match>
82
+
83
+ If "threshold" option was specified, plugin only ouput when the anomalyscore is more than threshold.
84
+
76
85
  == Theory
77
86
  "データマイニングによる異常検知" http://amzn.to/XHXNun
78
87
 
@@ -3,7 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = "fluent-plugin-anomalydetect"
6
- gem.version = "0.1.0"
6
+ gem.version = "0.1.1"
7
7
  gem.authors = ["Muddy Dixon"]
8
8
  gem.email = ["muddydixon@gmail.com"]
9
9
  gem.description = %q{detect anomal sequential input casually}
@@ -16,6 +16,6 @@ Gem::Specification.new do |gem|
16
16
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
17
  gem.require_paths = ["lib"]
18
18
 
19
- gem.add_development_dependency "fluentd"
19
+ gem.add_development_dependency "rake"
20
20
  gem.add_runtime_dependency "fluentd"
21
21
  end
@@ -43,7 +43,10 @@ module Fluent
43
43
  @data.shift
44
44
  end
45
45
 
46
- score(prob xt, @sigma, x)
46
+ p = prob(xt, @sigma, x)
47
+ s = score(p)
48
+ $log.debug "change_finder:#{Thread.current.object_id} x:#{x} xt:#{xt} p:#{p} s:#{s} term:#{@term} r:#{@r} data:#{@data} mu:#{@mu} sigma:#{@sigma} c:#{@c}"
49
+ s
47
50
  end
48
51
 
49
52
  def prob(mu, sigma, v)
@@ -60,7 +63,7 @@ module Fluent
60
63
  def smooth(size)
61
64
  _end = @data.size
62
65
  _begin = [_end - size, 0].max
63
- @data.slice(_begin, _end).inject(0.0) { |sum, v| sum += v } / (_end - _begin)
66
+ (_size = (_end - _begin)) == 0 ? 0.0 : @data.slice(_begin, _end).inject(:+).to_f / _size
64
67
  end
65
68
 
66
69
  def show_status
@@ -2,7 +2,7 @@ module Fluent
2
2
  class AnomalyDetectOutput < Output
3
3
  Fluent::Plugin.register_output('anomalydetect', self)
4
4
 
5
- require 'fluent/plugin/change_finder'
5
+ require_relative 'change_finder'
6
6
  require 'pathname'
7
7
 
8
8
  config_param :outlier_term, :integer, :default => 28
@@ -64,6 +64,8 @@ module Fluent
64
64
  load_from_file
65
65
  init_records
66
66
  start_watch
67
+ rescue => e
68
+ $log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
67
69
  end
68
70
 
69
71
  def shutdown
@@ -73,6 +75,8 @@ module Fluent
73
75
  @watcher.join
74
76
  end
75
77
  store_to_file
78
+ rescue => e
79
+ $log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
76
80
  end
77
81
 
78
82
  def load_from_file
@@ -93,11 +97,11 @@ module Fluent
93
97
  @outlier_buf = stored[:outlier_buf]
94
98
  @score = stored[:score]
95
99
  else
96
- $log.warn "configuration param was changed. ignore stored data"
100
+ $log.warn "anomalydetect: configuration param was changed. ignore stored data"
97
101
  end
98
102
  end
99
103
  rescue => e
100
- $log.warn "Can't load store_file #{e}"
104
+ $log.warn "anomalydetect: Can't load store_file #{e}"
101
105
  end
102
106
  end
103
107
 
@@ -117,7 +121,7 @@ module Fluent
117
121
  }, f)
118
122
  end
119
123
  rescue => e
120
- $log.warn "Can't write store_file #{e}"
124
+ $log.warn "anomalydetect: Can't write store_file #{e}"
121
125
  end
122
126
  end
123
127
 
@@ -126,16 +130,19 @@ module Fluent
126
130
  end
127
131
 
128
132
  def watch
129
-
130
133
  @last_checked = Fluent::Engine.now
131
- loop {
132
- sleep 0.5
133
- now = Fluent::Engine.now
134
- if now - @last_checked >= @tick
135
- flush_emit(now - @last_checked)
136
- @last_checked = now
134
+ loop do
135
+ begin
136
+ sleep 0.5
137
+ now = Fluent::Engine.now
138
+ if now - @last_checked >= @tick
139
+ flush_emit(now - @last_checked)
140
+ @last_checked = now
141
+ end
142
+ rescue => e
143
+ $log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
137
144
  end
138
- }
145
+ end
139
146
  end
140
147
 
141
148
  def init_records
@@ -155,14 +162,20 @@ module Fluent
155
162
  val = if @record_count
156
163
  flushed.size
157
164
  else
158
- flushed.inject(0.0) { |sum, record| sum += record[@target].to_f if record[@target] } / flushed.size
165
+ filtered = flushed.map {|record| record[@target] }.compact
166
+ return nil if filtered.empty?
167
+ filtered.inject(:+).to_f / filtered.size
159
168
  end
160
169
 
161
170
  outlier = @outlier.next(val)
171
+
162
172
  @outlier_buf.push outlier
163
173
  @outlier_buf.shift if @outlier_buf.size > @smooth_term
164
- score = @score.next(@outlier_buf.inject(0) { |sum, v| sum += v } / @outlier_buf.size)
174
+ outlier_avg = @outlier_buf.empty? ? 0.0 : @outlier_buf.inject(:+).to_f / @outlier_buf.size
175
+
176
+ score = @score.next(outlier_avg)
165
177
 
178
+ $log.debug "out_anomalydetect:#{Thread.current.object_id} flushed:#{flushed} val:#{val} outlier:#{outlier} outlier_buf:#{@outlier_buf} score:#{score}"
166
179
  if @threshold < 0 or (@threshold >= 0 and score > @threshold)
167
180
  {"outlier" => outlier, "score" => score, "target" => val}
168
181
  else
@@ -185,6 +198,8 @@ module Fluent
185
198
  push_records records
186
199
 
187
200
  chain.next
201
+ rescue => e
202
+ $log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
188
203
  end
189
204
  end
190
205
  end
@@ -108,8 +108,7 @@ class AnomalyDetectOutputTest < Test::Unit::TestCase
108
108
  smooth_term 3
109
109
  ]
110
110
 
111
- data = (0..10).map do || (rand * 100).to_i end
112
- data.push 0
111
+ data = 10.times.map { (rand * 100).to_i } + [0]
113
112
  d.run do
114
113
  data.each do |val|
115
114
  (0..val - 1).each do ||
@@ -121,6 +120,49 @@ class AnomalyDetectOutputTest < Test::Unit::TestCase
121
120
  end
122
121
  end
123
122
 
123
+ def test_emit_target
124
+ d = create_driver %[
125
+ tag test.anomaly
126
+ outlier_term 28
127
+ outlier_discount 0.05
128
+ score_term 28
129
+ score_discount 0.05
130
+ tick 10
131
+ smooth_term 3
132
+ target y
133
+ ]
134
+
135
+ data = 10.times.map { (rand * 100).to_i } + [0]
136
+ d.run do
137
+ data.each do |val|
138
+ d.emit({'y' => val})
139
+ r = d.instance.flush
140
+ assert_equal val, r['target']
141
+ end
142
+ end
143
+ end
144
+
145
+ def test_emit_when_target_does_not_exist
146
+ d = create_driver %[
147
+ tag test.anomaly
148
+ outlier_term 28
149
+ outlier_discount 0.05
150
+ score_term 28
151
+ score_discount 0.05
152
+ tick 10
153
+ smooth_term 3
154
+ target y
155
+ ]
156
+
157
+ d.run do
158
+ 10.times do
159
+ d.emit({'foobar' => 999.99})
160
+ r = d.instance.flush
161
+ assert_equal nil, r
162
+ end
163
+ end
164
+ end
165
+
124
166
  def test_emit_stock_data
125
167
  require 'csv'
126
168
  reader = CSV.open("test/stock.2432.csv", "r")
metadata CHANGED
@@ -1,46 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-anomalydetect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Muddy Dixon
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-21 00:00:00.000000000 Z
11
+ date: 2013-08-09 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: fluentd
14
+ name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: fluentd
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  description: detect anomal sequential input casually
@@ -62,30 +57,28 @@ files:
62
57
  - test/stock.2432.csv
63
58
  homepage: https://github.com/muddydixon/fluent-plugin-anomalydetect
64
59
  licenses: []
60
+ metadata: {}
65
61
  post_install_message:
66
62
  rdoc_options: []
67
63
  require_paths:
68
64
  - lib
69
65
  required_ruby_version: !ruby/object:Gem::Requirement
70
- none: false
71
66
  requirements:
72
- - - ! '>='
67
+ - - '>='
73
68
  - !ruby/object:Gem::Version
74
69
  version: '0'
75
70
  required_rubygems_version: !ruby/object:Gem::Requirement
76
- none: false
77
71
  requirements:
78
- - - ! '>='
72
+ - - '>='
79
73
  - !ruby/object:Gem::Version
80
74
  version: '0'
81
75
  requirements: []
82
76
  rubyforge_project:
83
- rubygems_version: 1.8.24
77
+ rubygems_version: 2.0.0
84
78
  signing_key:
85
- specification_version: 3
79
+ specification_version: 4
86
80
  summary: detect anomal sequential input casually
87
81
  test_files:
88
82
  - test/helper.rb
89
83
  - test/plugin/test_out_anomalydetect.rb
90
84
  - test/stock.2432.csv
91
- has_rdoc: