From 368d344291859d24bb5aa7de83ebfb8c93009281 Mon Sep 17 00:00:00 2001 From: uu59 Date: Tue, 24 Mar 2015 17:08:06 +0900 Subject: [PATCH 01/27] #162 in_tail multiline preview --- app/assets/javascripts/vue/in_tail_format.js | 7 ++ app/controllers/api_controller.rb | 2 +- app/models/fluentd/setting/in_tail.rb | 18 +++++- .../in_tail/after_file_choose.html.haml | 2 +- app/views/shared/vue/_in_tail_format.html.erb | 2 +- config/application.yml | 1 + lib/regexp_preview.rb | 64 +++++++++++++++++-- 7 files changed, 87 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index 2e79ffe..c6f6002 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -58,6 +58,12 @@ }, methods: { + onKeyup: function(ev){ + var el = ev.target; + if(el.name.match(/\[format/)){ + this.preview(); + } + }, updateHighlightedLines: function() { if(!this.regexpMatches) { this.highlightedLines = null; @@ -132,6 +138,7 @@ regexp: self.params.setting.regexp, time_format: self.params.setting.time_format, format: _.isEmpty(self.format) ? "regexp" : self.format, + params: self.params.setting, file: self.targetFile } }).done(resolve).fail(reject); diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 998b784..f4faf43 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -19,7 +19,7 @@ class ApiController < ApplicationController end def regexp_preview - preview = RegexpPreview.new(params[:file], params[:format], regexp: params[:regexp], time_format: params[:time_format]) + preview = RegexpPreview.new(params[:file], params[:format], params[:params], regexp: params[:regexp], time_format: params[:time_format]) matches = preview.matches render json: { params: { diff --git a/app/models/fluentd/setting/in_tail.rb b/app/models/fluentd/setting/in_tail.rb index e5a009f..fef5d7a 100644 --- a/app/models/fluentd/setting/in_tail.rb +++ b/app/models/fluentd/setting/in_tail.rb @@ -18,6 +18,7 @@ class Fluentd :ltsv => [:delimiter, :time_key], :json => [:time_key], :regexp => [:time_format, :regexp], + :multiline => [:format_firstline] + (1..20).map{|n| "format#{n}".to_sym } # TODO: Grok could generate Regexp including \d, \s, etc. fluentd config parser raise error with them for escape sequence check. # TBD How to handle Grok/Regexp later, just comment out for hide # :grok => [:grok_str], @@ -59,8 +60,21 @@ class Fluentd indent = " " * 2 format_specific_conf = "" - extra_format_options.each do |key| - format_specific_conf << "#{indent}#{key} #{send(key)}\n" + + if format.to_sym == :multiline + known_formats[:multiline].each do |key| + value = send(key) + if value.present? + format_specific_conf << "#{indent}#{key} /#{value}/\n" + end + end + else + extra_format_options.each do |key| + format_specific_conf << "#{indent}#{key} #{send(key)}\n" + end + end + + if format.to_sym == :multiline end format_specific_conf end diff --git a/app/views/fluentd/settings/in_tail/after_file_choose.html.haml b/app/views/fluentd/settings/in_tail/after_file_choose.html.haml index 79da579..1311151 100644 --- a/app/views/fluentd/settings/in_tail/after_file_choose.html.haml +++ b/app/views/fluentd/settings/in_tail/after_file_choose.html.haml @@ -12,7 +12,7 @@ = f.text_field :path, class: "form-control", disabled: true = render partial: "shared/vue/in_tail_format", locals: { file: f.object.path, formats: @setting.known_formats, initialSelected: f.object.format || @setting.guess_format } - %pre= file_tail(@setting.path).join("\n") + %pre= file_tail(@setting.path, Settings.in_tail_preview_target_line).join("\n") %p = f.submit t('terms.next'), class: "btn btn-lg btn-primary pull-right" diff --git a/app/views/shared/vue/_in_tail_format.html.erb b/app/views/shared/vue/_in_tail_format.html.erb index 8537739..dc998a1 100644 --- a/app/views/shared/vue/_in_tail_format.html.erb +++ b/app/views/shared/vue/_in_tail_format.html.erb @@ -7,7 +7,7 @@
- +
diff --git a/config/application.yml b/config/application.yml index ee5fc90..db5addf 100644 --- a/config/application.yml +++ b/config/application.yml @@ -3,6 +3,7 @@ defaults: &defaults default_log_tail_count: 30 histories_count_in_preview: 5 max_backup_files_count: 100 + in_tail_preview_target_line: 40 recommended_plugins: - category: filter name: "rewrite-tag-filter" diff --git a/lib/regexp_preview.rb b/lib/regexp_preview.rb index 74af30b..5ef4916 100644 --- a/lib/regexp_preview.rb +++ b/lib/regexp_preview.rb @@ -4,15 +4,19 @@ require "fluent/configurable" require "fluent/parser" class RegexpPreview - attr_reader :file, :format, :time_format, :regexp + attr_reader :file, :format, :params, :time_format, :regexp - def initialize(file, format, options = {}) + def initialize(file, format, params = {}, options = {}) @file = file @format = format + @params = params case format when "regexp" @regexp = Regexp.new(options[:regexp]) @time_format = options[:time_format] + @strategy = :matches_single_line + when "multiline" + @strategy = :matches_multiline when "ltsv", "json", "csv", "tsv" else definition = Fluent::TextParser::TEMPLATE_REGISTRY.lookup(format).call @@ -24,9 +28,15 @@ class RegexpPreview end def matches - return [] unless @regexp # such as ltsv, json, etc + return [] unless @strategy # such as ltsv, json, etc + send(@strategy) + end + + private + + def matches_single_line reader = FileReverseReader.new(File.open(file)) - matches = reader.tail.map do |line| + matches = reader.tail(Settings.in_tail_preview_target_line).map do |line| result = { :whole => line, :matches => [], @@ -45,4 +55,50 @@ class RegexpPreview end matches end + + def matches_multiline + return [] if multiline_regexps.empty? + reader = FileReverseReader.new(File.open(file)) + result = [] + target_lines = reader.tail(Settings.in_tail_preview_target_line).map{|line| line << "\n" } + target_lines.each_with_index do |line, line_no| + if line.match(params[:format_firstline]) + lines = target_lines[line_no, multiline_regexps.length] + next if lines.length < multiline_regexps.length + ret = multiline_detect(lines, multiline_regexps) + next unless ret + result << ret + end + end + result + end + + def multiline_detect(lines, regexps) + whole = "" + matches = [] + lines.each_with_index do |line, j| + m = line.match(multiline_regexps[j]) + unless m + return nil + end + m.names.each_with_index do |name, index| + matches << { + key: name, + matched: m[name], + pos: m.offset(index + 1).map{|pos| pos + whole.length}, + } + end + whole << line + end + { + whole: whole, + matches: matches, + } + end + + def multiline_regexps + @multiline_regexps ||= (1..20).map do |n| + params["format#{n}"].presence + end.compact + end end From 8c5a959b97f26b1d9c89c4c0157b3300dec0cf83 Mon Sep 17 00:00:00 2001 From: uu59 Date: Wed, 8 Apr 2015 16:59:55 +0900 Subject: [PATCH 02/27] Remove useless lines --- app/models/fluentd/setting/in_tail.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/models/fluentd/setting/in_tail.rb b/app/models/fluentd/setting/in_tail.rb index fef5d7a..b5a2906 100644 --- a/app/models/fluentd/setting/in_tail.rb +++ b/app/models/fluentd/setting/in_tail.rb @@ -74,8 +74,6 @@ class Fluentd end end - if format.to_sym == :multiline - end format_specific_conf end From 9207ca420f884034692fbad968ca5e729d0c6cd8 Mon Sep 17 00:00:00 2001 From: uu59 Date: Wed, 8 Apr 2015 17:04:18 +0900 Subject: [PATCH 03/27] Proper short variable name --- lib/regexp_preview.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/regexp_preview.rb b/lib/regexp_preview.rb index 5ef4916..d754af0 100644 --- a/lib/regexp_preview.rb +++ b/lib/regexp_preview.rb @@ -76,8 +76,8 @@ class RegexpPreview def multiline_detect(lines, regexps) whole = "" matches = [] - lines.each_with_index do |line, j| - m = line.match(multiline_regexps[j]) + lines.each_with_index do |line, i| + m = line.match(multiline_regexps[i]) unless m return nil end From 40cca27ca97256c7ca413da0820340af45cf871b Mon Sep 17 00:00:00 2001 From: uu59 Date: Wed, 8 Apr 2015 17:16:47 +0900 Subject: [PATCH 04/27] Rename settings key --- .../fluentd/settings/in_tail/after_file_choose.html.haml | 2 +- config/application.yml | 2 +- lib/regexp_preview.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/fluentd/settings/in_tail/after_file_choose.html.haml b/app/views/fluentd/settings/in_tail/after_file_choose.html.haml index 1311151..8c494c9 100644 --- a/app/views/fluentd/settings/in_tail/after_file_choose.html.haml +++ b/app/views/fluentd/settings/in_tail/after_file_choose.html.haml @@ -12,7 +12,7 @@ = f.text_field :path, class: "form-control", disabled: true = render partial: "shared/vue/in_tail_format", locals: { file: f.object.path, formats: @setting.known_formats, initialSelected: f.object.format || @setting.guess_format } - %pre= file_tail(@setting.path, Settings.in_tail_preview_target_line).join("\n") + %pre= file_tail(@setting.path, Settings.in_tail_preview_line_count).join("\n") %p = f.submit t('terms.next'), class: "btn btn-lg btn-primary pull-right" diff --git a/config/application.yml b/config/application.yml index db5addf..c2df865 100644 --- a/config/application.yml +++ b/config/application.yml @@ -3,7 +3,7 @@ defaults: &defaults default_log_tail_count: 30 histories_count_in_preview: 5 max_backup_files_count: 100 - in_tail_preview_target_line: 40 + in_tail_preview_line_count: 40 recommended_plugins: - category: filter name: "rewrite-tag-filter" diff --git a/lib/regexp_preview.rb b/lib/regexp_preview.rb index d754af0..6271a5a 100644 --- a/lib/regexp_preview.rb +++ b/lib/regexp_preview.rb @@ -36,7 +36,7 @@ class RegexpPreview def matches_single_line reader = FileReverseReader.new(File.open(file)) - matches = reader.tail(Settings.in_tail_preview_target_line).map do |line| + matches = reader.tail(Settings.in_tail_preview_line_count).map do |line| result = { :whole => line, :matches => [], @@ -60,7 +60,7 @@ class RegexpPreview return [] if multiline_regexps.empty? reader = FileReverseReader.new(File.open(file)) result = [] - target_lines = reader.tail(Settings.in_tail_preview_target_line).map{|line| line << "\n" } + target_lines = reader.tail(Settings.in_tail_preview_line_count).map{|line| line << "\n" } target_lines.each_with_index do |line, line_no| if line.match(params[:format_firstline]) lines = target_lines[line_no, multiline_regexps.length] From 79f0b1fe284ad7eee60fcaab685826add1927193 Mon Sep 17 00:00:00 2001 From: uu59 Date: Wed, 8 Apr 2015 18:20:18 +0900 Subject: [PATCH 05/27] Refactor --- app/controllers/api_controller.rb | 2 +- lib/regexp_preview.rb | 97 ++----------------------------- lib/regexp_preview/multi_line.rb | 58 ++++++++++++++++++ lib/regexp_preview/single_line.rb | 44 ++++++++++++++ 4 files changed, 109 insertions(+), 92 deletions(-) create mode 100644 lib/regexp_preview/multi_line.rb create mode 100644 lib/regexp_preview/single_line.rb diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index f4faf43..53178af 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -19,7 +19,7 @@ class ApiController < ApplicationController end def regexp_preview - preview = RegexpPreview.new(params[:file], params[:format], params[:params], regexp: params[:regexp], time_format: params[:time_format]) + preview = RegexpPreview.processor(params[:format]).new(params[:file], params[:format], params) matches = preview.matches render json: { params: { diff --git a/lib/regexp_preview.rb b/lib/regexp_preview.rb index 6271a5a..0ec21b7 100644 --- a/lib/regexp_preview.rb +++ b/lib/regexp_preview.rb @@ -2,103 +2,18 @@ require "fluent/registry" require "fluent/configurable" require "fluent/parser" +require "regexp_preview/single_line" +require "regexp_preview/multi_line" -class RegexpPreview +module RegexpPreview attr_reader :file, :format, :params, :time_format, :regexp - def initialize(file, format, params = {}, options = {}) - @file = file - @format = format - @params = params + def self.processor(format) case format - when "regexp" - @regexp = Regexp.new(options[:regexp]) - @time_format = options[:time_format] - @strategy = :matches_single_line when "multiline" - @strategy = :matches_multiline - when "ltsv", "json", "csv", "tsv" + RegexpPreview::MultiLine else - definition = Fluent::TextParser::TEMPLATE_REGISTRY.lookup(format).call - raise "Unknown format '#{format}'" unless definition - definition.configure({}) # NOTE: SyslogParser define @regexp in configure method so call it to grab Regexp object - @regexp = definition.patterns["format"] - @time_format = definition.patterns["time_format"] + RegexpPreview::SingleLine end end - - def matches - return [] unless @strategy # such as ltsv, json, etc - send(@strategy) - end - - private - - def matches_single_line - reader = FileReverseReader.new(File.open(file)) - matches = reader.tail(Settings.in_tail_preview_line_count).map do |line| - result = { - :whole => line, - :matches => [], - } - m = line.match(regexp) - next result unless m - - m.names.each_with_index do |name, index| - result[:matches] << { - key: name, - matched: m[name], - pos: m.offset(index + 1), - } - end - result - end - matches - end - - def matches_multiline - return [] if multiline_regexps.empty? - reader = FileReverseReader.new(File.open(file)) - result = [] - target_lines = reader.tail(Settings.in_tail_preview_line_count).map{|line| line << "\n" } - target_lines.each_with_index do |line, line_no| - if line.match(params[:format_firstline]) - lines = target_lines[line_no, multiline_regexps.length] - next if lines.length < multiline_regexps.length - ret = multiline_detect(lines, multiline_regexps) - next unless ret - result << ret - end - end - result - end - - def multiline_detect(lines, regexps) - whole = "" - matches = [] - lines.each_with_index do |line, i| - m = line.match(multiline_regexps[i]) - unless m - return nil - end - m.names.each_with_index do |name, index| - matches << { - key: name, - matched: m[name], - pos: m.offset(index + 1).map{|pos| pos + whole.length}, - } - end - whole << line - end - { - whole: whole, - matches: matches, - } - end - - def multiline_regexps - @multiline_regexps ||= (1..20).map do |n| - params["format#{n}"].presence - end.compact - end end diff --git a/lib/regexp_preview/multi_line.rb b/lib/regexp_preview/multi_line.rb new file mode 100644 index 0000000..06bc987 --- /dev/null +++ b/lib/regexp_preview/multi_line.rb @@ -0,0 +1,58 @@ +module RegexpPreview + class MultiLine + attr_reader :file, :format, :params + + def initialize(file, format, params = {}) + @file = file + @format = format + @params = params[:params] + end + + def matches + return [] if patterns.empty? + reader = FileReverseReader.new(File.open(file)) + result = [] + target_lines = reader.tail(Settings.in_tail_preview_line_count).map{|line| line << "\n" } + target_lines.each_with_index do |line, line_no| + if line.match(params[:format_firstline]) + lines = target_lines[line_no, patterns.length] + next if lines.length < patterns.length + ret = detect_chunk(lines) + next unless ret + result << ret + end + end + result + end + + private + + def detect_chunk(lines) + whole = "" + matches = [] + lines.each_with_index do |line, i| + match = line.match(patterns[i]) + return nil unless match + match.names.each_with_index do |name, index| + matches << { + key: name, + matched: match[name], + pos: match.offset(index + 1).map{|pos| pos + whole.length}, + } + end + whole << line + end + { + whole: whole, + matches: matches, + } + end + + def patterns + @patterns ||= (1..20).map do |n| + params["format#{n}"].presence + end.compact.map {|pattern| Regexp.new(pattern)} + end + end +end + diff --git a/lib/regexp_preview/single_line.rb b/lib/regexp_preview/single_line.rb new file mode 100644 index 0000000..3754909 --- /dev/null +++ b/lib/regexp_preview/single_line.rb @@ -0,0 +1,44 @@ +module RegexpPreview + class SingleLine + attr_reader :file, :format, :params, :regexp + + def initialize(file, format, params = {}) + @file = file + @format = format + @params = params + case format + when "regexp" + @regexp = Regexp.new(params[:regexp]) + when "ltsv", "json", "csv", "tsv" + else # apache, nginx, etc + definition = Fluent::TextParser::TEMPLATE_REGISTRY.lookup(format).call + raise "Unknown format '#{format}'" unless definition + definition.configure({}) # NOTE: SyslogParser define @regexp in configure method so call it to grab Regexp object + @regexp = definition.patterns["format"] + end + end + + def matches + return [] unless @regexp # such as ltsv, json, apache, etc + reader = FileReverseReader.new(File.open(file)) + matches = reader.tail(Settings.in_tail_preview_line_count).map do |line| + result = { + :whole => line, + :matches => [], + } + match = line.match(regexp) + next result unless match + + match.names.each_with_index do |name, index| + result[:matches] << { + key: name, + matched: match[name], + pos: match.offset(index + 1), + } + end + result + end + matches + end + end +end From 098b74de4bbca1565e38cbcd10bd0da1d301cc4a Mon Sep 17 00:00:00 2001 From: yoshihara Date: Mon, 20 Apr 2015 15:50:32 +0900 Subject: [PATCH 06/27] Fix a bug that error is raised when multi-line format --- app/controllers/api_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 53178af..5db3e36 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -21,11 +21,14 @@ class ApiController < ApplicationController def regexp_preview preview = RegexpPreview.processor(params[:format]).new(params[:file], params[:format], params) matches = preview.matches + render json: { params: { setting: { - regexp: preview.regexp.try(:source), - time_format: preview.time_format, + # NOTE: regexp and timeformat are used when format == 'apache' || 'nginx' || etc. + # TODO: prepare rendered JSON by prcessor(RegexpPreview::{Signle,Multi}Line + regexp: preview.try(:regexp).try(:source), + time_format: preview.try(:time_format), } }, matches: matches.compact, From ac778ea69a212767c0250052f1c11d4a560120b2 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Mon, 20 Apr 2015 18:46:01 +0900 Subject: [PATCH 07/27] Use textarea instaed of inputbox * 20 for format1..20 in multiline --- app/assets/javascripts/vue/in_tail_format.js | 18 ++++++++++++++++++ app/views/shared/vue/_in_tail_format.html.erb | 17 +++++++++++++++-- config/locales/translation_en.yml | 1 + config/locales/translation_ja.yml | 1 + 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index c6f6002..dd053ed 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -1,5 +1,6 @@ (function(){ "use strict"; + var maxFormatCount = 20; $(function(){ if($('#in_tail_format').length === 0) return; @@ -30,7 +31,20 @@ highlightedLines: null, }, + computed: { + useTextArea: function() { + return this.format == "multiline"; + } + }, + compiled: function(){ + this.$watch('params.setting.formats', function(formats){ + _.range(1, maxFormatCount).forEach(function(i) {params.setting["format" + String(i)] = "";}); + + _.compact(formats.split("\n")).forEach(function(formatLine, index) { + params.setting["format" + String(index + 1)] = formatLine; + }); + }), this.$watch('params.setting.regexp', function(){ this.preview(); }); @@ -46,6 +60,10 @@ if(!params.setting) { params.setting = {}; } + + var formats = _.map(_.range(1, maxFormatCount), function(i) {return params.setting["format" + String(i)];}); + params.setting.formats = _.compact(formats).join("\n"); + _.each(this.formatOptions, function(options){ _.each(options, function(key){ if(!params.setting.hasOwnProperty(key)){ diff --git a/app/views/shared/vue/_in_tail_format.html.erb b/app/views/shared/vue/_in_tail_format.html.erb index dc998a1..da89526 100644 --- a/app/views/shared/vue/_in_tail_format.html.erb +++ b/app/views/shared/vue/_in_tail_format.html.erb @@ -6,8 +6,21 @@
- - + + +
+ +
+
+ + +
+ +
+

