def associate_logfiles( log_dir, record_changes = false )
      raise "Directory does not exist: #{log_dir}" unless File.directory?( log_dir )

      # scans for logfiles prefixed with a month stamp like "2016.03_"
      ret_h = {}
      search_path = record_changes ? File.join(log_dir, '**', 'record_changes', '*.log') : File.join(log_dir, '**', '20*_*.log')
      Dir.glob(search_path) do |log_fp|
        next if !record_changes && (log_fp.include?('record_changes') || log_fp.include?('import')) # TODO: include import
        month_stamp = File.basename(log_fp).split('_').first
        next unless month_stamp.length == 7 && month_stamp[4] == '.'
        begin
          DateTime.strptime(month_stamp, '%Y.%m')
        rescue ArgumentError
          next
        end
        log_class = File.basename(log_fp, File.extname(log_fp)).split('_')[1..-1].join('_')
        ret_h[log_class] ||= []
        ret_h[log_class] << log_fp
      end
      ret_h
    end
def associate_logfiles( log_dir, record_changes = false )
      raise "Directory does not exist: #{log_dir}" unless File.directory?( log_dir )

      # scans for logfiles prefixed with a month stamp like "2016.03_"
      ret_h = {}
      search_path = record_changes ? File.join(log_dir, '**', 'record_changes', '*.log') : File.join(log_dir, '**', '20*_*.log')
      Dir.glob(search_path) do |log_fp|
        next if !record_changes && (log_fp.include?('record_changes') || log_fp.include?('import')) # TODO: include import
        month_stamp = File.basename(log_fp).split('_').first
        next unless month_stamp.length == 7 && month_stamp[4] == '.'
        begin
          DateTime.strptime(month_stamp, '%Y.%m')
        rescue ArgumentError
          next
        end
        log_class = File.basename(log_fp, File.extname(log_fp)).split('_')[1..-1].join('_')
        ret_h[log_class] ||= []
        ret_h[log_class] << log_fp
      end
      ret_h
    end
Returns a hash whose keys are the common log names and whose values are arrays of file paths, e.g.:
 {
                     "ajax_errors" => [
       [ 0] "/Users/seanhuber/Downloads/record_changes/2015.08_ajax_errors.log",
       [ 1] "/Users/seanhuber/Downloads/record_changes/2015.09_ajax_errors.log",
       [ 2] "/Users/seanhuber/Downloads/record_changes/2015.10_ajax_errors.log"
   ],
               "care_plan_updates" => [
       [ 0] "/Users/seanhuber/Downloads/record_changes/2015.08_care_plan_updates.log",
       [ 1] "/Users/seanhuber/Downloads/record_changes/2015.09_care_plan_updates.log",
       [ 2] "/Users/seanhuber/Downloads/record_changes/2015.10_care_plan_updates.log"
   ],
                    "eval_updates" => [
       [ 0] "/Users/seanhuber/Downloads/record_changes/2015.08_eval_updates.log",
       [ 1] "/Users/seanhuber/Downloads/record_changes/2015.09_eval_updates.log",
       [ 2] "/Users/seanhuber/Downloads/record_changes/2015.10_eval_updates.log"
   ],
 }