dummy_log_generator 0.1.0 → 0.2.0

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 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