Threads and databases point me in the direction

I have a function that calls SNMP getbulk until it's out of rows for the column, currently called from main using crossbeam scope and spawn. Works well, but now instead of just printing or logging the data, I want to get it into a database. Which would should I do? Should I try and put items in the database from each thread spawned? Should I try to accumulate all results is some shared mutable data structure and return them to be processed? Or should I use some sort of message queue to send results to be put in the database? I've included the current relevant code just in case.

crossbeam::scope(|scope| {
        for agent in agents {
            scope.spawn(move |_| {
                let mut collected_oids: Vec<Vec<u32>> = oids.iter().map(|&s| s.to_owned()).collect();
                let mut not_in_column: Vec<bool>  = vec![true, true, true, true, true];
                let mut sess = SyncSession::new(
                    agent, community, Some(timeout), 0).unwrap();
                let mut end_of_column = 0;
                while end_of_column < cols {
                    let mut oids_for_getbulk: Vec<&[u32]> = collected_oids.iter().map(|s| &s[..]).collect();
                    let response = sess.getbulk(
                        non_repeat, max_repetitions).unwrap();
                    let mut col = 0_usize;
                    for (name, val) in response.varbinds {
                        if col == cols {
                            col = 0;
                            row += 1;
                        println!("{}. {} {} => {:?}", row, agent, name, val);
                        /// Find the last table row and add those each column's
        /// objectidentifiers to the new oids

                        let oid: String = oids[col].iter().map(|x| x.to_string() + ".").collect();
                        /// TODO needs to be logged, not printed
                        /// Keep track of fails. Remember, if snmp querying two
                        /// adjacent columns, at some point the next column
                        /// will be queried but should fail as you've already
                        /// queried it.
                        if !name.to_string().starts_with(&oid) {
                            println!("Failed: {} => {}", oid, name);
                        if name.to_string().starts_with(&oid) {
                            let next_getbulk = name.to_string()
                                .map(|c| c.parse::<u32>().unwrap())
                            collected_oids[col] = next_getbulk;
                            if col == cols - 1 {
                                row += 1;
                        } else {
                            end_of_column += 1;
                            println!("End of column: {} => cols: {}", end_of_column, cols);
                            if !end_of_column < cols {
                        col += 1;

Bonus questions (I come from a Python background): Is there a logging crate for Rust like Python's logging. What do Rustaceans do for mocking in tests?