rails-pretty-logger 0.2.9 → 0.3.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +215 -35
  3. data/app/assets/javascripts/rails/pretty/logger/application.js +16 -23
  4. data/app/assets/stylesheets/rails/pretty/logger/application.css +1 -15
  5. data/app/assets/stylesheets/rails/pretty/logger/dashboards.css +463 -141
  6. data/app/assets/stylesheets/rails/pretty/logger/list.css +1 -94
  7. data/app/controllers/rails/pretty/logger/application_controller.rb +31 -0
  8. data/app/controllers/rails/pretty/logger/dashboards_controller.rb +9 -4
  9. data/app/controllers/rails/pretty/logger/hourly_logs_controller.rb +36 -4
  10. data/app/helpers/rails/pretty/logger/application_helper.rb +32 -0
  11. data/app/helpers/rails/pretty/logger/dashboards_helper.rb +114 -14
  12. data/app/views/layouts/rails/pretty/logger/application.html.erb +9 -8
  13. data/app/views/partials/_error_pagination.html.erb +12 -10
  14. data/app/views/partials/_log_entries.html.erb +5 -0
  15. data/app/views/partials/_log_filters.html.erb +14 -0
  16. data/app/views/partials/_pretyyloggernavbar.html.erb +14 -9
  17. data/app/views/rails/pretty/logger/dashboards/index.html.erb +37 -20
  18. data/app/views/rails/pretty/logger/dashboards/logs.html.erb +33 -14
  19. data/app/views/rails/pretty/logger/hourly_logs/index.html.erb +49 -25
  20. data/app/views/rails/pretty/logger/hourly_logs/logs.html.erb +35 -14
  21. data/config/locales/rails_pretty_logger.en.yml +35 -0
  22. data/config/locales/rails_pretty_logger.tr.yml +35 -0
  23. data/lib/generators/rails_pretty_logger/install/install_generator.rb +29 -0
  24. data/lib/generators/rails_pretty_logger/install/templates/rails_pretty_logger.rb +20 -0
  25. data/lib/rails/pretty/logger/active_support_logger.rb +3 -7
  26. data/lib/rails/pretty/logger/config/logger_config.rb +0 -16
  27. data/lib/rails/pretty/logger/configuration.rb +18 -0
  28. data/lib/rails/pretty/logger/console_logger.rb +2 -2
  29. data/lib/rails/pretty/logger/engine.rb +22 -4
  30. data/lib/rails/pretty/logger/rails_logger.rb +62 -41
  31. data/lib/rails/pretty/logger/version.rb +1 -1
  32. data/lib/rails/pretty/logger.rb +547 -31
  33. data/lib/tasks/rails/pretty/logger_tasks.rake +36 -23
  34. metadata +79 -38
  35. data/Rakefile +0 -22
  36. data/app/assets/javascripts/rails/pretty/logger/dashboards.js +0 -2
  37. data/app/assets/javascripts/rails/pretty/logger/list.min.js +0 -2
  38. data/app/models/rails/pretty/logger/application_record.rb +0 -9
  39. data/app/assets/config/{rails_pretty_logger_manifest.js → manifest.js} +1 -1
@@ -1,33 +1,52 @@
1
- <%= render "partials/pretyyloggernavbar", path_name: 'Hourly logs' %>
1
+ <%= render "partials/pretyyloggernavbar", path_name: t("rails_pretty_logger.navigation.hourly_logs") %>
2
2
 
