dummy_log_generator 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a76a35eb0df876125713ea8afd7d3128368e73b
4
- data.tar.gz: e97dc9abc1fff91fcb1e4c6b5b8179b9c25cf596
3
+ metadata.gz: edaffe0f73cbc9aafc71dc10db3eeb80bfed02d6
4
+ data.tar.gz: 807e80562f29882db31edee29b8dac692e098caa
5
5
  SHA512:
6
- metadata.gz: 65200ca5573a8f275ee1dfd1c5869ce30d1e5840737ff55eefc5c45d90c0d9b7c2729976274e6d8a549183c6337d29c3484e2bbf9f4b1aa84e407752f9bd586a
7
- data.tar.gz: 15921096adbdd0d1214a7a6afb0b9d29a74b5be39da3af8b4a0b01918597c671d903d044658fa98f7dfeca88289d7ad05e9efc9e007294e2392f4cf0cf2c4cab
6
+ metadata.gz: a229cbbf0c5863bd2d5a17417d3074d73f6f71c58f53a763a81959f4dd79f4b6e356454ccc7414a2094c90974e950f6ad18203ad6d68174cc51f7e9dfe6c1a4b
7
+ data.tar.gz: 7b220a87b3e6296d436198e16be03ce7fd2a2a3b391f0f8a925e474c3fb04d007006da2ee627a982751ff43956bfbc7923c5bf4f3e1f9c402133fc3239af14b9
data/.gitignore CHANGED
@@ -25,4 +25,4 @@ test/tmp
25
25
  test/version_tmp
26
26
  dummy_log_generator.pid
27
27
  dummy_log_generator.log
28
-
28
+ dummy.log
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ### 0.2.0
2
+
3
+ Enhancement:
4
+
5
+ * Add `message` option to write only a specific message
6
+ * Add `input` option to write messges by reading lines of an input file in roration
7
+ * Add `dummy_log_generator_simple` command
8
+ * Add `dummy_log_generator_yes` command
9
+
1
10
  ### 0.1.0
2
11
 
3
12
  Enhancement:
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in dummy_log_generator.gemspec
4
3
  gemspec
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # DummyLogGenerator
2
2
 
3
- Generates dummy log data for Fluentd benchmark
3
+ Generates dummy log data for Fluentd benchmark.
4
+
5
+ This gem includes three executable commands
6
+
7
+ 1. dummy\_log\_generator
8
+ 2. dummy\_log\_generator\_simple
9
+ 3. dummy\_log\_generator\_yes
4
10
 
5
11
  ## Installation
6
12
 
@@ -19,10 +25,20 @@ Or install it yourself as:
19
25
  Run as
20
26
 
21
27
  $ dummy_log_generator -c dummy_data_generator.conf
28
+ $ dummy_log_generator_simple [options]
29
+ $ dummy_log_generator_yes [options]
30
+
31
+ ## dummy\_log\_generator
32
+
33
+ `dummy_log_generator` allows you to
34
+
35
+ 1. specify a rate of generating messages per second,
36
+ 2. determine a log format, and
37
+ 3. generate logs randomly
22
38
 
23
- ## Usage
39
+ ### Usage
24
40
 
25
- Sample configuration is as follows:
41
+ Create a configuration file. A sample configuration is as follows:
26
42
 
27
43
  ```ruby
28
44
  # dummy_log_generator.conf
@@ -41,7 +57,13 @@ configure 'sample' do
41
57
  end
42
58
  ```
43
59
 
44
- Running dummy_log_generator outputs to `dummy.log` like
60
+ Running
61
+
62
+ ```
63
+ $ dummy_log_genrator -c dummy_log_generator.conf
64
+ ```
65
+
66
+ Outputs to the `dummy.log` (specified by `output` parameter) file like:
45
67
 
46
68
  ```
47
69
  id:422 time:[2013-11-19 02:34:58] level:INFO method:POST uri:/api/v1/textdata reqtime:3.9726677258569842 foobar:LFK6XV1N
@@ -49,9 +71,9 @@ id:423 time:[2013-11-19 02:34:58] level:DEBUG method:GET uri:/api/v1/people
49
71
  id:424 time:[2013-11-19 02:34:58] level:WARN method:POST uri:/api/v1/textdata reqtime:2.930590441869852 foobar:XEZ5bQsh
50
72
  ```
51
73
 
52
- ## Configuration Parameters
74
+ ### Configuration Parameters
53
75
 
54
- Following parameters for configuration is available
76
+ Following parameters in the configuration file are available:
55
77
 