<%=t("fluentd.settings.in_tail.notice_for_multiline_limit") %>

+ + +
diff --git a/config/locales/translation_en.yml b/config/locales/translation_en.yml index 73896d8..ce04526 100644 --- a/config/locales/translation_en.yml +++ b/config/locales/translation_en.yml @@ -211,6 +211,7 @@ en: For each config parameter, please refer to the Tail input plugin documentation page. in_tail: notice_for_permission: "Please check permission or group setting for %{user} user can read it." + notice_for_multiline_limit: "The number i of lines is assigned \"format#{i}\". Lines more than 20 are ignored." restart_from_first: Restart from first grok_manual: |

diff --git a/config/locales/translation_ja.yml b/config/locales/translation_ja.yml index c8da92b..6764381 100644 --- a/config/locales/translation_ja.yml +++ b/config/locales/translation_ja.yml @@ -216,6 +216,7 @@ ja: in_tailプラグインの解説ページFluentularもご参照ください。 in_tail: + notice_for_multiline_limit: "i行目が\"format#{i}\"として設定されます。20行以上は無視します。" notice_for_permission: "※%{user}ユーザーが読み込み可能なようにパーミッションやグループの設定をご確認ください。" restart_from_first: 最初からやり直す grok_manual: | From 88aec6f538816b525f315baee7e1715adf355e15 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Mon, 20 Apr 2015 18:52:53 +0900 Subject: [PATCH 08/27] js: Use `==` but `===` for comparison --- app/assets/javascripts/vue/in_tail_format.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index dd053ed..9633e6c 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -33,7 +33,7 @@ computed: { useTextArea: function() { - return this.format == "multiline"; + return this.format === "multiline"; } }, From c591734ede89e0ba5c224d7c0752ad4bf66ae061 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 11:41:46 +0900 Subject: [PATCH 09/27] Remove duplicated accessor defining --- app/models/fluentd/setting/in_tail.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/fluentd/setting/in_tail.rb b/app/models/fluentd/setting/in_tail.rb index b5a2906..044815b 100644 --- a/app/models/fluentd/setting/in_tail.rb +++ b/app/models/fluentd/setting/in_tail.rb @@ -24,7 +24,7 @@ class Fluentd # :grok => [:grok_str], } end - attr_accessor *known_formats.values.flatten.compact + attr_accessor *known_formats.values.flatten.compact.uniq def known_formats self.class.known_formats From f4381a81fdbc7be4fde30dcb90054f3d44a52c5d Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 12:12:47 +0900 Subject: [PATCH 10/27] Use constant for magic number --- app/models/fluentd/setting/in_tail.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/fluentd/setting/in_tail.rb b/app/models/fluentd/setting/in_tail.rb index 044815b..1d160bb 100644 --- a/app/models/fluentd/setting/in_tail.rb +++ b/app/models/fluentd/setting/in_tail.rb @@ -1,6 +1,8 @@ class Fluentd module Setting class InTail + MULTI_LINE_MAX_FORMAT_COUNT = 20 + include ActiveModel::Model attr_accessor :path, :tag, :format, :regexp, :time_format, :rotate_wait, :pos_file, :read_from_head, :refresh_interval @@ -18,7 +20,7 @@ class Fluentd :ltsv => [:delimiter, :time_key], :json => [:time_key], :regexp => [:time_format, :regexp], - :multiline => [:format_firstline] + (1..20).map{|n| "format#{n}".to_sym } + :multiline => [:format_firstline] + (1..MULTI_LINE_MAX_FORMAT_COUNT).map{|n| "format#{n}".to_sym } # TODO: Grok could generate Regexp including \d, \s, etc. fluentd config parser raise error with them for escape sequence check. # TBD How to handle Grok/Regexp later, just comment out for hide # :grok => [:grok_str], From ceaed87dadc08f97892e1f3ba8d0bcb8cdc9437c Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 13:06:41 +0900 Subject: [PATCH 11/27] Extract JSON generating for preview to each RegexpPreview class --- app/controllers/api_controller.rb | 13 +------------ lib/regexp_preview/multi_line.rb | 14 ++++++++++++++ lib/regexp_preview/single_line.rb | 19 ++++++++++++++++++- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 5db3e36..c162390 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -20,19 +20,8 @@ class ApiController < ApplicationController def regexp_preview preview = RegexpPreview.processor(params[:format]).new(params[:file], params[:format], params) - matches = preview.matches - render json: { - params: { - setting: { - # NOTE: regexp and timeformat are used when format == 'apache' || 'nginx' || etc. - # TODO: prepare rendered JSON by prcessor(RegexpPreview::{Signle,Multi}Line - regexp: preview.try(:regexp).try(:source), - time_format: preview.try(:time_format), - } - }, - matches: matches.compact, - } + render json: preview.matches_json end def grok_to_regexp diff --git a/lib/regexp_preview/multi_line.rb b/lib/regexp_preview/multi_line.rb index 06bc987..01fb1c8 100644 --- a/lib/regexp_preview/multi_line.rb +++ b/lib/regexp_preview/multi_line.rb @@ -8,6 +8,20 @@ module RegexpPreview @params = params[:params] end + def matches_json + { + params: { + setting: { # for vue.js + regexp: nil, + time_format: nil, + } + }, + matches: matches.compact, + } + end + + private + def matches return [] if patterns.empty? reader = FileReverseReader.new(File.open(file)) diff --git a/lib/regexp_preview/single_line.rb b/lib/regexp_preview/single_line.rb index 3754909..7361b62 100644 --- a/lib/regexp_preview/single_line.rb +++ b/lib/regexp_preview/single_line.rb @@ -1,11 +1,13 @@ module RegexpPreview class SingleLine - attr_reader :file, :format, :params, :regexp + attr_reader :file, :format, :params, :regexp, :time_format def initialize(file, format, params = {}) @file = file @format = format + @time_format = params[:time_format] @params = params + case format when "regexp" @regexp = Regexp.new(params[:regexp]) @@ -18,6 +20,21 @@ module RegexpPreview end end + def matches_json + { + params: { + setting: { + # NOTE: regexp and time_format are used when format == 'apache' || 'nginx' || etc. + regexp: regexp.source, + time_format: time_format, + } + }, + matches: matches.compact, + } + end + + private + def matches return [] unless @regexp # such as ltsv, json, apache, etc reader = FileReverseReader.new(File.open(file)) From 60dbf172229eaf2e46c9283aa3a3889cff2a7177 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 13:44:40 +0900 Subject: [PATCH 12/27] Add missing `time_format` getting from definition --- lib/regexp_preview/single_line.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/regexp_preview/single_line.rb b/lib/regexp_preview/single_line.rb index 7361b62..37d30dc 100644 --- a/lib/regexp_preview/single_line.rb +++ b/lib/regexp_preview/single_line.rb @@ -17,6 +17,7 @@ module RegexpPreview raise "Unknown format '#{format}'" unless definition definition.configure({}) # NOTE: SyslogParser define @regexp in configure method so call it to grab Regexp object @regexp = definition.patterns["format"] + @time_format = definition.patterns["time_format"] end end From 74cff38dc9809b5eab67e43d0b7e4ddd9e42d702 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 15:26:22 +0900 Subject: [PATCH 13/27] Assign nil to unused instance variables --- lib/regexp_preview/single_line.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/regexp_preview/single_line.rb b/lib/regexp_preview/single_line.rb index 37d30dc..f32a8dd 100644 --- a/lib/regexp_preview/single_line.rb +++ b/lib/regexp_preview/single_line.rb @@ -11,7 +11,10 @@ module RegexpPreview case format when "regexp" @regexp = Regexp.new(params[:regexp]) + @time_format = nil when "ltsv", "json", "csv", "tsv" + @regexp = nil + @time_format = nil else # apache, nginx, etc definition = Fluent::TextParser::TEMPLATE_REGISTRY.lookup(format).call raise "Unknown format '#{format}'" unless definition @@ -26,7 +29,7 @@ module RegexpPreview params: { setting: { # NOTE: regexp and time_format are used when format == 'apache' || 'nginx' || etc. - regexp: regexp.source, + regexp: regexp.try(:source), time_format: time_format, } }, From 6158e4db8fca47c3c8067f71431b308fdef97bfe Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 15:26:49 +0900 Subject: [PATCH 14/27] Remove needless accessor definition --- lib/regexp_preview.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/regexp_preview.rb b/lib/regexp_preview.rb index 0ec21b7..bb0c81f 100644 --- a/lib/regexp_preview.rb +++ b/lib/regexp_preview.rb @@ -6,8 +6,6 @@ require "regexp_preview/single_line" require "regexp_preview/multi_line" module RegexpPreview - attr_reader :file, :format, :params, :time_format, :regexp - def self.processor(format) case format when "multiline" From 5cd1515336df800e95706dfd261c9b288ad1cf0f Mon Sep 17 00:00:00 2001 From: yoshihara Date: Tue, 21 Apr 2015 17:48:44 +0900 Subject: [PATCH 15/27] spec: add specs for RegexpPreview::SingleLine --- spec/lib/regexp_preview/single_line_spec.rb | 183 ++++++++++++++++++++ spec/support/fixtures/error4.log | 1 + 2 files changed, 184 insertions(+) create mode 100644 spec/lib/regexp_preview/single_line_spec.rb create mode 100644 spec/support/fixtures/error4.log diff --git a/spec/lib/regexp_preview/single_line_spec.rb b/spec/lib/regexp_preview/single_line_spec.rb new file mode 100644 index 0000000..99d263e --- /dev/null +++ b/spec/lib/regexp_preview/single_line_spec.rb @@ -0,0 +1,183 @@ +require 'spec_helper' + +describe RegexpPreview::SingleLine do + describe ".initialize" do + subject { RegexpPreview::SingleLine.new("log_file.log", format, params) } + + describe "format" do + let :params do + { + regexp: "(?regexp)", + time_format: "time_format", + } + end + + shared_examples "should set regexp and time_format from selected format" do + it do + expect(subject.regexp).to eq regexp + expect(subject.time_format).to eq time_format + expect(subject.params).to eq params + end + end + + shared_examples "should set params only" do + include_examples "should set regexp and time_format from selected format" do + let(:regexp) { nil } + let(:time_format) { nil } + end + end + + context "regexp" do + let(:format) { "regexp" } + + it 'should set regexp from params' do + expect(subject.regexp).to eq /#{params[:regexp]}/ + expect(subject.time_format).to be_nil + expect(subject.params).to eq params + end + end + + context "ltsv" do + let(:format) { "ltsv" } + + include_examples "should set params only" + end + + context "json" do + let(:format) { "json" } + + include_examples "should set params only" + end + + context "csv" do + let(:format) { "csv" } + + include_examples "should set params only" + end + + context "tsv" do + let(:format) { "tsv" } + + include_examples "should set params only" + end + + context "syslog" do # "apache", "nginx", etc + let(:format) { "syslog" } + + include_examples "should set regexp and time_format from selected format" do + let(:regexp) do + /^(?

