User Tools

Site Tools


Thread-queued logger

The queue is unlimited.

logger.cpp
/*
 *
 * Copyright 2019 Oleg Borodin  <borodin@unix7.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 */
 
#include <iostream>
#include <fstream>
#include <sstream>
#include <functional>
#include <condition_variable>
#include <queue>
#include <thread>
#include <mutex>
#include <chrono>
#include <memory>
#include <stdexcept>
#include <iomanip>
 
 
class logger {
    private:
        class message {
            public:
                std::string content;
                message(std::string msg) : content(msg) {}
        };
        std::queue<message> queue;
        std::condition_variable cv;
        std::mutex mutex;
        std::vector<std::shared_ptr<std::thread>> threads;
        std::string path;
        std::shared_ptr<std::ofstream> file;
        bool shutdown = false;
 
        std::string timestamp() {
            std::time_t now = std::time(0);
            std::stringstream ss;
            ss << std::put_time(std::gmtime(&now), "%Y-%m-%d %T %Z");
            return ss.str();
        }
    public:
        logger(std::string path, int num_threads = 1) : path(path) {
            using mode = std::fstream;
            file = std::make_shared<std::ofstream>(
                path, mode::out | mode::app
            );
            if (!file->is_open()) {
                throw std::runtime_error("cannot open log file");
            }
            auto worker = [&]{
                while(true) {
                    std::unique_lock<std::mutex> lock(mutex);
                    cv.wait(lock, [&]{ return !queue.empty() || shutdown; });
                    if (shutdown) { file->close(); return; }
                    while(!queue.empty()) {
                        auto msg = queue.front();
                        queue.pop();
                        cv.notify_one();
                        *file << msg.content << std::endl;
                        std::cerr << msg.content << std::endl;
                    }
                }
            };
 
            for (std::size_t i = 0; i < num_threads; ++i) {
                auto t = new std::thread(std::move(worker));
                std::shared_ptr<std::thread> thread(std::move(t));
                threads.push_back(thread);
            }
            for (std::size_t i = 0; i < threads.size(); ++i) {
                threads[i]->detach();
            }
        }
 
        void stop() {
            shutdown = true;
            cv.notify_all();
        }
 
        void log(std::string msg) {
            message m(timestamp() + " " + msg);
            auto func = [&]{
                std::unique_lock<std::mutex> lock(mutex);
                queue.push(m);
            };
            if (!shutdown) {
                func();
            }
            cv.notify_all();
        }
};
 
 
int main(int argc, char **argv) {
 
    try {
        logger l("./std.log");
        l.log("init hello");
 
        int i = 0;
        while(true) {
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            l.log("hello");
            if (i > 10) { l.stop(); break; }
            i++;
        }
    } catch (std::exception& e) {
        std::cerr << "#exception: " << e.what() << ", exit." << std::endl;
        return 1;
    }
    return 0;
}
$ c++ -O -pthread -o logger logger.cpp
$ ./logger
2019-04-25 08:17:10 UTC init hello
2019-04-25 08:17:11 UTC hello
2019-04-25 08:17:12 UTC hello
2019-04-25 08:17:13 UTC hello
2019-04-25 08:17:14 UTC hello
2019-04-25 08:17:15 UTC hello
2019-04-25 08:17:16 UTC hello
2019-04-25 08:17:17 UTC hello
2019-04-25 08:17:18 UTC hello
2019-04-25 08:17:19 UTC hello
2019-04-25 08:17:20 UTC hello
2019-04-25 08:17:21 UTC hello
$

First PagePrevious PageBack to overviewNext PageLast Page