User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cpp:qlogger [2019-05-28 15:26]
cpp:qlogger [2020-02-15 00:57] (current)
Line 1: Line 1:
  
 +=====Thread-queued logger=====
 +
 +The queue is unlimited.
 +
 +<code c++ logger.cpp>​
 +/*
 + *
 + * Copyright 2004-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;
 +}
 +</​code>​
 +
 +<​file>​
 +$ 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
 +$
 +</​file>​
 +
 +----
 +[<>]