3
- <%= render "partials/error_pagination", log: @log, log_data: @log_data , locals: {path: "logs_dashboards_path"} %>
3
+ <% unless rails_pretty_logger_tail_mode? %>
4
+ <%= render "partials/error_pagination", log: @log, log_data: @log_data , locals: {path: "logs_dashboards_path"} %>
5
+ <% end %>
6
+
7
+ <div class="form-group log-controls">
8
+ <div class="log-toolbar">
9
+ <% if rails_pretty_logger_tail_mode? %>
10
+ <%= link_to(t("rails_pretty_logger.logs.filtered_view"), logs_dashboards_path(rails_pretty_logger_log_filter_params(include_mode: false).merge(date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
11
+ <% else %>
12
+ <%= link_to(t("rails_pretty_logger.logs.tail_last_lines", count: Rails::Pretty::Logger::PrettyLogger.tail_lines), logs_dashboards_path(rails_pretty_logger_log_filter_params.merge(mode: "tail"))) %>
13
+ <% if rails_pretty_logger_request_grouping? %>
14
+ <%= link_to(t("rails_pretty_logger.logs.plain_lines"), logs_dashboards_path(rails_pretty_logger_log_filter_params(include_group: false).merge(date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
15
+ <% else %>
16
+ <%= link_to(t("rails_pretty_logger.logs.group_requests"), logs_dashboards_path(rails_pretty_logger_log_filter_params.merge(group: "request", date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
17
+ <% end %>
18
+ <% end %>
19
+ </div>
20
+
21
+ <%= render "partials/log_filters", path: "logs_dashboards_path" %>
4
22
 
5
- <div class="form-group">
6
23
  <%= form_for :date_range, url: logs_dashboards_path(log_file: params[:log_file]), method: :post do |f| %>
24
+ <%= hidden_field_tag :query, params[:query] if params[:query].present? %>
25
+ <%= hidden_field_tag :severity, params[:severity] if params[:severity].present? %>
26
+ <%= hidden_field_tag :group, params[:group] if params[:group].present? %>
7
27
  <p>
8
- <%= label(:start, :title, "Start Date:") %>
28
+ <%= label(:start, :title, t("rails_pretty_logger.filters.start_date")) %>
9
29
  <%= f.date_field(:start, value: @log.start_date, max: Date.today) %>
10
- <%= label(:end, :title, "End Date:") %>
30
+ <%= label(:end, :title, t("rails_pretty_logger.filters.end_date")) %>
11
31
  <%= f.date_field(:end, value: @log.end_date, max: Date.today) %>
12
- <%= label(:divider, :title, "Logs per page:") %>
32
+ <%= label(:divider, :title, t("rails_pretty_logger.filters.logs_per_page")) %>
13
33
  <%= f.number_field(:divider, value: set_divider(params)) %>
14
- <%= f.submit "Submit" %>
34
+ <%= f.submit t("rails_pretty_logger.actions.submit") %>
15
35
  </p>
16
36
  <%- end%>
17
37
 
18
- <% if @log_data[:logs_count] > 0 %>
38
+ <% if @log_data[:logs_count] > 0 && !rails_pretty_logger_read_only? %>
19
39
  <p>
20
- <%= link_to("Clear logs",
40
+ <%= button_to(t("rails_pretty_logger.actions.clear_logs"),
21
41
  clear_logs_dashboards_path(log_file: params[:log_file]),
22
- html_options = {class: "clear_logs",
23
42
  method: :post,
24
- data: { confirm: "Are you sure to clear all logs? " }}) %>
43
+ class: "clear_logs",
44
+ form: { class: "clear_logs_form",
45
+ data: { turbo_confirm: t("rails_pretty_logger.confirmations.clear_all") }}) %>
25
46
  </p>
26
47
  <% end %>
27
48
  </div>
28
49
 
29
50
  <hr>
30
51
 
31
- <% @log_data[:paginated_logs].each do |line| %>
32
- <%= check_highlight(line) %> <br>
33
- <% end %>
52
+ <%= render "partials/log_entries", log_data: @log_data %>
@@ -1,39 +1,63 @@
1
- <% if @log_file_list.count == 0 %>
2
- <div>
3
- <h1 class="message">There is no log file to show</h1>
1
+ <% unless @hourly_logs_present %>
2
+ <div class="empty-state">
3
+ <h1 class="message"><%= t("rails_pretty_logger.states.no_logs") %></h1>
4
4
  </div>
5
5
 
6
6
  <% else %>
7
7
 
8
8
  <div class="hourly-list">
9
-
10
9
  <div id="hourly">
11
-
12
10
  <div class="logger_navbar">
13
- <p>
14
- <%= link_to('Main Logs',dashboards_path, html_options = {class: "dashboard_button"}) %>
15
- </p>
16
-
11
+ <div class="logger_navbar__heading">
12
+ <h1><%= t("rails_pretty_logger.navigation.hourly_logs") %></h1>
13
+ </div>
17
14
 
15
+ <nav class="logger_navbar__actions">
16
+ <%= link_to(t("rails_pretty_logger.navigation.main_logs"), dashboards_path, class: "dashboard_button") %>
17
+ </nav>
18
18
  </div>
19
- <input class="search" placeholder="Search" />
20
- <button class="sort" data-sort="name">
21
- Sort
22
- </button>
23
- <ul class="pagination"></ul>
24
19
 
25
- <ul class="list">
26
- <% @log_file_list .each do |key, value| %>
27
- <li>
28
- <h5 class="name">
29
- <%= link_to(modify_name(value.fetch(:file_name)),
30
- logs_hourly_logs_path(log_file: value.fetch(:file_name), date_range: { start: time_now, end: time_now }),
31
- html_options = {class: "dashboard_button",
32
- data: { confirm: "Log file size is #{ value.fetch(:file_size) } MB. Are you sure to open this file? " }}) %>
33
- </h5>
34
- <% end %>
35
- </li>
20
+ <%= form_with url: hourly_logs_path, method: :get, scope: nil, class: "hourly_filters filter-panel" do |form| %>
21
+ <%= form.label :search, t("rails_pretty_logger.filters.search") %>
22
+ <%= form.search_field :search, value: params[:search], placeholder: t("rails_pretty_logger.filters.search"), class: "search" %>
23
+ <%= form.hidden_field :sort, value: params[:sort] %>
24
+ <%= form.submit t("rails_pretty_logger.filters.search") %>
25
+ <%= link_to(t("rails_pretty_logger.actions.sort", direction: params[:sort] == "desc" ? "asc" : "desc"),
26
+ hourly_logs_path(search: params[:search], sort: params[:sort] == "desc" ? "asc" : "desc"),
27
+ class: "sort") %>
28
+ <% end %>
29
+
30
+ <% if @log_file_list.empty? %>
31
+ <div class="empty-state empty-state--inline">
32
+ <h1 class="message"><%= t("rails_pretty_logger.states.no_logs") %></h1>
33
+ </div>
34
+ <% end %>
35
+
36
+ <ul class="list log-file-list">
37
+ <% @log_file_list.each do |value| %>
38
+ <li class="log-file-row">
39
+ <div class="log-file-row__main">
40
+ <%= link_to(modify_name(value.fetch(:file_name)),
41
+ logs_hourly_logs_path(log_file: value.fetch(:file_name), date_range: { start: time_now, end: time_now }),
42
+ html_options = {class: "name log-file-row__name",
43
+ data: { confirm: t("rails_pretty_logger.confirmations.open_file", size: value.fetch(:file_size)) }}) %>
44
+ <span class="log-file-row__meta"><%= t("rails_pretty_logger.logs.file_size", size: value.fetch(:file_size)) %></span>
45
+ </div>
46
+ </li>
47
+ <% end %>
36
48
  </ul>
49
+
50
+ <% if @total_pages > 1 %>
51
+ <ul class="pagination">
52
+ <% 1.upto(@total_pages) do |page| %>
53
+ <li>
54
+ <%= link_to page,
55
+ hourly_logs_path(search: params[:search], sort: params[:sort], page: page),
56
+ class: ("active" if page == @page) %>
57
+ </li>
58
+ <% end %>
59
+ </ul>
60
+ <% end %>
37
61
  </div>
38
62
  </div>
39
63
  <% end %>
@@ -1,35 +1,56 @@
1
1
  <div class="logger_navbar">
2
- <p>
3
- <%= link_to('Main Logs',dashboards_path, html_options = {class: "dashboard_button"}) %>
4
- <%= link_to('Hourly Logs ', hourly_logs_path, html_options = {class: "dashboard_button"}) %>
5
- </p>
2
+ <div class="logger_navbar__heading">
3
+ <h1><%= t("rails_pretty_logger.navigation.hourly_logs") %></h1>
4
+ </div>
5
+
6
+ <nav class="logger_navbar__actions">
7
+ <%= link_to(t("rails_pretty_logger.navigation.main_logs"), dashboards_path, class: "dashboard_button") %>
8
+ <%= link_to(t("rails_pretty_logger.navigation.hourly_logs"), hourly_logs_path, class: "dashboard_button") %>
9
+ </nav>
6
10
  </div>
7
- <hr>
11
+
8
12
  <% if @log_data[:logs_count] > 0 %>
9
13
 
14
+ <% unless rails_pretty_logger_tail_mode? %>
10
15
  <%= render "partials/error_pagination", log: @log, log_data: @log_data, locals: {path: "logs_hourly_logs_path"} %>
16
+ <% end %>
11
17
 
12
- <% if @log_data[:logs_count] > 0 %>
18
+ <div class="form-group log-controls">
19
+ <div class="log-toolbar">
20
+ <% if rails_pretty_logger_tail_mode? %>
21
+ <%= link_to(t("rails_pretty_logger.logs.filtered_view"), logs_hourly_logs_path(rails_pretty_logger_log_filter_params(include_mode: false).merge(date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
22
+ <% else %>
23
+ <%= link_to(t("rails_pretty_logger.logs.tail_last_lines", count: Rails::Pretty::Logger::PrettyLogger.tail_lines), logs_hourly_logs_path(rails_pretty_logger_log_filter_params.merge(mode: "tail"))) %>
24
+ <% if rails_pretty_logger_request_grouping? %>
25
+ <%= link_to(t("rails_pretty_logger.logs.plain_lines"), logs_hourly_logs_path(rails_pretty_logger_log_filter_params(include_group: false).merge(date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
26
+ <% else %>
27
+ <%= link_to(t("rails_pretty_logger.logs.group_requests"), logs_hourly_logs_path(rails_pretty_logger_log_filter_params.merge(group: "request", date_range: { start: @log.start_date, end: @log.end_date, divider: set_divider(params) }))) %>
28
+ <% end %>
29
+ <% end %>
30
+ </div>
31
+
32
+ <%= render "partials/log_filters", path: "logs_hourly_logs_path" %>
33
+
34
+ <% if @log_data[:logs_count] > 0 && !rails_pretty_logger_read_only? %>
13
35
  <p>
14
- <%= link_to("Clear logs",
36
+ <%= button_to(t("rails_pretty_logger.actions.clear_logs"),
15
37
  clear_logs_hourly_logs_path(log_file: params[:log_file]),
16
- html_options = {class: "clear_logs",
17
38
  method: :post,
18
- data: { confirm: "Are you sure to clear all logs? " }}) %>
39
+ class: "clear_logs",
40
+ form: { class: "clear_logs_form",
41
+ data: { turbo_confirm: t("rails_pretty_logger.confirmations.clear_all") }}) %>
19
42
  </p>
20
43
  <% end %>
21
44
  </div>
22
45
 
23
46
 
24
- <hr>
47
+ <hr>
25
48
 
26
- <% @log_data[:paginated_logs].each do |line| %>
27
- <%= check_highlight(line) %> <br>
28
- <% end %>
49
+ <%= render "partials/log_entries", log_data: @log_data %>
29
50
 
30
51
  <% else %>
31
52
 
32
53
  <div>
33
- <h1 class="message">There is no log file to show</h1>
54
+ <h1 class="message"><%= t("rails_pretty_logger.states.no_logs") %></h1>
34
55
  </div>
35
56
  <% end %>
@@ -0,0 +1,35 @@
1
+ en:
2
+ rails_pretty_logger:
3
+ actions:
4
+ clear: "Clear"
5
+ clear_logs: "Clear logs"
6
+ sort: "Sort %{direction}"
7
+ submit: "Submit"
8
+ confirmations:
9
+ clear_all: "Are you sure to clear all logs?"
10
+ clear_file: "Are you sure to clear all logs from %{file}?"
11
+ open_file: "Log file size is %{size} MB. Are you sure to open this file?"
12
+ filters:
13
+ clear_filters: "Clear filters"
14
+ end_date: "End Date:"
15
+ filter: "Filter"
16
+ logs_per_page: "Logs per page:"
17
+ search: "Search"
18
+ search_log_content: "Search log content"
19
+ severity: "Severity"
20
+ start_date: "Start Date:"
21
+ logs:
22
+ file_size: "%{size} MB"
23
+ filtered_view: "Filtered view"
24
+ group_requests: "Group requests"
25
+ plain_lines: "Plain lines"
26
+ tail_last_lines: "Tail last %{count} lines"
27
+ ungrouped_lines: "Ungrouped lines"
28
+ navigation:
29
+ hourly_logs: "Hourly logs"
30
+ log_files: "Log files"
31
+ main_logs: "Main logs"
32
+ pagination: "Pagination"
33
+ states:
34
+ no_logs: "There is no log file to show"
35
+ stdout_logging: "RAILS_LOG_TO_STDOUT is present. No logs are being kept."
@@ -0,0 +1,35 @@
1
+ tr:
2
+ rails_pretty_logger:
3
+ actions:
4
+ clear: "Temizle"
5
+ clear_logs: "Logları temizle"
6
+ sort: "%{direction} sırala"
7
+ submit: "Gönder"
8
+ confirmations:
9
+ clear_all: "Tüm logları temizlemek istediğine emin misin?"
10
+ clear_file: "%{file} dosyasındaki tüm logları temizlemek istediğine emin misin?"
11
+ open_file: "Log dosyası %{size} MB. Bu dosyayı açmak istediğine emin misin?"
12
+ filters:
13
+ clear_filters: "Filtreleri temizle"
14
+ end_date: "Bitiş tarihi:"
15
+ filter: "Filtrele"
16
+ logs_per_page: "Sayfa başına log:"
17
+ search: "Ara"
18
+ search_log_content: "Log içeriğinde ara"
19
+ severity: "Seviye"
20
+ start_date: "Başlangıç tarihi:"
21
+ logs:
22
+ file_size: "%{size} MB"
23
+ filtered_view: "Filtrelenmiş görünüm"
24
+ group_requests: "İstekleri grupla"
25
+ plain_lines: "Düz satırlar"
26
+ tail_last_lines: "Son %{count} satır"
27
+ ungrouped_lines: "Gruplanmamış satırlar"
28
+ navigation:
29
+ hourly_logs: "Saatlik loglar"
30
+ log_files: "Log dosyaları"
31
+ main_logs: "Ana loglar"
32
+ pagination: "Sayfalama"
33
+ states:
34
+ no_logs: "Gösterilecek log dosyası yok"
35
+ stdout_logging: "RAILS_LOG_TO_STDOUT mevcut. Log dosyası tutulmuyor."
@@ -0,0 +1,29 @@
1
+ require "rails/generators"
2
+
3
+ module RailsPrettyLogger
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ JAVASCRIPT_MANIFEST_LINK = "//= link rails/pretty/logger/application.js".freeze
7
+
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ def copy_initializer
11
+ template "rails_pretty_logger.rb", "config/initializers/rails_pretty_logger.rb"
12
+ end
13
+
14
+ def mount_engine
15
+ route %(mount Rails::Pretty::Logger::Engine => "/rails-pretty-logger")
16
+ end
17
+
18
+ def link_javascript_asset
19
+ manifest = "app/assets/config/manifest.js"
20
+ manifest_path = File.join(destination_root, manifest)
21
+ return unless File.exist?(manifest_path)
22
+ return if File.read(manifest_path).include?(JAVASCRIPT_MANIFEST_LINK)
23
+
24
+ separator = File.read(manifest_path).end_with?("\n") ? "" : "\n"
25
+ append_to_file manifest, "#{separator}#{JAVASCRIPT_MANIFEST_LINK}\n"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ Rails::Pretty::Logger.configure do |config|
2
+ # The hook runs inside the Rails Pretty Logger engine controller.
3
+ # Example for apps that expose authenticate_user!:
4
+ # config.authenticate_with = -> { authenticate_user! }
5
+ config.authenticate_with = nil
6
+
7
+ # Production dashboards should usually be read-only unless clearing logs is explicitly desired.
8
+ config.read_only = Rails.env.production?
9
+
10
+ # Set to nil to allow any log file size.
11
+ config.max_file_size = 50.megabytes
12
+
13
+ # Number of lines shown by the tail view.
14
+ config.tail_lines = 500
15
+
16
+ # Optional parser for custom log formats. Return nil for lines the parser does not handle.
17
+ # Supported keys include :timestamp, :severity, :request_method, :request_path,
18
+ # :request_ip, :response_status, and :duration.
19
+ # config.log_line_parser = ->(line) { nil }
20
+ end
@@ -5,11 +5,7 @@ module Rails::Pretty::Logger
5
5
 
6
6
  class ActiveSupportLogger < RailsLogger
7
7
  include ActiveSupport::LoggerThreadSafeLevel
8
- if Rails::VERSION::MAJOR < 6.0
9
- include LoggerSilence
10
- else
11
- include ActiveSupport::LoggerSilence
12
- end
8
+ include ActiveSupport::LoggerSilence
13
9
 
14
10
  # Returns true if the logger destination matches one of the sources
15
11
  #
@@ -80,8 +76,8 @@ module Rails::Pretty::Logger
80
76
  end
81
77
  end
82
78
 
83
- def initialize(*args)
84
- super
79
+ def initialize(*args, **kwargs)
80
+ super(*args, **kwargs)
85
81
  @formatter = SimpleFormatter.new
86
82
  after_initialize if respond_to? :after_initialize
87
83
  end
@@ -1,17 +1 @@
1
1
  require "rails/pretty/logger/console_logger"
2
- require "rails/pretty/logger/active_support_logger"
3
-
4
-
5
- module Rails
6
- module Pretty
7
- module Logger
8
- module Config
9
-
10
- class LoggerConfig < Rails::Application
11
-
12
- end
13
-
14
- end
15
- end
16
- end
17
- end
@@ -0,0 +1,18 @@
1
+ module Rails::Pretty::Logger
2
+ class Configuration
3
+ attr_accessor :authenticate_with, :log_line_parser, :max_file_size, :tail_lines
4
+ attr_writer :read_only
5
+
6
+ def initialize
7
+ @authenticate_with = nil
8
+ @log_line_parser = nil
9
+ @read_only = Rails.env.production?
10
+ @max_file_size = nil
11
+ @tail_lines = 500
12
+ end
13
+
14
+ def read_only?
15
+ @read_only == true
16
+ end
17
+ end
18
+ end
@@ -5,8 +5,8 @@ module Rails::Pretty::Logger
5
5
 
6
6
  class ConsoleLogger < ActiveSupportLogger
7
7
 
8
- def initialize(*args)
9
- super(*args)
8
+ def initialize(*args, **kwargs)
9
+ super(*args, **kwargs)
10
10
  @formatter = ConsoleFormatter.new
11
11
  end
12
12
  end
@@ -3,11 +3,29 @@ module Rails
3
3
  module Logger
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace Rails::Pretty::Logger
6
- ActiveSupport.on_load(:action_controller) do
7
- include Rails::Pretty::Logger
6
+
7
+ initializer "rails_pretty_logger.assets" do |app|
8
+ assets_config = app.config.assets if app.config.respond_to?(:assets)
9
+
10
+ if assets_config.respond_to?(:paths) && assets_config.respond_to?(:precompile)
11
+ %w[stylesheets javascripts].each do |asset_path|
12
+ path = root.join("app/assets", asset_path).to_s
13
+ assets_config.paths << path unless assets_config.paths.include?(path)
14
+ end
15
+
16
+ %w[
17
+ rails/pretty/logger/application.js
18
+ rails/pretty/logger/application.css
19
+ rails/pretty/logger/dashboards.css
20
+ rails/pretty/logger/list.css
21
+ ].each do |asset|
22
+ assets_config.precompile << asset unless assets_config.precompile.include?(asset)
23
+ end
24
+ end
8
25
  end
9
- initializer "rails-pretty-logger.assets.precompile" do |app|
10
- app.config.assets.precompile += %w( rails/pretty/logger/application.css rails/pretty/logger/application.js)
26
+
27
+ rake_tasks do
28
+ load root.join("lib/tasks/rails/pretty/logger_tasks.rake")
11
29
  end
12
30
  end
13
31
  end
@@ -1,4 +1,7 @@
1
1
 
2
+ require "fileutils"
3
+ require "logger"
4
+
2
5
  module Rails::Pretty::Logger
3
6
 
4
7
  class RailsLogger < ::Logger
@@ -7,14 +10,10 @@ module Rails::Pretty::Logger
7
10
  progname: nil, formatter: nil, datetime_format: nil,
8
11
  shift_period_suffix: '%Y%m%d')
9
12
 
10
- self.level = level
11
- self.progname = progname
12
- @default_formatter = Formatter.new
13
- self.datetime_format = datetime_format
14
- self.formatter = formatter
13
+ super(nil, level: level, progname: progname, formatter: formatter, datetime_format: datetime_format)
15
14
  @logdev = nil
16
15
  if logdev
17
- log_name = "log/" + logdev + ".log"
16
+ log_name = Rails.root.join("log", "#{logdev}.log").to_s
18
17
  @logdev = LoggerDevice.new(log_name, :shift_age => shift_age,
19
18
  :shift_size => shift_size,
20
19
  :shift_period_suffix => shift_period_suffix, file_count: file_count )
@@ -95,48 +94,70 @@ module Rails::Pretty::Logger
95
94
  end
96
95
 
97
96
  def shift_log_period(period_end)
98
- suffix = period_end.strftime(@shift_period_suffix)
97
+ with_rotation_lock do
98
+ suffix = period_end.strftime(@shift_period_suffix)
99
+
100
+ suffix_year = period_end.strftime('%Y')
101
+ suffix_month = period_end.strftime('%m')
102
+ suffix_day = period_end.strftime('%d')
103
+
104
+ if @shift_age == 'hourly'
105
+ suffix = period_end.strftime('%Y%m%d_%H%M')
106
+ end
99
107
 
100
- suffix_year = period_end.strftime('%Y')
101
- suffix_month = period_end.strftime('%m')
102
- suffix_day = period_end.strftime('%d')
108
+ age_file = available_log_path("#{@filename}.#{suffix}")
109
+
110
+ @dev.close rescue nil
111
+
112
+ File.rename("#{@filename}", age_file)
113
+ new_path = File.join(Rails.root, 'log', 'hourly', suffix_year, suffix_month, suffix_day)
114
+ FileUtils.mkdir_p new_path
115
+ destination = available_log_path(File.join(new_path, File.basename(age_file)))
116
+ FileUtils.mv age_file, destination
117
+ delete_old_hourly_files
118
+ @dev = create_logfile(@filename)
119
+ true
120
+ end
121
+ end
103
122
 
104
- if @shift_age == 'hourly'
105
- suffix = period_end.strftime('%Y%m%d_%H%M')
123
+ def with_rotation_lock
124
+ FileUtils.mkdir_p(File.dirname(rotation_lock_path))
125
+ File.open(rotation_lock_path, File::RDWR | File::CREAT, 0644) do |lock|
126
+ lock.flock(File::LOCK_EX)
127
+ yield
106
128
  end
129
+ end
107
130
 
108
- age_file = "#{@filename}.#{suffix}"
131
+ def rotation_lock_path
132
+ Rails.root.join("tmp", "rails_pretty_logger", "#{File.basename(@filename)}.rotate.lock").to_s
133
+ end
109
134
 
110
- if FileTest.exist?(age_file)
111
- # try to avoid filename crash caused by Timestamp change.
112
- idx = 0
113
- # .99 can be overridden; avoid too much file search with 'loop do'
114
- while idx < 100
115
- idx += 1
116
- age_file = "#{@filename}.#{suffix}.#{idx}"
117
- break unless FileTest.exist?(age_file)
118
- end
135
+ def available_log_path(path)
136
+ candidate = path
137
+ index = 0
138
+ while File.exist?(candidate)
139
+ index += 1
140
+ candidate = "#{path}.#{index}"
119
141
  end
142
+ candidate
143
+ end
120
144
 
121
- #delete old files
122
- log_files = Dir[ File.join(Rails.root, 'log', 'hourly') + "/#{suffix_year}/**/*"].reject {|fn| File.directory?(fn) }
123
- while (log_files.length > @file_count) do
124
- arr = log_files.reduce([]){|memo, log_file| memo << File.ctime(log_file).to_i}
125
- file_index = arr.index(arr.min)
126
- file_path = log_files[file_index]
127
- delete_old_file(file_path)
128
- log_files = Dir[ File.join(Rails.root, 'log', 'hourly') + "/#{suffix_year}/**/*"].reject {|fn| File.directory?(fn) }
145
+ def delete_old_hourly_files
146
+ log_files = hourly_log_files
147
+ while log_files.length > @file_count
148
+ delete_old_file(log_files.min_by { |log_file| hourly_log_sort_key(log_file) })
149
+ log_files = hourly_log_files
129
150
  end
151
+ end
130
152
 
131
- @dev.close rescue nil
153
+ def hourly_log_files
154
+ log_prefix = "#{File.basename(@filename)}."
155
+ Dir[File.join(Rails.root, 'log', 'hourly', '**', '*')]
156
+ .select { |file| File.file?(file) && File.basename(file).start_with?(log_prefix) }
157
+ end
132
158
 
133
- File.rename("#{@filename}", age_file)
134
- old_log_path = Rails.root.join(age_file)
135
- new_path = File.join(Rails.root, 'log', 'hourly', suffix_year, suffix_month, suffix_day)
136
- FileUtils.mkdir_p new_path
137
- FileUtils.mv old_log_path, new_path, :force => true
138
- @dev = create_logfile(@filename)
139
- return true
159
+ def hourly_log_sort_key(file)
160
+ File.basename(file)[/\.([0-9]{8}_[0-9]{4})(?:\.[0-9]+)?\z/, 1] || File.mtime(file).utc.strftime("%Y%m%d_%H%M")
140
161
  end
141
162
 
142
163
  def delete_old_file(file_path)
@@ -144,9 +165,9 @@ module Rails::Pretty::Logger
144
165
  month_dir = File.expand_path("..",day_dir)
145
166
  year_dir = File.expand_path("../..",day_dir)
146
167
  File.delete(file_path) if File.exist?(file_path)
147
- Dir.rmdir(day_dir) if Dir.empty?(day_dir)
148
- Dir.rmdir(month_dir) if month_dir.empty?
149
- Dir.rmdir(year_dir) if year_dir.empty?
168
+ Dir.rmdir(day_dir) if Dir.exist?(day_dir) && Dir.empty?(day_dir)
169
+ Dir.rmdir(month_dir) if Dir.exist?(month_dir) && Dir.empty?(month_dir)
170
+ Dir.rmdir(year_dir) if Dir.exist?(year_dir) && Dir.empty?(year_dir)
150
171
  end
151
172
  end
152
173
 
@@ -1,7 +1,7 @@
1
1
  module Rails
2
2
  module Pretty
3
3
  module Logger
4
- VERSION = '0.2.9'
4
+ VERSION = "0.3.0"
5
5
  end
6
6
  end
7
7
  end