<commit_before># If you require the TeeLogger then
class TeeLogWriter
  def initialize
    logdir = FileUtils.mkdir_p(File.join(caproot,'log')).first
    @file=File.open(File.join(logdir,'deploy.log'), "w")
  end

  def puts(message)
    message = message.respond_to?(:force_encoding) ? message.force_encoding("UTF-8") : message
    STDOUT.puts message
    @file.puts "[#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}"
  end

  ##
  # return the directory that holds the capfile
  def caproot
    @caproot ||= File.dirname(capfile)
  end

  private

  ##
  # Find the location of the capfile
  def capfile
    previous = nil
    current  = File.expand_path(Dir.pwd)

    until !File.directory?(current) || current == previous
      filename = File.join(current, 'Capfile')
      return filename if File.file?(filename)
      current, previous = File.expand_path("..", current), current
    end
  end

end

Capistrano::Configuration.instance(true).load do
  #replace the running logger device with our own.
  self.logger.instance_variable_set(:@device,TeeLogWriter.new)
end
<commit_msg>add redaction to TeeLogWriter<commit_after>class TeeLogWriter

  def self.redact(secure_message)
    self.redactions = (self.redactions + [secure_message].flatten).uniq
    secure_message
  end

  def self.redaction_replacement
    @redaction_replacement ||= '######'
  end

  def self.redaction_replacement=(replacement)
    @redaction_replacement = replacement
  end

  def self.redacted(message)
    with_ensured_encoding(message) do |message|
      redactions.inject(message) do |message,redaction|
        message.gsub(redaction,redaction_replacement)
      end
    end
  end

  def with_redactions(message)
    yield self.class.redacted(message)
  end

  def puts(message)
    with_redactions(message) do |message|
      STDOUT.puts message
      file.puts "[#{log_timestamp}] #{message}"
    end
  end

  private

  def file
    @file ||= File.open(File.join(logdir,"deploy.#{file_timestamp}.log"), "w")
  end

  def self.redactions
    @redactions ||= []
  end

  def self.redactions=(value)
    @redactions = value
  end

  def self.with_ensured_encoding(message)
    yield message.respond_to?(:force_encoding) ? message.force_encoding("UTF-8") : message
  end

  def log_timestamp
    Time.now.strftime("%Y-%m-%d %H:%M:%S%z")
  end

  def file_timestamp
    Time.now.strftime("%Y%m%d-%H%M%S%z")
  end

  def caproot
    @caproot ||= File.dirname(capfile)
  end

  def logdir
    FileUtils.mkdir_p(File.join(caproot,'log')).first
  end

  def capfile
    previous = nil
    current  = File.expand_path(Dir.pwd)

    until !File.directory?(current) || current == previous
      filename = File.join(current, 'Capfile')
      return filename if File.file?(filename)
      current, previous = File.expand_path("..", current), current
    end
  end

end

require 'capistrano/configuration'

module Capistrano
  class CLI
    module Execute
      def handle_error(error) #:nodoc:
        # redact the error even in the case of an abort
        error = error.class.new(TeeLogWriter.redacted(error.message))
        case error
        when Net::SSH::AuthenticationFailed
          abort "authentication failed for `#{error.message}'"
        when Capistrano::Error
          abort(error.message)
        else raise error
        end
      end
    end
  end
end


Capistrano::Configuration.instance(true).load do
  #replace the running logger device with our own.
  self.logger.instance_variable_set(:@device,TeeLogWriter.new)
end

{'fraction_non_alphanumeric': 0.11439114391143912, 'fraction_numerical': 0.0005676979846721544, 'mean_word_length': 3.4215809284818066, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2263671', 'n_tokens_mistral': 1158, 'n_tokens_neox': 1129, 'n_words': 256}