fluentd-ui 0.3.14 → 0.3.15

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.

Potentially problematic release.


This version of fluentd-ui might be problematic. Click here for more details.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6bc6e3afc054e9c31d53f4cbcdf624a5747b86b8
4
+ data.tar.gz: 92a4781f7b6ee8a42f44e99bd7ce1b52a74c2c63
5
+ SHA512:
6
+ metadata.gz: 989ffd56a8d254b321a3b7c267fff4876a0e1ae35dee49181074799866f3b4d17b61a5532f18f72acb13ef7539729a4899693ffc8a4985d8edee68087895e1cb
7
+ data.tar.gz: b781c709a136ab244b0d117977935e92790ed11860ec949ecaf688def91689ea6f8161724f07d4e569d3ba1f449d5a62bda41946f4c1f46b082a75e181573590
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ Release 0.3.15 - 2015/03/11
2
+ * [fixed] #159 Fix latest out_s3 plugin compatibility
3
+ * [fixed] #160 Add validation that `buffer_path` required if `buffer_type` is file
4
+ * [maintenance] Minor refactors
5
+
1
6
  Release 0.3.14 - 2015/02/04
2
7
  * [maintenance] #150, #151 minor fix in README.md.
3
8
  * [maintenance] #149 Make circle-ci result stable.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fluentd-ui (0.3.14)
4
+ fluentd-ui (0.3.15)
5
5
  addressable
6
6
  bundler
7
7
  diff-lcs
@@ -141,7 +141,7 @@ GEM
141
141
  mime-types (2.4.3)
142
142
  mini_portile (0.6.2)
143
143
  minitest (5.5.1)
144
- msgpack (0.5.10)
144
+ msgpack (0.5.11)
145
145
  multi_json (1.10.1)
146
146
  net-http-digest_auth (1.4)
147
147
  net-http-persistent (2.9.4)
@@ -22,7 +22,7 @@ module SettingConcern
22
22
  @fluentd.agent.config_append @setting.to_config
23
23
  if @fluentd.agent.running?
24
24
  unless @fluentd.agent.restart
25
- @setting.errors.add(:base, @fluentd.agent.log_tail(1).first)
25
+ @setting.errors.add(:base, @fluentd.agent.log.tail(1).first)
26
26
  return render "shared/settings/show"
27
27
  end
28
28
  end
@@ -26,7 +26,7 @@ module SettingHistoryConcern
26
26
  if @fluentd.agent.dryrun
27
27
  flash = { success: t('messages.dryrun_is_passed') }
28
28
  else
29
- flash = { danger: @fluentd.agent.log_tail(1).first }
29
+ flash = { danger: @fluentd.agent.log.tail(1).first }
30
30
  end
31
31
  redirect_to :back, flash: flash
32
32
  end
@@ -2,7 +2,7 @@ class Fluentd::AgentsController < ApplicationController
2
2
  before_action :find_fluentd
3
3
 
4
4
  def start
5
- run_action(__method__) { @fluentd.agent.log_tail(1).first }
5
+ run_action(__method__) { @fluentd.agent.log.tail(1).first }
6
6
  redirect_to daemon_path(@fluentd), status: 303 # 303 is change HTTP Verb GET
7
7
  end
8
8
 
@@ -12,12 +12,12 @@ class Fluentd::AgentsController < ApplicationController
12
12
  end
13
13
 
14
14
  def restart
15
- run_action(__method__) { @fluentd.agent.log_tail(1).first }
15
+ run_action(__method__) { @fluentd.agent.log.tail(1).first }
16
16
  redirect_to daemon_path(@fluentd), status: 303 # 303 is change HTTP Verb GET
17
17
  end
18
18
 
19
19
  def log_tail
20
- @logs = @fluentd.agent.log_tail(params[:limit]).reverse if @fluentd
20
+ @logs = @fluentd.agent.log.tail(params[:limit]).reverse if @fluentd
21
21
  render json: @logs
22
22
  end
23
23
 
@@ -39,7 +39,7 @@ class Fluentd::SettingsController < ApplicationController
39
39
  if dryrun(params[:config])
40
40
  flash.now[:success] = I18n.t('messages.dryrun_is_passed')
41
41
  else
42
- flash.now[:danger] = @fluentd.agent.last_error_message
42
+ flash.now[:danger] = @fluentd.agent.log.last_error_message
43
43
  end
44
44
  @config = params[:config]
45
45
  render "edit"
@@ -41,11 +41,11 @@ class FluentdController < ApplicationController
41
41
 
42
42
  def errors
43
43
  @error_duration_days = 5