56
78
  * output
57
79
 
@@ -61,6 +83,10 @@ Following parameters for configuration is available
61
83
 
62
84
  Specify how many messages to generate per second. Default: 500 msgs / sec
63
85
 
86
+ * workers
87
+
88
+ Specify number of processes for parallel processing.
89
+
64
90
  * delimiter
65
91
 
66
92
  Specify the delimiter between each field. Default: "\t" (Tab)
@@ -73,7 +99,15 @@ Following parameters for configuration is available
73
99
 
74
100
  Define data fields to generate
75
101
 
76
- ## Field Data Types
102
+ * message
103
+
104
+ Use this if you want to write only a specific message. `field` is ignored.
105
+
106
+ * input
107
+
108
+ Use this if you want to write messages by reading lines of an input file in rotation. `field` and `message` are ignored.
109
+
110
+ ### Field Data Types
77
111
 
78
112
  You can specify following data types to your `field` parameters:
79
113
 
@@ -137,6 +171,64 @@ You can specify following data types to your `field` parameters:
137
171
 
138
172
  You can specify a fixed float number
139
173
 
174
+ ## dummy\_log\_generator\_simple
175
+
176
+ I created a simple version of `dummy_log_generator` since it can not achieve the maximum system I/O throughputs because of its rich features.
177
+ This simple version, `dummy_log_generator_simple` could achieve the system I/O limit in my environment.
178
+
179
+ ### Usage
180
+
181
+ ```
182
+ $ dummy_log_genrator_simple [options]
183
+ ```
184
+
185
+ ### Options
186
+
187
+ ```
188
+ Usage:
189
+ dummy_log_generator_simple
190
+
191
+ Options:
192
+ [--sync] # Use fsync(2) or not
193
+ -s, [--second=N] # Duration of running in second
194
+ # Default: 1
195
+ -p, [--parallel=N] # Number of processes to run in parallel
196
+ # Default: 1
197
+ -o, [--output=OUTPUT] # Output file
198
+ # Default: dummy.log
199
+ -i, [--input=INPUT] # Input file (Output messages by reading lines of the file in rotation)
200
+ -m, [--message=MESSAGE] # Output message
201
+ # Default: time:2013-11-20 23:39:42 +0900 level:ERROR method:POST uri:/api/v1/people reqtime:3.1983877060667103
202
+ ```
203
+
204
+ ## dummy\_log\_generator\_yes
205
+
206
+ I created a wrapped version of `yes` command, `dummy_log_generator_yes`, to confrim that `dummy_log_generator_simple` achieves the maximum system I/O throughputs.
207
+ I do not use `dummy_log_generator_yes` command anymore because I verified that `dummy_log_generator_simple` achieves the I/O limit, but I will keep this command so that users can do verification experiments with it.
208
+
209
+ ### Usage
210
+
211
+ ```
212
+ $ dummy_log_genrator_yes [options]
213
+ ```
214
+
215
+ ### Options
216
+
217
+ ```
218
+ Usage:
219
+ dummy_log_generator_yes
220
+
221
+ Options:
222
+ -s, [--second=N] # Duration of running in second
223
+ # Default: 1
224
+ -p, [--parallel=N] # Number of processes to run in parallel
225
+ # Default: 1
226
+ -o, [--output=OUTPUT] # Output file
227
+ # Default: dummy.log
228
+ -m, [--message=MESSAGE] # Output message
229
+ # Default: time:2013-11-20 23:39:42 +0900 level:ERROR method:POST uri:/api/v1/people reqtime:3.1983877060667103
230
+ ```
231
+
140
232
  ## Relatives
141
233
 
