lib/support/rake/task_logger.rb (89 lines of code) (raw):
# frozen_string_literal: true
require 'fileutils'
module Support
module Rake
class TaskLogger
def self.set_current!(logger)
Thread.current[:gdk_task_logger] = logger
logger&.create_latest_symlink!(@symlink_mutex ||= Mutex.new)
end
def self.current
Thread.current[:gdk_task_logger]
end
def self.start_time
@start_time ||= Time.now
end
def self.from_task(task)
new("#{TaskLogger.logs_dir}/#{task.name.gsub(%r{[:\s/.]+}, '-')}.log")
end
def self.logs_dir
"#{GDK.root}/log/gdk/rake-#{TaskLogger.start_time.strftime('%Y-%m-%d_%H-%M-%S_%L')}"
end
attr_reader :file_path, :recent_line
def initialize(file_path)
@file_path = Pathname(file_path)
create_logs_dir!
end
def file
@file ||= File.open(@file_path, 'w').tap { |file| file.sync = true }
end
# Input must have at least one valid char. This excludes newlines and separators-only lines.
INPUT_REGEXP = /\w/
# Rails noise.
IGNORE_INPUT_NOISE = 'DEPRECATION WARNING'
private_constant :INPUT_REGEXP, :IGNORE_INPUT_NOISE
def record_input(string)
return unless string
recent_line = string
.split("\n")
.reverse_each
.find { |line| !line.include?(IGNORE_INPUT_NOISE) && INPUT_REGEXP.match?(line) }
@recent_line = recent_line if recent_line
end
def cleanup!(delete: true)
return if @file&.closed?
File.delete(@file_path) if @file&.size === 0 && delete
@file&.close
end
def tail(max_lines: 25, exclude_gems: true)
f = File.read(@file_path)
original_lines = f.split("\n")
lines = original_lines.reject { |l| exclude_gems && l.include?('/ruby/gems/') }
needs_log_link = lines.length > max_lines || original_lines.length > lines.length
lines = lines.last(max_lines)
lines.push("", "See #{@file_path} for the full log.") if needs_log_link
lines.join("\n")
end
def create_latest_symlink!(mutex)
mutex.synchronize do
link_name = "#{GDK.root}/log/gdk/rake-latest"
next if File.symlink?(link_name) && File.readlink(link_name) == TaskLogger.logs_dir
FileUtils.ln_sf(TaskLogger.logs_dir, link_name)
end
end
private
def create_logs_dir!
file_path.parent.mkpath
end
end
end
end
# Inspired by https://stackoverflow.com/a/16184325/6403374
# but adjusted so we can use it with our own task logger
#
# We use __send__ to proxy IO function on Kernel.
# rubocop:disable GitlabSecurity/PublicSend
module Kernel
[:printf, :p, :print, :puts, :warn].each do |method|
name = "__#{method}__"
alias_method name, method
define_method(method) do |*args|
logger = Support::Rake::TaskLogger.current
return __send__(name, *args) unless logger
gdk_rake_log_lock.synchronize do
$stdout = logger.file
$stderr = logger.file
__send__(name, *args)
ensure
$stdout = STDOUT
$stderr = STDERR
end
end
end
private
def gdk_rake_log_lock
@gdk_rake_log_lock ||= Mutex.new
end
end
# rubocop:enable GitlabSecurity/PublicSend