44
- @errors = @fluentd.agent.errors_since(@error_duration_days.days.ago)
44
+ @errors = @fluentd.agent.log.errors_since(@error_duration_days.days.ago)
45
45
  end
46
46
 
47
47
  def raw_log
48
- send_data @fluentd.agent.log, type: "application/octet-stream", filename: File.basename(@fluentd.log_file)
48
+ send_data @fluentd.agent.log.read, type: "application/octet-stream", filename: File.basename(@fluentd.log_file)
49
49
  end
50
50
 
51
51
  private
@@ -38,7 +38,7 @@ class MiscController < ApplicationController
38
38
  File.unlink(path) if File.exists?(path)
39
39
 
40
40
  Zip::File.open(path, Zip::File::CREATE) do |zip|
41
- zip.get_output_stream('fluentd.log') {|f| f.puts fluentd.agent.log }
41
+ zip.get_output_stream('fluentd.log') {|f| f.puts fluentd.agent.log.read }
42
42
  zip.add("fluentd-ui.log", log_path)
43
43
 
44
44
  add_env_file_to(zip)
@@ -5,7 +5,7 @@ class TutorialsController < ApplicationController
5
5
  helper_method :tutorial_ready?
6
6
 
7
7
  def index
8
- @log = @fluentd.agent.log_tail.reverse if @fluentd
8
+ @log = @fluentd.agent.log.tail.reverse if @fluentd
9
9
  end
10
10
 
11
11
  def chapter1
@@ -19,34 +19,19 @@ class Fluentd
19
19
  module Common
20
20
  attr_reader :extra_options
21
21
 
22
- def initialize(options = {})
23
- @extra_options = options
24
- end
25
-
26
- def wait_process_starting_seconds
27
- 10.seconds # wait time for fluentd pidfile created
28
- end
29
-
30
- def errors_since(since = 1.day.ago)
31
- errors = []
32
- logged_errors do |error|
33
- break if Time.parse(error[:subject]) < since
34
- errors << error
35
- end
36
- errors
22
+ def self.included(base)
23
+ base.send(:include, Fluentd::Agent::ProcessOperation)
37
24
  end
38
25
 
39
- def recent_errors(limit = 3)
40
- errors = []
41
- logged_errors do |error|
42
- errors << error
43
- break if errors.length >= limit
26
+ # define these methods on each Agent class
27
+ %w(start stop restart version).each do |method|
28
+ define_method(method) do
29
+ raise NotImplementedError, "'#{method}' method is required to be defined"
44
30
  end
45
- errors
46
31
  end
47
32
 
48
- def last_error_message
49
- recent_errors(1).first.try(:[], :subject) || ""
33
+ def initialize(options = {})
34
+ @extra_options = options
50
35
  end
51
36
 
52
37
  def pid_file
@@ -57,16 +42,59 @@ class Fluentd
57
42
  extra_options[:log_file] || self.class.default_options[:log_file]
58
43
  end
59
44
 
45
+ def log
46
+ @log ||= FluentdLog.new(log_file)
47
+ end
48
+
60
49
  def config_file
61
50
  extra_options[:config_file] || self.class.default_options[:config_file]
62
51
  end
63
52
 
53
+ # -- config
54
+ def config
55
+ File.read(config_file)
56
+ end
57
+
58
+ def config_write(content)
59
+ backup_config
60
+ File.open(config_file, "w") do |f|
61
+ f.write content
62
+ end
63
+ end
64
+
65
+ def config_append(content)
66
+ backup_config
67
+ File.open(config_file, "a") do |f|
68
+ f.write "\n"
69
+ f.write content
70
+ end
71
+ end
72
+
73
+ def configuration
74
+ if File.exists? config_file
75
+ ::Fluentd::Agent::Configuration.new(config_file)
76
+ end
77
+ end
78
+
79
+ # -- backup methods
64
80
  def config_backup_dir
65
81
  dir = File.join(FluentdUI.data_dir, "#{Rails.env}_confg_backups")
66
82
  FileUtils.mkdir_p(dir)
67
83
  dir
68
84
  end
69
85
 
86
+ def backup_files
87
+ Dir.glob(File.join("#{config_backup_dir}", "*.conf"))
88
+ end
89
+
90
+ def backup_files_in_old_order
91
+ backup_files.sort
92
+ end
93
+
94
+ def backup_files_in_new_order
95
+ backup_files_in_old_order.reverse
96
+ end
97
+
70
98
  def running_config_backup_dir
71
99
  dir = File.join(FluentdUI.data_dir, "#{Rails.env}_running_confg_backup")