142
234
  There is a [fluent-plugin-dummydata-producer](https://github.com/tagomoris/fluent-plugin-dummydata-producer), but I wanted to output dummy data to a log file, and I wanted a standalone tool.
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+
4
+ module DummyLogGeneratorSimple
5
+ class CLI < Thor
6
+ default_command :start
7
+ desc "start", "Start a dummy_log_generator"
8
+ option :sync, :type => :boolean, :desc => 'Use fsync(2) or not'
9
+ option :second, :aliases => ["-s"], :type => :numeric, :default => 1, :desc => 'Duration of running in second'
10
+ option :parallel, :aliases => ["-p"], :type => :numeric, :default => 1, :desc => 'Number of processes to run in parallel'
11
+ option :output, :aliases => ["-o"], :type => :string, :default => 'dummy.log', :desc => 'Output file'
12
+ option :input, :aliases => ["-i"], :type => :string, :desc => 'Input file (Output messages by reading lines of the file in rotation)'
13
+ option :message, :aliases => ["-m"], :type => :string, :desc => 'Output message',
14
+ :default => "time:2013-11-20 23:39:42 +0900\tlevel:ERROR\tmethod:POST\turi:/api/v1/people\treqtime:3.1983877060667103\n"
15
+ def start
16
+ sync = @options[:sync]
17
+ parallel = @options[:parallel]
18
+ output = @options[:output]
19
+ message = "#{@options[:message].chomp}\n"
20
+ messages = nil
21
+ if input = @options[:input] and (messages = readlines(input)).nil?
22
+ STDERR.puts "Input file `#{input}` does not exist or is not readable."
23
+ exit 1
24
+ end
25
+
26
+ open(output, (File::WRONLY | File::APPEND | File::CREAT)) do |out_file|
27
+ out_file.sync = true if sync
28
+ if messages
29
+ wait_and_kill { write_messages(out_file, messages, parallel) }
30
+ else
31
+ wait_and_kill { write_message(out_file, message, parallel) }
32
+ end
33
+ end
34
+ end
35
+
36
+ no_commands {
37
+ def wait_and_kill(&block)
38
+ second = Time.now + @options[:second]
39
+ pids = yield # fork
40
+ while Time.now < second do; sleep 0.01; end
41
+ pids.each {|pid| Process.kill(:TERM, pid) }
42
+ end
43
+
44
+ def write_message(out_file, message, parallel = 1)
45
+ parallel.times.map do
46
+ Process.fork do
47
+ while true do
48
+ out_file.write message
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def write_messages(out_file, messages, parallel = 1)
55
+ size = messages.size
56
+ parallel.times.map do
57
+ Process.fork do
58
+ idx = -1 # create this in child-process not to be shared
59
+ while true do
60
+ idx = (idx + 1) % size
61
+ out_file.write messages[idx]
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def readlines(input)
68
+ messages = nil
69
+ begin
70
+ open(input) do |in_file|
71
+ messages = in_file.readlines
72
+ end
73
+ rescue Errno::ENOENT
74
+ end
75
+ messages
76
+ end
77
+ }
78
+ end
79
+ end
80
+
81
+ DummyLogGeneratorSimple::CLI.start(ARGV)
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+
4
+ module DummyLogGeneratorYes
5
+ class CLI < Thor
6
+ default_command :start
7
+ desc "start", "Start a dummy_log_generator"
8
+ option :second, :aliases => ["-s"], :type => :numeric, :default => 1, :desc => 'Duration of running in second'
9
+ option :parallel, :aliases => ["-p"], :type => :numeric, :default => 1, :desc => 'Number of processes to run in parallel'
10
+ option :output, :aliases => ["-o"], :type => :string, :default => 'dummy.log', :desc => 'Output file'
11
+ option :message, :aliases => ["-m"], :type => :string, :desc => 'Output message',
12
+ :default => "time:2013-11-20 23:39:42 +0900\tlevel:ERROR\tmethod:POST\turi:/api/v1/people\treqtime:3.1983877060667103\n"
13
+ def start
14
+ parallel = @options[:parallel]
15
+ output = @options[:output]
16
+ message = "#{@options[:message].chomp}\n"
17
+ wait_and_kill {
18
+ parallel.times.map do
19
+ spawn("yes #{message}", :out => [output, (File::WRONLY | File::APPEND | File::CREAT)])
20
+ end
21
+ }
22
+ end
23
+
24
+ no_commands {
25
+ def wait_and_kill(&block)
26
+ second = Time.now + @options[:second]
27
+ pids = yield # fork
28
+ while Time.now < second do; sleep 0.01; end
29
+ pids.each {|pid| Process.kill(:TERM, pid) }
30
+ end
31
+ }
32
+ end
33
+ end
34
+
35
+ DummyLogGeneratorYes::CLI.start(ARGV)
36
+
@@ -15,6 +15,7 @@ module DummyLogGenerator
15
15
 
16
16
  desc "start", "Start a dummy_log_generator"
17
17
  option :config, :aliases => ["-c"], :type => :string, :default => 'dummy_log_generator.conf'
18
+ option :rate, :aliases => ["-r"], :type => :numeric
18
19
  # options for serverengine
19
20
  option :daemonize, :aliases => ["-d"], :type => :boolean
20
21
  option :workers, :aliases => ["-w"], :type => :numeric
@@ -24,6 +25,7 @@ module DummyLogGenerator
24
25
  if options[:config] && File.exists?(options[:config])
25
26
  dsl = instance_eval(File.read(options[:config]), options[:config])
26
27
  @options[:setting] = dsl.setting
28
+ @options[:rate] ||= dsl.setting.rate
27
29
  # options for serverengine
28
30
  @options[:workers] ||= dsl.setting.workers
29
31
  end
@@ -6,29 +6,17 @@ module DummyLogGenerator
6
6
  @setting = Setting.new
7
7
  end
8
8
 
9
- def rate(rate)
10
- setting.rate = rate
11
- end
12
-
13
- def output(output)
14
- setting.output = output
9
+ def method_missing(name, *args)
10
+ if @setting.respond_to?("#{name}=")
11
+ @setting.__send__("#{name}=", *args)
12
+ else
13
+ raise ConfigError.new("Config parameter `#{name}` does not exist")
14
+ end
15
15
  end
16
16
 
17
17
  def field(name, opts)
18
18
  setting.fields[name] = opts
19
19
  end
20
-
21
- def delimiter(delimiter)
22
- setting.delimiter = delimiter
23
- end
24
-
25
- def labeled(labeled)
26
- setting.labeled = labeled
27
- end
28
-
29
- def workers(workers)
30
- setting.workers = workers
31
- end
32
20
  end
33
21
  end
34
22
 
@@ -1,44 +1,80 @@
1
1
  module DummyLogGenerator
2
2
  class Generator
3
3
  def initialize(setting)
4
- prepare_format_proc(setting.labeled, setting.delimiter)
5
- prepare_field_procs(setting.fields)
4
+ @message_proc =
5
+ if input = setting.input
6
+ prepare_message_proc_for_input(input)
7
+ elsif message = setting.message
8
+ prepare_message_proc_for_message(message)
9
+ else
10
+ fields, labeled, delimiter = setting.fields, setting.labeled, setting.delimiter
11
+ prepare_message_proc_for_fields(fields, labeled, delimiter)
12
+ end
6
13
  end
7
14
 
8
- def prepare_format_proc(labeled, delimiter)
9
- @format_proc =
10
- if labeled
11
- Proc.new {|fields| fields.map {|key, val| "#{key}:#{val}" }.join(delimiter) }
12
- else
13
- Proc.new {|fields| fields.values.join(delimiter) }
15
+ def prepare_message_proc_for_input(input)
16
+ messages = nil
17
+ begin
18
+ open(input) do |in_file|
19
+ messages = in_file.readlines
14
20
  end
21
+ rescue Errno::ENOENT
22
+ raise ConfigError.new("Input file `#{input}` is not readable")
23
+ end
24
+ idx = -1
25
+ size = messages.size
26
+ Proc.new {
27
+ idx = (idx + 1) % size
28
+ messages[idx]
29
+ }
30
+ end
31
+
32
+ def prepare_message_proc_for_message(message)
33
+ message = "#{message.chomp}\n"
34
+ Proc.new { message }
35
+ end
36
+
37
+ def prepare_message_proc_for_fields(fields, labeled, delimiter)
38
+ format_proc = prepare_format_proc(labeled, delimiter)
39
+ field_procs = prepare_field_procs(fields)
40
+
41
+ prev_data = {}
42
+ Proc.new {
43
+ data = {}
44
+ field_procs.each do |key, proc|
45
+ prev = prev_data[key] || -1
46
+ data[key] = proc.call(prev)
47
+ end
48
+ prev_data = data
49
+ format_proc.call(data)
50
+ }
51
+ end
52
+
53
+ def prepare_format_proc(labeled, delimiter)
54
+ if labeled
55
+ Proc.new {|fields| "#{fields.map {|key, val| "#{key}:#{val}" }.join(delimiter)}\n" }
56
+ else
57
+ Proc.new {|fields| "#{fields.values.join(delimiter)}\n" }
58
+ end
15
59
  end
16
60
 
17
61
  def prepare_field_procs(fields)
18
62
  rand = ::DummyLogGenerator::Random.new
19
- @field_procs = {}
63
+ field_procs = {}
20
64
  fields.each do |key, opts|
21
65
  opts = opts.dup
22
66
  type = opts.delete(:type)
23
67
  if rand.respond_to?(type)
24
- @field_procs[key] = rand.send(type, opts)
68
+ field_procs[key] = rand.send(type, opts)
25
69
  else
26
70
  raise ConfigError.new(type)
27
71
  end
28
72
  end
73
+ field_procs
29
74
  end
30
75
 
31
- def generate(prev_data = {})
32
- data = {}
33
- @field_procs.each do |key, proc|
34
- prev = prev_data[key] || -1
35
- data[key] = proc.call(prev)
36
- end
37
- data
38
- end
39
-
40
- def format(fields)
41
- @format_proc.call(fields)
76
+ def generate
77
+ @message_proc.call
42
78
  end
43
79
  end
44
80
 
@@ -1,6 +1,6 @@
1
1
  module DummyLogGenerator
2
2
  class Setting
3
- attr_accessor :rate, :output, :labeled, :delimiter, :fields, :workers
3
+ attr_accessor :rate, :output, :labeled, :delimiter, :fields, :workers, :message, :input
4
4
 
5
5
  def initialize
6
6
  @rate = 500
@@ -9,6 +9,8 @@ module DummyLogGenerator
9
9
  @delimiter = "\t"
10
10
  @fields = {}
11
11
  @workers = 1
12
+ @message = nil
13
+ @input = nil
12
14
  end
13
15
  end
14
16
  end
@@ -1,3 +1,3 @@
1
1
  module DummyLogGenerator
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -2,13 +2,15 @@ require 'serverengine'
2
2
 
3
3
  module DummyLogGenerator
4
4
  module Worker
5
+ BIN_NUM = 10
6
+
5
7
  def initialize
6
8
  reload
7
9
  end
8
10
 
9
11
  def reload
10
12
  @generator = Generator.new(config[:setting])
11
- @rate = config[:setting].rate
13
+ @rate = config[:rate]
12
14
 
13
15
  output = config[:setting].output
14
16
  if output.respond_to?(:write) and output.respond_to?(:close)
@@ -19,27 +21,19 @@ module DummyLogGenerator
19
21
  end
20
22
  end
21
23
 
22
- # thanks! ref. https://github.com/tagomoris/fluent-plugin-dummydata-producer/blob/a550fd4424f71cd9227e138c3c89f600ba40a0d5/lib/fluent/plugin/in_dummydata_producer.rb#L63
23
24
  def run
24
- batch_num = (@rate / 9).to_i + 1
25
- prev_data = {}
25
+ batch_num = (@rate / BIN_NUM).to_i
26
+ residual_num = (@rate % BIN_NUM)
26
27
  while !@stop
27
28
  current_time = Time.now.to_i
28
- rate_count = 0
29
-
30
- while !@stop && rate_count < @rate && Time.now.to_i == current_time
31
- batch_num.times do
32
- # ToDo: what if generation is slower than I/O?
33
- # We may should generate data and output in parallel
34
- prev_data = @generator.generate(prev_data)
35
- @output.write "#{@generator.format(prev_data)}\n"
36
- end
37
- rate_count += batch_num
38
- sleep 0.1
29
+ BIN_NUM.times do
30
+ break unless (!@stop && Time.now.to_i <= current_time)
31
+ wait(0.1) { write(batch_num) }
39
32
  end
33
+ write(residual_num)
40
34
  # wait for next second
41
- while !@stop && Time.now.to_i == current_time
42
- sleep 0.04
35
+ while !@stop && Time.now.to_i <= current_time
36
+ sleep 0.01
43
37
  end
44
38
  end
45
39
  ensure
@@ -49,5 +43,18 @@ module DummyLogGenerator
49
43
  def stop
50
44
  @stop = true
51
45
  end
46
+
47
+ private
48
+
49
+ def write(num)
50
+ num.times { @output.write @generator.generate }
51
+ end
52
+
53
+ def wait(time)
54
+ start_time = Time.now
55
+ yield
56
+ sleep_time = time - (Time.now - start_time)
57
+ sleep sleep_time if sleep_time > 0
58
+ end
52
59
  end
53
60
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dummy_log_generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sonots
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-19 00:00:00.000000000 Z
11
+ date: 2013-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -113,6 +113,8 @@ email:
113
113
  - sonots@gmail.com
114
114
  executables:
115
115
  - dummy_log_generator
116
+ - dummy_log_generator_simple
117
+ - dummy_log_generator_yes
116
118
  extensions: []
117
119
  extra_rdoc_files: []
118
120
  files:
@@ -124,6 +126,8 @@ files:
124
126
  - README.md
125
127
  - Rakefile
126
128
  - bin/dummy_log_generator
129
+ - bin/dummy_log_generator_simple
130
+ - bin/dummy_log_generator_yes
127
131
  - dummy_log_generator.conf
128
132
  - dummy_log_generator.gemspec
129
133
  - lib/dummy_log_generator.rb