- +
From 446a92ebd616fe0da0550d5d87d5dad319fd3ae3 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 13:59:39 +0900 Subject: [PATCH 18/27] Add missing space --- app/views/shared/vue/_in_tail_format.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/vue/_in_tail_format.html.erb b/app/views/shared/vue/_in_tail_format.html.erb index 708cb19..eb642c6 100644 --- a/app/views/shared/vue/_in_tail_format.html.erb +++ b/app/views/shared/vue/_in_tail_format.html.erb @@ -17,7 +17,7 @@
-

<%=t("fluentd.settings.in_tail.notice_for_multiline_limit") %>

+

<%= t("fluentd.settings.in_tail.notice_for_multiline_limit") %>

From 874efd1f321a2a5b3039f1002b84665c8b8b3c95 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 14:36:49 +0900 Subject: [PATCH 19/27] Update comment to match with implementation --- lib/regexp_preview/single_line.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/regexp_preview/single_line.rb b/lib/regexp_preview/single_line.rb index f32a8dd..1981e73 100644 --- a/lib/regexp_preview/single_line.rb +++ b/lib/regexp_preview/single_line.rb @@ -40,7 +40,7 @@ module RegexpPreview private def matches - return [] unless @regexp # such as ltsv, json, apache, etc + return [] unless @regexp # such as ltsv, json, etc reader = FileReverseReader.new(File.open(file)) matches = reader.tail(Settings.in_tail_preview_line_count).map do |line| result = { From 5e1627ca6fa18f6878a4891633e90a5bbe1686c6 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 14:59:45 +0900 Subject: [PATCH 20/27] Use more meaningful variable name tmp -> params --- spec/lib/regexp_preview/multi_line_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/lib/regexp_preview/multi_line_spec.rb b/spec/lib/regexp_preview/multi_line_spec.rb index a31aa49..100c6ff 100644 --- a/spec/lib/regexp_preview/multi_line_spec.rb +++ b/spec/lib/regexp_preview/multi_line_spec.rb @@ -5,16 +5,16 @@ describe RegexpPreview::MultiLine do subject { RegexpPreview::MultiLine.new(File.expand_path("./spec/support/fixtures/error0.log", Rails.root), "multiline", params).matches_json } let :params do - tmp = { + params = { format_firstline: ".+", time_format: "time_format", } - tmp["format1"] = "(?foo)" - tmp["format2"] = "(?bar)" + params["format1"] = "(?foo)" + params["format2"] = "(?bar)" 3.upto(Fluentd::Setting::InTail::MULTI_LINE_MAX_FORMAT_COUNT) do |i| - tmp["format#{i}"] = "" + params["format#{i}"] = "" end - { params: tmp } + { params: params } end it 'should not have regexp and time_format in [:params][:setting]' do From 2a2b968775ba9b765178603bd1b733cf41e6cf5e Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 15:09:51 +0900 Subject: [PATCH 21/27] Remove duplicated `private` The first `private` is at L23. --- lib/regexp_preview/multi_line.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/regexp_preview/multi_line.rb b/lib/regexp_preview/multi_line.rb index 01fb1c8..7c291bb 100644 --- a/lib/regexp_preview/multi_line.rb +++ b/lib/regexp_preview/multi_line.rb @@ -39,8 +39,6 @@ module RegexpPreview result end - private - def detect_chunk(lines) whole = "" matches = [] From 16c04d986a88caa39e19d1d14cc2428e22e4e942 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 15:19:24 +0900 Subject: [PATCH 22/27] spec: Use more Regexp-like expression --- spec/lib/regexp_preview/single_line_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/regexp_preview/single_line_spec.rb b/spec/lib/regexp_preview/single_line_spec.rb index 99d263e..cec7b62 100644 --- a/spec/lib/regexp_preview/single_line_spec.rb +++ b/spec/lib/regexp_preview/single_line_spec.rb @@ -7,8 +7,8 @@ describe RegexpPreview::SingleLine do describe "format" do let :params do { - regexp: "(?regexp)", time_format: "time_format", + regexp: "(?\[.+\])", } end From 78573665a19f71852b3a9171017fe348660da705 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 15:26:48 +0900 Subject: [PATCH 23/27] spec: use more proper time_format Regexp --- spec/lib/regexp_preview/single_line_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/regexp_preview/single_line_spec.rb b/spec/lib/regexp_preview/single_line_spec.rb index cec7b62..57cce8f 100644 --- a/spec/lib/regexp_preview/single_line_spec.rb +++ b/spec/lib/regexp_preview/single_line_spec.rb @@ -7,8 +7,8 @@ describe RegexpPreview::SingleLine do describe "format" do let :params do { - time_format: "time_format", regexp: "(?\[.+\])", + time_format: "%y/%m/%d", } end From 6c32e9128dcf7be9759a523ad7c48c7ec1462be1 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 15:42:03 +0900 Subject: [PATCH 24/27] js: Use `_.chain` to generate formats parameter --- app/assets/javascripts/vue/in_tail_format.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index 9633e6c..42d1343 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -61,8 +61,8 @@ params.setting = {}; } - var formats = _.map(_.range(1, maxFormatCount), function(i) {return params.setting["format" + String(i)];}); - params.setting.formats = _.compact(formats).join("\n"); + var formats = _.chain(_.range(1, maxFormatCount)).map(function(i) {return params.setting["format" + String(i)];}).compact().value(); + params.setting.formats = formats.join("\n"); _.each(this.formatOptions, function(options){ _.each(options, function(key){ From 8c8cab2ef4bf328d61877a7696e6e6bce1fd781c Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 15:45:29 +0900 Subject: [PATCH 25/27] js: fix the bug that `formats` doesn't include `format20` parameter _.range(1, 20) doesn't include 20. --- app/assets/javascripts/vue/in_tail_format.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index 42d1343..a9565a3 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -61,7 +61,7 @@ params.setting = {}; } - var formats = _.chain(_.range(1, maxFormatCount)).map(function(i) {return params.setting["format" + String(i)];}).compact().value(); + var formats = _.chain(_.range(1, maxFormatCount + 1)).map(function(i) {return params.setting["format" + String(i)];}).compact().value(); params.setting.formats = formats.join("\n"); _.each(this.formatOptions, function(options){ From 445e8b8dd3cc8889095558d75152356f12f102a7 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 16:00:53 +0900 Subject: [PATCH 26/27] Clarify description for multi_line --- config/locales/translation_en.yml | 2 +- config/locales/translation_ja.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/translation_en.yml b/config/locales/translation_en.yml index ce04526..eaaef1a 100644 --- a/config/locales/translation_en.yml +++ b/config/locales/translation_en.yml @@ -211,7 +211,7 @@ en: For each config parameter, please refer to the Tail input plugin documentation page. in_tail: notice_for_permission: "Please check permission or group setting for %{user} user can read it." - notice_for_multiline_limit: "The number i of lines is assigned \"format#{i}\". Lines more than 20 are ignored." + notice_for_multiline_limit: "Please input Regexp(s) separated by newline. blank lines are ignored. Lines more than 20 are dropped." restart_from_first: Restart from first grok_manual: |

