Class: Rails::Pretty::Logger::PrettyLogger
- Inherits:
-
Object
- Object
- Rails::Pretty::Logger::PrettyLogger
- Defined in:
- lib/rails/pretty/logger.rb
Defined Under Namespace
Classes: FileTooLarge, InvalidLogFile
Constant Summary collapse
- SEVERITIES =
%w[DEBUG INFO WARN ERROR FATAL UNKNOWN].freeze
- LINE_INDEX_CACHE_LIMIT =
32- TAIL_READ_CHUNK_SIZE =
64 * 1024
- STRUCTURED_TIMESTAMP_KEYS =
%w[@timestamp timestamp time datetime created_at].freeze
- STRUCTURED_SEVERITY_KEYS =
%w[severity level log_level].freeze
Instance Attribute Summary collapse
-
#log_file ⇒ Object
readonly
Returns the value of attribute log_file.
Class Method Summary collapse
- .clear_line_index_cache! ⇒ Object
- .clear_line_index_cache_for!(log_file) ⇒ Object
- .clear_line_index_memory_cache! ⇒ Object
- .custom_log_metadata(line) ⇒ Object
- .ensure_file_size_within_limit!(log_file) ⇒ Object
- .fetch_line_index_cache(cache_key, signature) ⇒ Object
- .file_size(log_file) ⇒ Object
- .get_hourly_log_file_list ⇒ Object
- .get_log_file_list ⇒ Object
- .highlight(log) ⇒ Object
- .line_index_cache ⇒ Object
- .line_index_cache_mutex ⇒ Object
- .line_index_cache_order ⇒ Object
- .line_index_cache_path(cache_key) ⇒ Object
- .line_index_cache_root ⇒ Object
- .log_root ⇒ Object
- .logger ⇒ Object
- .logs_atr(log_files) ⇒ Object
- .read_persistent_line_index(cache_key, signature) ⇒ Object
- .real_log_root ⇒ Object
- .resolve_log_file(log_file) ⇒ Object
- .store_line_index_cache(entry_key, offsets) ⇒ Object
- .structured_log_payload(line) ⇒ Object
- .tail_lines ⇒ Object
- .write_persistent_line_index(cache_key, signature, offsets) ⇒ Object
Instance Method Summary collapse
- #build_log_line_offsets ⇒ Object
- #build_request_group_index ⇒ Object
- #cached_log_line_offsets ⇒ Object
- #cached_request_group_index ⇒ Object
- #clear_logs ⇒ Object
- #custom_log_metadata(line) ⇒ Object
- #custom_log_severity(line) ⇒ Object
- #custom_log_timestamp(line) ⇒ Object
- #date_filtered_log? ⇒ Boolean
- #date_from_log_line(line) ⇒ Object
- #each_filtered_log_line(file) ⇒ Object
- #each_filtered_log_line_with_offset(file) ⇒ Object
- #each_log_line(file) ⇒ Object
- #each_log_line_with_offset(file) ⇒ Object
- #each_raw_log_line_with_offset(file) ⇒ Object
- #each_request_group(file) ⇒ Object
- #end_date ⇒ Object
- #filter_logs_with_date(file) ⇒ Object
- #filtered_request_groups? ⇒ Boolean
- #get_date_from_log_line(line) ⇒ Object
- #get_logs_from_file(file) ⇒ Object
- #get_test_logs(file) ⇒ Object
- #group_matches_filters?(group) ⇒ Boolean
- #grouped_log_data(error, divider) ⇒ Object
- #hourly_log?(file) ⇒ Boolean
-
#initialize(params) ⇒ PrettyLogger
constructor
A new instance of PrettyLogger.
- #line_include_date?(line) ⇒ Boolean
- #line_matches_filters?(line) ⇒ Boolean
- #log_data ⇒ Object
- #log_file_signature ⇒ Object
- #log_line_index_cache_key ⇒ Object
- #matching_request_group_index ⇒ Object
- #metadata_value(metadata, *keys) ⇒ Object
- #normalized_severity_filter ⇒ Object
- #read_log_lines_at_offsets(file, offsets) ⇒ Object
- #request_completion_metadata(line) ⇒ Object
- #request_group_from_index(group) ⇒ Object
- #request_group_index_cache_key ⇒ Object
- #request_grouping? ⇒ Boolean
- #request_start_metadata(line) ⇒ Object
- #request_timestamp(line) ⇒ Object
- #set_divider_value ⇒ Object
- #start_date ⇒ Object
- #structured_log_severity(line) ⇒ Object
- #structured_log_timestamp(line) ⇒ Object
- #tail_lines(file, count) ⇒ Object
- #tail_log_data ⇒ Object
- #test_log?(file) ⇒ Boolean
- #validate_date ⇒ Object
Constructor Details
#initialize(params) ⇒ PrettyLogger
Returns a new instance of PrettyLogger.
34 35 36 37 |
# File 'lib/rails/pretty/logger.rb', line 34 def initialize(params) @filter_params = params @log_file = self.class.resolve_log_file(params[:log_file]) end |
Instance Attribute Details
#log_file ⇒ Object (readonly)
Returns the value of attribute log_file.
32 33 34 |
# File 'lib/rails/pretty/logger.rb', line 32 def log_file @log_file end |
Class Method Details
.clear_line_index_cache! ⇒ Object
161 162 163 164 |
# File 'lib/rails/pretty/logger.rb', line 161 def self.clear_line_index_cache! clear_line_index_memory_cache! FileUtils.rm_rf(line_index_cache_root) end |
.clear_line_index_cache_for!(log_file) ⇒ Object
173 174 175 176 177 178 179 |
# File 'lib/rails/pretty/logger.rb', line 173 def self.clear_line_index_cache_for!(log_file) line_index_cache_mutex.synchronize do line_index_cache.delete_if { |(cache_key, _signature), _offsets| cache_key.first == log_file || cache_key[1] == log_file } line_index_cache_order.delete_if { |cache_key, _signature| cache_key.first == log_file || cache_key[1] == log_file } end FileUtils.rm_rf(line_index_cache_root) end |
.clear_line_index_memory_cache! ⇒ Object
166 167 168 169 170 171 |
# File 'lib/rails/pretty/logger.rb', line 166 def self.clear_line_index_memory_cache! line_index_cache_mutex.synchronize do line_index_cache.clear line_index_cache_order.clear end end |
.custom_log_metadata(line) ⇒ Object
118 119 120 121 122 123 124 |
# File 'lib/rails/pretty/logger.rb', line 118 def self.(line) parser = Rails::Pretty::Logger.configuration.log_line_parser return {} unless parser.respond_to?(:call) = parser.call(line) .is_a?(Hash) ? : {} end |
.ensure_file_size_within_limit!(log_file) ⇒ Object
100 101 102 103 104 105 |
# File 'lib/rails/pretty/logger.rb', line 100 def self.ensure_file_size_within_limit!(log_file) max_file_size = Rails::Pretty::Logger.configuration.max_file_size return if max_file_size.blank? raise FileTooLarge if File.size(log_file) > max_file_size.to_i end |
.fetch_line_index_cache(cache_key, signature) ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/rails/pretty/logger.rb', line 126 def self.fetch_line_index_cache(cache_key, signature) entry_key = [cache_key, signature] line_index_cache_mutex.synchronize do if line_index_cache.key?(entry_key) line_index_cache_order.delete(entry_key) line_index_cache_order << entry_key return line_index_cache.fetch(entry_key) end end if (offsets = read_persistent_line_index(cache_key, signature)) store_line_index_cache(entry_key, offsets) return offsets end offsets = yield.freeze write_persistent_line_index(cache_key, signature, offsets) store_line_index_cache(entry_key, offsets) offsets end |
.file_size(log_file) ⇒ Object
47 48 49 |
# File 'lib/rails/pretty/logger.rb', line 47 def self.file_size(log_file) File.size?("#{log_file}").to_f / 2**20 end |
.get_hourly_log_file_list ⇒ Object
85 86 87 88 |
# File 'lib/rails/pretty/logger.rb', line 85 def self.get_hourly_log_file_list log_files = Dir[File.join(log_root, "hourly", "**", "*")].select { |file| File.file?(file) }.sort logs_atr(log_files) end |
.get_log_file_list ⇒ Object
80 81 82 83 |
# File 'lib/rails/pretty/logger.rb', line 80 def self.get_log_file_list log_files = Dir[File.join(log_root, "*")].select { |file| File.file?(file) } logs_atr(log_files) end |
.highlight(log) ⇒ Object
43 44 45 |
# File 'lib/rails/pretty/logger.rb', line 43 def self.highlight(log) self.logger.tagged('HIGHLIGHT') { logger.info log } end |
.line_index_cache ⇒ Object
181 182 183 |
# File 'lib/rails/pretty/logger.rb', line 181 def self.line_index_cache @line_index_cache ||= {} end |
.line_index_cache_mutex ⇒ Object
189 190 191 |
# File 'lib/rails/pretty/logger.rb', line 189 def self.line_index_cache_mutex @line_index_cache_mutex ||= Mutex.new end |
.line_index_cache_order ⇒ Object
185 186 187 |
# File 'lib/rails/pretty/logger.rb', line 185 def self.line_index_cache_order @line_index_cache_order ||= [] end |
.line_index_cache_path(cache_key) ⇒ Object
212 213 214 |
# File 'lib/rails/pretty/logger.rb', line 212 def self.line_index_cache_path(cache_key) line_index_cache_root.join("#{Digest::SHA256.hexdigest(Marshal.dump(cache_key))}.marshal") end |
.line_index_cache_root ⇒ Object
216 217 218 |
# File 'lib/rails/pretty/logger.rb', line 216 def self.line_index_cache_root Rails.root.join("tmp", "cache", "rails_pretty_logger", "line_indexes") end |
.log_root ⇒ Object
51 52 53 |
# File 'lib/rails/pretty/logger.rb', line 51 def self.log_root Rails.root.join("log") end |
.logger ⇒ Object
39 40 41 |
# File 'lib/rails/pretty/logger.rb', line 39 def self.logger Rails.logger end |
.logs_atr(log_files) ⇒ Object
90 91 92 93 94 95 96 97 98 |
# File 'lib/rails/pretty/logger.rb', line 90 def self.logs_atr(log_files) log = {} log_files.each_with_index do |log_file,index| log[index] = {} log[index][:file_name] = log_file log[index][:file_size] = self.file_size(log_file).round(4) end log end |
.read_persistent_line_index(cache_key, signature) ⇒ Object
193 194 195 196 197 198 199 200 |
# File 'lib/rails/pretty/logger.rb', line 193 def self.read_persistent_line_index(cache_key, signature) payload = Marshal.load(File.binread(line_index_cache_path(cache_key))) return unless payload[:signature] == signature payload[:offsets].freeze rescue Errno::ENOENT, EOFError, TypeError, ArgumentError nil end |
.real_log_root ⇒ Object
75 76 77 78 |
# File 'lib/rails/pretty/logger.rb', line 75 def self.real_log_root FileUtils.mkdir_p(log_root) log_root.realpath end |
.resolve_log_file(log_file) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/rails/pretty/logger.rb', line 55 def self.resolve_log_file(log_file) raise InvalidLogFile if log_file.blank? candidate = Pathname.new(log_file.to_s) candidate = log_root.join(candidate) unless candidate.absolute? root_path = real_log_root real_path = candidate.realpath unless real_path.to_s == root_path.to_s || real_path.to_s.start_with?("#{root_path}/") raise InvalidLogFile end raise InvalidLogFile unless real_path.file? real_path.to_s rescue Errno::ENOENT, Errno::EACCES, ArgumentError raise InvalidLogFile end |
.store_line_index_cache(entry_key, offsets) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/rails/pretty/logger.rb', line 149 def self.store_line_index_cache(entry_key, offsets) line_index_cache_mutex.synchronize do line_index_cache[entry_key] = offsets line_index_cache_order.delete(entry_key) line_index_cache_order << entry_key while line_index_cache_order.length > LINE_INDEX_CACHE_LIMIT line_index_cache.delete(line_index_cache_order.shift) end end end |
.structured_log_payload(line) ⇒ Object
111 112 113 114 115 116 |
# File 'lib/rails/pretty/logger.rb', line 111 def self.structured_log_payload(line) payload = JSON.parse(line) payload if payload.is_a?(Hash) rescue JSON::ParserError, TypeError nil end |
.tail_lines ⇒ Object
107 108 109 |
# File 'lib/rails/pretty/logger.rb', line 107 def self.tail_lines Rails::Pretty::Logger.configuration.tail_lines.to_i.positive? ? Rails::Pretty::Logger.configuration.tail_lines.to_i : 500 end |
.write_persistent_line_index(cache_key, signature, offsets) ⇒ Object
202 203 204 205 206 207 208 209 210 |
# File 'lib/rails/pretty/logger.rb', line 202 def self.write_persistent_line_index(cache_key, signature, offsets) FileUtils.mkdir_p(line_index_cache_root) path = line_index_cache_path(cache_key) temp_path = "#{path}.#{$$}.tmp" File.binwrite(temp_path, Marshal.dump(signature: signature, offsets: offsets)) FileUtils.mv(temp_path, path) rescue SystemCallError, TypeError, ArgumentError FileUtils.rm_f(temp_path) if temp_path end |
Instance Method Details
#build_log_line_offsets ⇒ Object
423 424 425 426 427 428 429 430 431 |
# File 'lib/rails/pretty/logger.rb', line 423 def build_log_line_offsets offsets = [] each_log_line_with_offset(@log_file) do |line, offset| offsets << offset if line_matches_filters?(line) end offsets end |
#build_request_group_index ⇒ Object
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/rails/pretty/logger.rb', line 497 def build_request_group_index groups = [] current_group = nil each_log_line_with_offset(@log_file) do |line, offset| if ( = (line)) groups << current_group if current_group current_group = .merge(line_offsets: [offset]) elsif current_group current_group[:line_offsets] << offset current_group.merge!((line) || {}) else current_group = { type: :ungrouped, line_offsets: [offset] } end end groups << current_group if current_group groups end |
#cached_log_line_offsets ⇒ Object
417 418 419 420 421 |
# File 'lib/rails/pretty/logger.rb', line 417 def cached_log_line_offsets self.class.fetch_line_index_cache(log_line_index_cache_key, log_file_signature) do build_log_line_offsets end end |
#cached_request_group_index ⇒ Object
491 492 493 494 495 |
# File 'lib/rails/pretty/logger.rb', line 491 def cached_request_group_index self.class.fetch_line_index_cache(request_group_index_cache_key, log_file_signature) do build_request_group_index end end |
#clear_logs ⇒ Object
220 221 222 223 |
# File 'lib/rails/pretty/logger.rb', line 220 def clear_logs File.open(@log_file, File::TRUNC) {} self.class.clear_line_index_cache_for!(@log_file) end |
#custom_log_metadata(line) ⇒ Object
647 648 649 |
# File 'lib/rails/pretty/logger.rb', line 647 def (line) self.class.(line) end |
#custom_log_severity(line) ⇒ Object
642 643 644 645 |
# File 'lib/rails/pretty/logger.rb', line 642 def custom_log_severity(line) severity = ((line), :severity, :level, :log_level).to_s.upcase severity if SEVERITIES.include?(severity) end |
#custom_log_timestamp(line) ⇒ Object
638 639 640 |
# File 'lib/rails/pretty/logger.rb', line 638 def (line) ((line), :timestamp, :time, :datetime, :created_at) end |
#date_filtered_log? ⇒ Boolean
458 459 460 |
# File 'lib/rails/pretty/logger.rb', line 458 def date_filtered_log? !test_log?(@log_file) && !hourly_log?(@log_file) end |
#date_from_log_line(line) ⇒ Object
596 597 598 599 600 601 |
# File 'lib/rails/pretty/logger.rb', line 596 def date_from_log_line(line) = (line) || (line) || (line) &.to_date&.strftime("%Y-%m-%d") rescue Date::Error, NoMethodError nil end |
#each_filtered_log_line(file) ⇒ Object
237 238 239 240 241 |
# File 'lib/rails/pretty/logger.rb', line 237 def each_filtered_log_line(file) return enum_for(:each_filtered_log_line, file) unless block_given? each_filtered_log_line_with_offset(file) { |line, _offset| yield line } end |
#each_filtered_log_line_with_offset(file) ⇒ Object
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/rails/pretty/logger.rb', line 243 def each_filtered_log_line_with_offset(file) return enum_for(:each_filtered_log_line_with_offset, file) unless block_given? start = false each_raw_log_line_with_offset(file) do |line, offset| if get_date_from_log_line(line) start = true yield line, offset elsif start && !(line_include_date?(line)) yield line, offset else start = false end end end |
#each_log_line(file) ⇒ Object
268 269 270 271 272 |
# File 'lib/rails/pretty/logger.rb', line 268 def each_log_line(file) return enum_for(:each_log_line, file) unless block_given? each_log_line_with_offset(file) { |line, _offset| yield line } end |
#each_log_line_with_offset(file) ⇒ Object
274 275 276 277 278 279 280 281 282 |
# File 'lib/rails/pretty/logger.rb', line 274 def each_log_line_with_offset(file) return enum_for(:each_log_line_with_offset, file) unless block_given? if test_log?(file) || hourly_log?(file) each_raw_log_line_with_offset(file) { |line, offset| yield line, offset } else each_filtered_log_line_with_offset(file) { |line, offset| yield line, offset } end end |
#each_raw_log_line_with_offset(file) ⇒ Object
284 285 286 287 288 289 290 291 292 293 294 |
# File 'lib/rails/pretty/logger.rb', line 284 def each_raw_log_line_with_offset(file) return enum_for(:each_raw_log_line_with_offset, file) unless block_given? File.open(file, "r") do |io| until io.eof? offset = io.pos line = io.gets yield line, offset if line end end end |
#each_request_group(file) ⇒ Object
483 484 485 486 487 488 489 |
# File 'lib/rails/pretty/logger.rb', line 483 def each_request_group(file) return enum_for(:each_request_group, file) unless block_given? cached_request_group_index.each do |group| yield request_group_from_index(group) end end |
#end_date ⇒ Object
229 230 231 |
# File 'lib/rails/pretty/logger.rb', line 229 def end_date @filter_params.dig(:date_range, :end) || Time.now.strftime("%Y-%m-%d") end |
#filter_logs_with_date(file) ⇒ Object
233 234 235 |
# File 'lib/rails/pretty/logger.rb', line 233 def filter_logs_with_date(file) each_filtered_log_line(file).to_a end |
#filtered_request_groups? ⇒ Boolean
530 531 532 |
# File 'lib/rails/pretty/logger.rb', line 530 def filtered_request_groups? @filter_params[:query].to_s.strip.present? || normalized_severity_filter.present? end |
#get_date_from_log_line(line) ⇒ Object
296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/rails/pretty/logger.rb', line 296 def get_date_from_log_line(line) date = date_from_log_line(line) if date.present? start_date = @filter_params.dig(:date_range, :start) end_date = @filter_params.dig(:date_range, :end) if start_date.present? && end_date.present? date.between?(start_date, end_date) else date.between?(Time.now.strftime("%Y-%m-%d"), Time.now.strftime("%Y-%m-%d")) end end end |
#get_logs_from_file(file) ⇒ Object
264 265 266 |
# File 'lib/rails/pretty/logger.rb', line 264 def get_logs_from_file(file) each_log_line(file).to_a end |
#get_test_logs(file) ⇒ Object
260 261 262 |
# File 'lib/rails/pretty/logger.rb', line 260 def get_test_logs(file) IO.foreach(file).to_a end |
#group_matches_filters?(group) ⇒ Boolean
534 535 536 |
# File 'lib/rails/pretty/logger.rb', line 534 def group_matches_filters?(group) group.fetch(:lines).any? { |line| line_matches_filters?(line) } end |
#grouped_log_data(error, divider) ⇒ Object
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/rails/pretty/logger.rb', line 467 def grouped_log_data(error, divider) self.class.ensure_file_size_within_limit!(@log_file) page_start = @filter_params[:page].to_i * divider page_end = page_start + divider matching_groups = matching_request_group_index paginated_groups = (matching_groups[page_start...page_end] || []).map { |group| request_group_from_index(group) } { logs_count: (matching_groups.length.to_f / divider).ceil, paginated_logs: paginated_groups, error: error } end |
#hourly_log?(file) ⇒ Boolean
371 372 373 |
# File 'lib/rails/pretty/logger.rb', line 371 def hourly_log?(file) file.include?("#{File::SEPARATOR}hourly#{File::SEPARATOR}") end |
#line_include_date?(line) ⇒ Boolean
309 310 311 |
# File 'lib/rails/pretty/logger.rb', line 309 def line_include_date?(line) date_from_log_line(line).present? end |
#line_matches_filters?(line) ⇒ Boolean
375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/rails/pretty/logger.rb', line 375 def line_matches_filters?(line) query = @filter_params[:query].to_s.strip return false if query.present? && !line.downcase.include?(query.downcase) severity = @filter_params[:severity].to_s.upcase return true unless SEVERITIES.include?(severity) structured_severity = structured_log_severity(line) return structured_severity == severity if structured_severity.present? line.match?(/\b#{Regexp.escape(severity)}\b/i) end |
#log_data ⇒ Object
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/rails/pretty/logger.rb', line 325 def log_data error = validate_date divider = set_divider_value return grouped_log_data(error, divider) if request_grouping? page_start = @filter_params[:page].to_i * divider page_end = page_start + divider self.class.ensure_file_size_within_limit!(@log_file) line_offsets = cached_log_line_offsets paginated_logs = read_log_lines_at_offsets(@log_file, line_offsets[page_start...page_end] || []) data = {} data[:logs_count] = (line_offsets.length.to_f / divider).ceil data[:paginated_logs] = paginated_logs data[:error] = error return data end |
#log_file_signature ⇒ Object
453 454 455 456 |
# File 'lib/rails/pretty/logger.rb', line 453 def log_file_signature stat = File.stat(@log_file) [stat.size, stat.mtime.to_f, stat.ctime.to_f] end |
#log_line_index_cache_key ⇒ Object
442 443 444 445 446 447 448 449 450 451 |
# File 'lib/rails/pretty/logger.rb', line 442 def log_line_index_cache_key [ @log_file, date_filtered_log? ? start_date : nil, date_filtered_log? ? end_date : nil, @filter_params[:query].to_s.strip.downcase, normalized_severity_filter, Rails::Pretty::Logger.configuration.log_line_parser&.object_id ] end |
#matching_request_group_index ⇒ Object
517 518 519 520 521 522 |
# File 'lib/rails/pretty/logger.rb', line 517 def matching_request_group_index groups = cached_request_group_index return groups unless filtered_request_groups? groups.select { |group| group_matches_filters?(request_group_from_index(group)) } end |
#metadata_value(metadata, *keys) ⇒ Object
651 652 653 654 655 656 657 658 659 |
# File 'lib/rails/pretty/logger.rb', line 651 def (, *keys) keys.each do |key| string_key = key.to_s return [string_key] if .key?(string_key) return [key] if .key?(key) end nil end |
#normalized_severity_filter ⇒ Object
462 463 464 465 |
# File 'lib/rails/pretty/logger.rb', line 462 def normalized_severity_filter severity = @filter_params[:severity].to_s.upcase SEVERITIES.include?(severity) ? severity : nil end |
#read_log_lines_at_offsets(file, offsets) ⇒ Object
433 434 435 436 437 438 439 440 |
# File 'lib/rails/pretty/logger.rb', line 433 def read_log_lines_at_offsets(file, offsets) File.open(file, "r") do |io| offsets.map do |offset| io.seek(offset) io.gets end.compact end end |
#request_completion_metadata(line) ⇒ Object
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
# File 'lib/rails/pretty/logger.rb', line 575 def (line) = (line) response_status = (, :response_status, :status) duration = (, :duration, :request_duration) if response_status.present? || duration.present? return { status: response_status.to_s, duration: duration.to_s } end match = line.strip.match(/\ACompleted\s+(?<status>\d{3}).*?\sin\s+(?<duration>[\d.]+ms)/) return unless match { status: match[:status], duration: match[:duration] } end |
#request_group_from_index(group) ⇒ Object
524 525 526 527 528 |
# File 'lib/rails/pretty/logger.rb', line 524 def request_group_from_index(group) group.merge(lines: read_log_lines_at_offsets(@log_file, group.fetch(:line_offsets))).tap do |request_group| request_group.delete(:line_offsets) end end |
#request_group_index_cache_key ⇒ Object
538 539 540 541 542 543 544 545 546 |
# File 'lib/rails/pretty/logger.rb', line 538 def request_group_index_cache_key [ :request_groups, @log_file, date_filtered_log? ? start_date : nil, date_filtered_log? ? end_date : nil, Rails::Pretty::Logger.configuration.log_line_parser&.object_id ] end |
#request_grouping? ⇒ Boolean
413 414 415 |
# File 'lib/rails/pretty/logger.rb', line 413 def request_grouping? @filter_params[:group].to_s == "request" end |
#request_start_metadata(line) ⇒ Object
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 |
# File 'lib/rails/pretty/logger.rb', line 548 def (line) = (line) request_method = (, :request_method, :method) request_path = (, :request_path, :path) if request_method.present? && request_path.present? return { type: :request, method: request_method.to_s, path: request_path.to_s, ip: (, :request_ip, :ip), started_at: (, :request_started_at, :started_at, :timestamp, :time).to_s } end match = line.strip.match(/\AStarted\s+(?<method>[A-Z]+)\s+"(?<path>[^"]+)"(?:\s+for\s+(?<ip>\S+))?\s+at\s+(?<timestamp>.+)\z/) return unless match { type: :request, method: match[:method], path: match[:path], ip: match[:ip], started_at: match[:timestamp].strip } end |
#request_timestamp(line) ⇒ Object
603 604 605 606 |
# File 'lib/rails/pretty/logger.rb', line 603 def (line) match = line.strip.match(/\AStarted\s+.*\sat\s+(?<timestamp>.+)\z/) match[:timestamp] if match end |
#set_divider_value ⇒ Object
356 357 358 359 360 361 362 363 364 365 |
# File 'lib/rails/pretty/logger.rb', line 356 def set_divider_value if @filter_params[:date_range].blank? 100 elsif @filter_params[:date_range][:divider].blank? 100 else divider = @filter_params[:date_range][:divider].to_i divider.positive? ? divider : 100 end end |
#start_date ⇒ Object
225 226 227 |
# File 'lib/rails/pretty/logger.rb', line 225 def start_date @filter_params.dig(:date_range, :start) || Time.now.strftime("%Y-%m-%d") end |
#structured_log_severity(line) ⇒ Object
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
# File 'lib/rails/pretty/logger.rb', line 619 def structured_log_severity(line) severity = custom_log_severity(line) return severity if severity.present? payload = self.class.structured_log_payload(line) return unless payload STRUCTURED_SEVERITY_KEYS.each do |key| severity = payload[key].to_s.upcase return severity if SEVERITIES.include?(severity) end nested_log = payload["log"] return unless nested_log.respond_to?(:[]) nested_severity = nested_log["level"].to_s.upcase nested_severity if SEVERITIES.include?(nested_severity) end |
#structured_log_timestamp(line) ⇒ Object
608 609 610 611 612 613 614 615 616 617 |
# File 'lib/rails/pretty/logger.rb', line 608 def (line) payload = self.class.structured_log_payload(line) return unless payload STRUCTURED_TIMESTAMP_KEYS.each do |key| return payload[key].to_s if payload[key].present? end nil end |
#tail_lines(file, count) ⇒ Object
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
# File 'lib/rails/pretty/logger.rb', line 388 def tail_lines(file, count) count = count.to_i return [] unless count.positive? File.open(file, "rb") do |io| offset = io.size return [] if offset.zero? chunks = [] newline_count = 0 while offset.positive? && newline_count <= count chunk_size = [TAIL_READ_CHUNK_SIZE, offset].min offset -= chunk_size io.seek(offset) chunk = io.read(chunk_size) chunks.unshift(chunk) newline_count += chunk.count("\n") end chunks.join.lines.last(count).map { |line| line.force_encoding(Encoding.default_external) } end end |
#tail_log_data ⇒ Object
345 346 347 348 349 350 351 352 353 354 |
# File 'lib/rails/pretty/logger.rb', line 345 def tail_log_data self.class.ensure_file_size_within_limit!(@log_file) lines = tail_lines(@log_file, self.class.tail_lines).select { |line| line_matches_filters?(line) } { logs_count: lines.any? ? 1 : 0, paginated_logs: lines, error: nil } end |
#test_log?(file) ⇒ Boolean
367 368 369 |
# File 'lib/rails/pretty/logger.rb', line 367 def test_log?(file) File.basename(file).include?("test") end |
#validate_date ⇒ Object
313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/rails/pretty/logger.rb', line 313 def validate_date start_date = @filter_params.dig(:date_range, :start) end_date = @filter_params.dig(:date_range, :end) if (start_date.present? && end_date.present?) if (start_date > end_date) "End Date should not be less than Start Date." end else "Start and End Date must be given." end end |