Thanks to the tip from @Alexander Petrov from the comments to the question, this is the result that personally suits me personally in terms of functionality. Below I will give an example, suddenly someone will need something similar.
Description: The logger class was created to output any information from different classes used in the project to different files. Therefore, several files are opened (for convenience, a date is still written before the file name). The class instance is spinning in a separate thread.
File "Logger.h" :
#pragma once #include "stdafx.h" extern std::string PathEXE; // где хранится файл class Logger { public: Logger(){ std::cout << "\nLog class created. "+PathEXE; OpenLogFiles(); }// открывает файлы. void AddToLog(unsigned type, std::string text); // функция, вызываемая извне, для добавления в буффер, соответствующий желаемому файлу void Run();// функция, проверяющая постоянно буфферы на наличие данных private: //method void OpenLogFiles(); void output(std::string text, int i);// функция, печатающая в файл std::string pop(int i, int j);// функция, выбирающая что печатать //data std::vector<std::vector<std::string>> buffer; // вектор буфферов. Размер - кол-во файлов std::vector<HANDLE> file; // хэндлеры файлов std::vector<DWORD> filesize; // позиции в файлах std::mutex mutex_; //мютекс };
File "Logger.cpp" :
#include "stdafx.h" #include "Logger.h" void Logger::OpenLogFiles() { using namespace boost; gregorian::date TODAY = gregorian::day_clock::local_day(); std::string date = to_string(TODAY.day().as_number()) + "-" + to_string(TODAY.month().as_number()) + "-" + to_string(TODAY.year()) + "_"; std::string tempPath = PathEXE + date; std::vector<std::string> path; path.push_back(tempPath + "LOGS_1.txt"); path.push_back(tempPath + "LOGS_2.txt"); path.push_back(tempPath + "LOGS_3.txt"); path.push_back(tempPath + "LOGS_4.txt"); path.push_back(tempPath + "LOGS_5.txt"); path.push_back(tempPath + "LOGS_6.txt"); path.push_back(tempPath + "LOGS_0.txt"); for (unsigned i = 0; i < path.size(); ++i) { file.push_back(CreateFileA( path[i].c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) ); // открываем файл с флагом shared_reading if (file[i] == INVALID_HANDLE_VALUE) // проверка на правильность { printf("ERROR %x \n", GetLastError()); std::cout << "\nRELOAD NEEDED. LOG ERROR";//messagebox } filesize.push_back(0); // заполняем вектор позиций time_t rawtime; struct tm * t; time(&rawtime); t = localtime(&rawtime); std::string time = ""; std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); time += to_string(t->tm_year + 1900) + "." + to_string(t->tm_mon + 1) + "." + to_string(t->tm_mday) + "/" + to_string(t->tm_hour) + ":" + to_string(t->tm_min) + ":" + to_string(t->tm_sec) + "/" + to_string(t1.time_since_epoch().count() % 1000); output("Logs created on: "+time, i);// сразу пишем в файл, когда создан std::vector<std::string> buf;//создаем буффер buffer.push_back(buf); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void Logger::Run() { while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(1));//для уменьшения загрузки ЦП for (unsigned i = 0; i < buffer.size(); ++i) { int size = buffer[i].size(); if (size>0) { for (int k = 0; k < size; ++k) { output(pop(i, k), i); } } } } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void Logger::output(std::string text, int i) { WriteFile(file[i], (text).c_str(), (text).size(), &filesize[i], NULL);// пишем в файл filesize[i] += (text).size();// сдвигаем указатель } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// std::string Logger::pop(int i, int j) { std::string text; text = buffer[i][j]; // читаем начало буффера mutex_.lock(); buffer[i].erase(buffer[i].begin());// elfkztv yfxfkj ,eaathf mutex_.unlock(); text += " s=" + boost::to_string(buffer[i].size()); // добавляем информацию, сколько еще в буффере (у меня максимальный размер был 1 через час работы) return text; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void Logger::AddToLog(unsigned type, std::string text) { if (buffer.size() < type){ std::cout << "size!"; return; } // проверка на ошибку размера mutex_.lock(); buffer[type].push_back(text);// добавляем в конец буффера mutex_.unlock(); }
File "main.cpp" :
#include "stdafx.h" #include "Logger.h" Logger; std::string PathEXE = "C:\\Projects\\tests\\Release\\"; // задаем путь для файлов auto logqqq=Logger();// создаем экземпляр класса void Runner() { logqqq.Run(); } void helper(std::string text, int i, int timee) { while (true) { logqqq.AddToLog(i, "\n|text here|\n"); std::this_thread::sleep_for(std::chrono::milliseconds(timee)); } } int main() { std::cout << "\nstart"; std::thread my_thread(&Runner);// запускаем логгер std::this_thread::sleep_for(std::chrono::milliseconds(100));// ждем просто так //запуски потоков, кидающих что-то в буффер: //параметр 1 - текст(сейчас не активен), параметр 2 - файл, параметр 3 - частота "вброса" в миллисекундах. std::thread my1(&helper, "\ntext1", 0, 1); std::thread my2(&helper, "\ntext2", 1, 2); std::thread my3(&helper, "\ntext3", 2, 3); std::thread my4(&helper, "\ntext4", 3, 4); std::thread my5(&helper, "\ntext5", 4, 5); std::thread my6(&helper, "\ntext6", 5, 6); std::thread my7(&helper, "\ntext7", 6, 7); cout << "\nall threads started!"; my1.join(); return 0; }
PS Maybe not as optimally as possible, but I did not notice any problems with the speed of work. Yes, and in the amount of memory spent, too.