Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 33 additions & 14 deletions app/channels/onlylogs/logs_channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,18 @@ def initialize_watcher(data)
return
end

cursor_position = data["cursor_position"] || 0
filter = data["filter"].presence
mode = data["mode"] || "live"
regexp_mode = data["regexp_mode"] == true || data["regexp_mode"] == "true"
start_position = data["start_position"]&.to_i || 0
end_position = data["end_position"]&.to_i

if mode == "search"
# For search mode, read the entire file with filter and send all matching lines
read_entire_file_with_filter(file_path, filter, regexp_mode, start_position, end_position)
if mode == "static"
# Read a bounded snapshot once, then finish.
read_static(file_path, filter, regexp_mode, start_position, end_position)
else
# For live mode, start the watcher
start_log_watcher(file_path, cursor_position, filter, regexp_mode)
# Follow the tail of the file indefinitely.
start_log_watcher(file_path, live_tail_position(file_path), filter, regexp_mode)
end
end

Expand All @@ -75,6 +74,14 @@ def cleanup_existing_operations
stop_log_watcher
end

# Bytes from the end of the file to show when starting a live tail without
# an explicit cursor (matches the default whole-file live-mode page load).
LIVE_TAIL_BYTES = 10_000

def live_tail_position(file_path)
[::File.size(file_path) - LIVE_TAIL_BYTES, 0].max
end

def start_log_watcher(file_path, cursor_position, filter = nil, regexp_mode = false)
return if @log_watcher_running

Expand All @@ -90,6 +97,7 @@ def start_log_watcher(file_path, cursor_position, filter = nil, regexp_mode = fa

@log_watcher_thread = Thread.new do
Rails.logger.silence(Logger::ERROR) do
current_byte_offset = cursor_position
@log_file.watch do |new_lines|
break unless @log_watcher_running

Expand All @@ -102,7 +110,8 @@ def start_log_watcher(file_path, cursor_position, filter = nil, regexp_mode = fa
# next
# end

lines_to_send << render_log_line(log_line)
lines_to_send << render_log_line(log_line, byte_offset: current_byte_offset)
current_byte_offset += log_line.bytesize
end

if lines_to_send.any?
Expand Down Expand Up @@ -143,11 +152,11 @@ def stop_log_watcher
@log_file = nil
end

def read_entire_file_with_filter(file_path, filter = nil, regexp_mode = false, start_position = 0, end_position = nil)
def read_static(file_path, filter = nil, regexp_mode = false, start_position = 0, end_position = nil)
@log_watcher_running = true
@log_file = Onlylogs::File.new(file_path, last_position: 0)

transmit({action: "message", content: "Searching..."})
transmit({action: "message", content: filter.present? ? "Searching..." : "Loading..."})

@batch_sender = BatchSender.new(self)
@batch_sender.start
Expand All @@ -156,11 +165,15 @@ def read_entire_file_with_filter(file_path, filter = nil, regexp_mode = false, s

begin
Rails.logger.silence(Logger::ERROR) do
@log_file.grep(filter, regexp_mode: regexp_mode, start_position: start_position, end_position: end_position) do |log_line|
@log_file.grep(filter, regexp_mode: regexp_mode, start_position: start_position, end_position: end_position) do |result|
break if @batch_sender.nil?

# Result is now a hash with {byte_offset, content}
byte_offset = result[:byte_offset]
log_line = result[:content]

# Add to batch buffer (sender thread will handle sending)
@batch_sender.add_line(render_log_line(log_line))
@batch_sender.add_line(render_log_line(log_line, byte_offset: byte_offset, show_expand_button: true))

line_count += 1
end
Expand All @@ -170,7 +183,7 @@ def read_entire_file_with_filter(file_path, filter = nil, regexp_mode = false, s
@batch_sender.stop

# Send completion message
if line_count >= Onlylogs.max_line_matches
if Onlylogs.max_line_matches && line_count >= Onlylogs.max_line_matches
transmit({action: "finish", content: "Search finished. Search results limit reached."})
else
transmit({action: "finish", content: "Search finished."})
Expand All @@ -184,8 +197,14 @@ def read_entire_file_with_filter(file_path, filter = nil, regexp_mode = false, s
end
end

def render_log_line(log_line)
"<pre>#{FilePathParser.parse(AnsiColorParser.parse(ERB::Util.html_escape(log_line)))}</pre>"
def render_log_line(log_line, byte_offset: nil, show_expand_button: false)
parsed = FilePathParser.parse(AnsiColorParser.parse(ERB::Util.html_escape(log_line)))

{
content: parsed,
byte_offset: byte_offset,
show_expand_button: show_expand_button
}
end
end
end
19 changes: 18 additions & 1 deletion app/controllers/onlylogs/logs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ def index
@filter = params[:filter]
@autoscroll = params[:autoscroll] != "false"
@regexp_mode = params[:regexp_mode] == "true"
@mode = @filter.blank? ? (params[:mode] || "live") : "search" # "live" or "search"
@mode = @filter.blank? ? (params[:mode] || "live") : "static" # "live" or "static"
@start_position = nil
@end_position = nil

handle_byte_offset if params[:byte_offset].present?
end

def download
Expand All @@ -31,6 +35,19 @@ def download

private

EXPLORE_WINDOW_BYTES = 10_000

def handle_byte_offset
byte_offset = params[:byte_offset]&.to_i
return unless byte_offset.present?

@start_position = [byte_offset - EXPLORE_WINDOW_BYTES, 0].max
@end_position = byte_offset + EXPLORE_WINDOW_BYTES
@filter = nil
@mode = "static"
@autoscroll = false
end

def selected_log_file_path
return default_log_file_path if params[:log_file_path].blank?
authorized_log_file_path(params[:log_file_path])
Expand Down
Loading
Loading