72
100
  FileUtils.mkdir_p(dir)
@@ -77,20 +105,7 @@ class Fluentd
77
105
  File.join(running_config_backup_dir, "running.conf")
78
106
  end
79
107
 
80
- # define these methods on each Agent class
81
-
82
- %w(start stop restart).each do |method|
83
- define_method(method) do
84
- raise NotImplementedError, "'#{method}' method is required to be defined"
85
- end
86
- end
87
-
88
- %w(running? version log config config_write config_append log_tail configuration).each do |method|
89
- define_method(method) do
90
- raise NotImplementedError, "'#{method}' method is required to be defined"
91
- end
92
- end
93
-
108
+ # -------------- private --------------
94
109
  private
95
110
 
96
111
  def backup_running_config
@@ -103,6 +118,26 @@ class Fluentd
103
118
 
104
119
  true
105
120
  end
121
+
122
+ def backup_config
123
+ return unless File.exists? config_file
124
+
125
+ FileUtils.cp config_file, File.join(config_backup_dir, "#{Time.zone.now.strftime('%Y%m%d_%H%M%S')}.conf")
126
+
127
+ remove_over_backup_files
128
+ end
129
+
130
+ def remove_over_backup_files
131
+ over_file_count = backup_files.size - ::Settings.max_backup_files_count
132
+
133
+ return if over_file_count <= 0
134
+
135
+ backup_files_in_old_order.first(over_file_count).each do |file|
136
+ note_file_attached_backup = file.sub(/#{Regexp.escape(File.extname(file))}\z/, ::Fluentd::SettingArchive::Note::FILE_EXTENSION)
137
+ FileUtils.rm(note_file_attached_backup) if File.exist? note_file_attached_backup
138
+ FileUtils.rm(file) if File.exist? file
139
+ end
140
+ end
106
141
  end
107
142
  end
108
143
  end
@@ -2,7 +2,6 @@ class Fluentd
2
2
  class Agent
3
3
  class FluentdGem
4
4
  include Common
5
- include LocalCommon
6
5
 
7
6
  def self.default_options
8
7
  {
@@ -103,6 +102,10 @@ class Fluentd
103
102
  false
104
103
  end
105
104
  end
105
+
106
+ def wait_process_starting_seconds
107
+ 10.seconds # wait time for fluentd pidfile created
108
+ end
106
109
  end
107
110
  end
108
111
  end
@@ -0,0 +1,61 @@
1
+ class Fluentd
2
+ class Agent
3
+ module ProcessOperation
4
+ def self.included(base)
5
+ define_method(:dryrun!) do
6
+ raise NotImplementedError, "'dryrun!' method is required to be defined"
7
+ end
8
+ end
9
+
10
+ def running?
11
+ begin
12
+ pid && Process.kill(0, pid)
13
+ rescue Errno::ESRCH
14
+ File.unlink(pid_file) # no needed any more
15
+ false
16
+ end
17
+ end
18
+
19
+ def dryrun(file_path = nil)
20
+ dryrun!(file_path)
21
+ true
22
+ rescue ::Fluentd::Agent::ConfigError
23
+ false
24
+ end
25
+
26
+ def pid
27
+ return unless File.exists?(pid_file)
28
+ return if File.zero?(pid_file)
29
+ File.read(pid_file).to_i rescue nil
30
+ end
31
+
32
+ private
33
+
34
+ def exec_dryrun(command, file_path = nil)
35
+ Bundler.with_clean_env do
36
+ unless system("#{command} -q --dry-run #{options_to_argv(config_file: file_path)}", out: File::NULL, err: File::NULL)
37
+ raise ::Fluentd::Agent::ConfigError
38
+ end
39
+ end
40
+ end
41
+
42
+ def detached_command(cmd)
43
+ thread = Bundler.with_clean_env do
44
+ pid = spawn(cmd)
45
+ Process.detach(pid)
46
+ end
47
+ thread.join
48
+ thread.value.exitstatus.zero?
49
+ end
50
+
51
+ def options_to_argv(opts = {})
52
+ argv = ""
53
+ argv << " --use-v1-config"
54
+ argv << " -c #{opts[:config_file] || config_file}"
55
+ argv << " -d #{opts[:pid_file] || pid_file}"
56
+ argv << " -o #{opts[:log_file] || log_file}"
57
+ argv
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,7 +2,6 @@ class Fluentd
2
2
  class Agent
3
3
  class TdAgent
4
4
  include Common
5
- include LocalCommon
6
5
 
7
6
  def self.default_options
8
7
  {
@@ -6,7 +6,7 @@ class Fluentd
6
6
  KEYS = [
7
7
  :match,
8
8
  :host, :port, :database, :collection, :capped, :capped_size, :capped_max, :user, :password, :tag_mapped,
9
- :buffer_type, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval, :retry_wait, :retry_limit, :max_retry_wait, :num_threads,
9
+ :buffer_type, :buffer_path, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval, :retry_wait, :retry_limit, :max_retry_wait, :num_threads,
10
10
  ].freeze
11
11
 
12
12
  attr_accessor(*KEYS)
@@ -19,6 +19,7 @@ class Fluentd
19
19
  validates :database, presence: true
20
20
  validate :validate_capped
21
21
  validate :validate_collection
22
+ validates :buffer_path, presence: true, if: ->{ buffer_type == "file" }
22
23
 
23
24
  def validate_capped
24
25
  return true if capped.blank?
@@ -49,7 +50,7 @@ class Fluentd
49
50
 
50
51
  def advanced_options
51
52
  [
52
- :capped, :capped_size, :capped_max, :buffer_type, :buffer_queue_limit, :buffer_chunk_limit,
53
+ :capped, :capped_size, :capped_max, :buffer_type, :buffer_path, :buffer_queue_limit, :buffer_chunk_limit,
53
54
  :flush_interval, :retry_wait, :retry_limit, :max_retry_wait, :num_threads,
54
55
  ]
55
56
  end
@@ -8,9 +8,9 @@ class Fluentd
8
8
  :match,
9
9
  :aws_key_id, :aws_sec_key, :s3_bucket, :s3_region, :path,
10
10
  # :reduced_redundancy, :check_apikey_on_start, :command_parameter, # not configurable?
11
- :format, :include_time_key, :time_key, :delimiter, :label_delimiter, :add_newline, :output_tag, :output_time,
11
+ :format, :include_time_key, :time_key, :delimiter, :label_delimiter,
12
12
  :time_slice_format, :time_slice_wait, :time_format, :utc, :store_as, :proxy_uri, :use_ssl,
13
- :buffer_type, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval,
13
+ :buffer_type, :buffer_path, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval,
14
14
  :retry_wait, :retry_limit, :max_retry_wait, :num_threads,
15
15
  ].freeze
16
16
 
@@ -19,17 +19,16 @@ class Fluentd
19
19
  choice :format, %w(out_file json ltsv single_value)
20
20
  choice :store_as, %w(gzip lzo lzma2 json txt)
21
21
  choice :buffer_type, %w(memory file)
22
- booleans :include_time_key, :add_newline, :use_ssl, :output_tag, :output_time
22
+ booleans :include_time_key, :use_ssl
23
23
  flags :utc
24
24
 
25
25
  validates :match, presence: true
26
26
  validates :s3_bucket, presence: true
27
+ validates :buffer_path, presence: true, if: ->{ buffer_type == "file" }
27
28
 
28
29
  def self.initial_params
29
30
  {
30
31
  s3_region: "us-west-1",
31
- output_tag: true,
32
- output_time: true,
33
32
  use_ssl: true,
34
33
  }
35
34
  end
@@ -43,9 +42,9 @@ class Fluentd
43
42
 
44
43
  def advanced_options
45
44
  [
46
- :format, :output_tag, :output_time, :include_time_key, :time_key, :delimiter, :label_delimiter,
45
+ :format, :include_time_key, :time_key, :delimiter, :label_delimiter,
47
46
  :utc, :time_slice_format, :time_slice_wait, :store_as, :proxy_uri,
48
- :buffer_type, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval,
47
+ :buffer_type, :buffer_path, :buffer_queue_limit, :buffer_chunk_limit, :flush_interval,
49
48
  :retry_wait, :retry_limit, :max_retry_wait, :num_threads,
50
49
  ]
51
50
  end
@@ -17,6 +17,7 @@ class Fluentd
17
17
  validates :match, presence: true
18
18
  validates :apikey, presence: true
19
19
  validates :auto_create_table, presence: true
20
+ validates :buffer_path, presence: true, if: ->{ buffer_type == "file" }
20
21
 
21
22
  def plugin_name
22
23
  "tdlog"
@@ -0,0 +1,123 @@
1
+ class FluentdLog
2
+ attr_reader :log_file
3
+
4
+ def initialize(path)
5
+ @log_file = path
6
+ end
7
+
8
+ def read
9
+ return "" unless File.exists?(log_file)
10
+ content = File.open(log_file, "r+:ascii-8bit"){|f| f.read } # TODO: large log file
11
+ content.force_encoding('utf-8').valid_encoding? ? content : content.force_encoding('ascii-8bit')
12
+ end
13
+
14
+ def errors_since(since = 1.day.ago)
15
+ errors = []
16
+ logged_errors do |error|
17
+ break if Time.parse(error[:subject]) < since
18
+ errors << error
19
+ end
20
+ errors
21
+ end
22
+
23
+ def recent_errors(limit = 3)
24
+ errors = []
25
+ logged_errors do |error|
26
+ errors << error
27
+ break if errors.length >= limit
28
+ end
29
+ errors
30
+ end
31
+
32
+ def last_error_message
33
+ recent_errors(1).first.try(:[], :subject) || ""
34
+ end
35
+
36
+ def tail(limit = nil)
37
+ return [] unless File.exists?(log_file)
38
+
39
+ limit = limit.to_i rescue 0
40
+ limit = limit.zero? ? Settings.default_log_tail_count : limit
41
+ io = File.open(log_file)
42
+ buf = []
43
+ reader = ::FileReverseReader.new(io)
44
+ reader.each_line do |line|
45
+ buf << line
46
+ break if buf.length >= limit
47
+ end
48
+ buf
49
+ end
50
+
51
+ private
52
+
53
+ def logged_errors(&block)
54
+ return [] unless File.exist?(log_file)
55
+ buf = []
56
+ io = File.open(log_file)
57
+ reader = ::FileReverseReader.new(io)
58
+ reader.each_line do |line|
59
+ unless line["error"]
60
+ if buf.present?
61
+ # NOTE: if a following log is given
62
+ # 2014-06-30 11:24:08 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::EADDRINUSE: Address already in use - bind(2) for 0.0.0.0:24220>
63
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:206:in `bind'
64
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:206:in `listen'
65
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:461:in `block in tcp_server_sockets'
66
+ # the first line become a "subject", trailing lines are "notes"
67
+ # {
68
+ # subject: "2014-06-30 11:24:08 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::EADDRINUSE: Address already in use - bind(2) for 0.0.0.0:24220>",
69
+ # notes: [
70
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:206:in `bind'
71
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:206:in `listen'
72
+ # 2014-06-30 11:24:08 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.2/lib/ruby/2.1.0/socket.rb:461:in `block in tcp_server_sockets'
73
+ # ]
74
+ # }
75
+ split_error_lines_to_error_units(buf.reverse).each do |error_unit|
76
+ block.call({
77
+ subject: error_unit[:subject],
78
+ notes: error_unit[:notes],
79
+ })
80
+ end
81
+ end
82
+
83
+ buf = []
84
+ next
85
+ end
86
+ buf << line
87
+ end
88
+ ensure
89
+ io && io.close
90
+ end
91
+
92
+ def split_error_lines_to_error_units(buf)
93
+ # NOTE: if a following log is given
94
+ #
95
+ #2014-05-27 10:54:37 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::#EADDRINUSE: Address already in use - bind(2) for "0.0.0.0" port 24224>
96
+ #2014-05-27 10:55:40 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::#EADDRINUSE: Address already in use - bind(2) for "0.0.0.0" port 24224>
97
+ # 2014-05-27 10:55:40 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/cool.io-1.2.4/lib/cool.io/server.rb:57:in `initialize'
98
+ # 2014-05-27 10:55:40 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/cool.io-1.2.4/lib/cool.io/server.rb:57:in `new'
99
+ #
100
+ #the first line and second line must be each "error_unit". and after third lines lines are "notes" of second error unit of .
101
+ # [
102
+ # { subject: "2014-05-27 10:54:37 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::#EADDRINUSE: Address already in use - bind(2) for "0.0.0.0" port 24224> ",
103
+ # notes: [] },
104
+ # { subject: "2014-05-27 10:55:40 +0900 [error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::#EADDRINUSE: Address already in use - bind(2) for "0.0.0.0" port 24224> ",
105
+ # notes: [
106
+ # "2014-05-27 10:55:40 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/cool.io-1.2.4/lib/cool.io/server.rb:57:in `initialize'",
107
+ # "2014-05-27 10:55:40 +0900 [error]: /Users/uu59/.rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/cool.io-1.2.4/lib/cool.io/server.rb:57:in `new'"
108
+ # ]
109
+ # },
110
+ # ]
111
+ #
112
+ return_array = []
113
+ buf.each_with_index do |b, i|
114
+ if b.match(/\A /)
115
+ return_array[-1][:notes] << b
116
+ else
117
+ return_array << { subject: b, notes: [] }
118
+ end
119
+ end
120
+ return return_array.reverse
121
+ end
122
+ end
123
+