diff --git a/config/locales/translation_ja.yml b/config/locales/translation_ja.yml index 6764381..9a41a0b 100644 --- a/config/locales/translation_ja.yml +++ b/config/locales/translation_ja.yml @@ -216,7 +216,7 @@ ja: in_tailプラグインの解説ページFluentularもご参照ください。 in_tail: - notice_for_multiline_limit: "i行目が\"format#{i}\"として設定されます。20行以上は無視します。" + notice_for_multiline_limit: "改行区切りで正規表現を入力してください。空行はカウントされません。21行目以降の入力は無視されます。" notice_for_permission: "※%{user}ユーザーが読み込み可能なようにパーミッションやグループの設定をご確認ください。" restart_from_first: 最初からやり直す grok_manual: | From 6e0a8e589734f78d4627c0f232cc432d45e9db05 Mon Sep 17 00:00:00 2001 From: yoshihara Date: Thu, 23 Apr 2015 16:48:48 +0900 Subject: [PATCH 27/27] js: add missing adding 1 --- app/assets/javascripts/vue/in_tail_format.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue/in_tail_format.js b/app/assets/javascripts/vue/in_tail_format.js index a9565a3..a5944e6 100644 --- a/app/assets/javascripts/vue/in_tail_format.js +++ b/app/assets/javascripts/vue/in_tail_format.js @@ -39,7 +39,7 @@ compiled: function(){ this.$watch('params.setting.formats', function(formats){ - _.range(1, maxFormatCount).forEach(function(i) {params.setting["format" + String(i)] = "";}); + _.range(1, maxFormatCount + 1).forEach(function(i) {params.setting["format" + String(i)] = "";}); _.compact(formats.split("\n")).forEach(function(formatLine, index) { params.setting["format" + String(index + 1)] = formatLine;