1+ #include " file_logger.h"
2+ #include < algorithm>
3+ #include < iostream>
4+ #include < sstream>
5+
6+ #ifdef _WIN32
7+ #include < io.h>
8+ #define ftruncate _chsize
9+ #else
10+ #include < unistd.h>
11+ #endif
12+ #include < string.h>
13+
14+ using namespace trantor ;
15+
16+ FileLogger::FileLogger () : AsyncFileLogger() {}
17+
18+ FileLogger::~FileLogger () = default ;
19+
20+ void FileLogger::output_ (const char * msg, const uint64_t len) {
21+ if (!circular_log_file_ptr_) {
22+ circular_log_file_ptr_ =
23+ std::make_unique<CircularLogFile>(fileBaseName_, max_lines_);
24+ }
25+ circular_log_file_ptr_->writeLog (msg, len);
26+ }
27+
28+ FileLogger::CircularLogFile::CircularLogFile (const std::string& fileName,
29+ uint64_t maxLines)
30+ : max_lines_(maxLines), file_name_(fileName) {
31+ std::lock_guard<std::mutex> lock (mutex_);
32+ OpenFile ();
33+ LoadExistingLines ();
34+ TruncateFileIfNeeded ();
35+ }
36+
37+ FileLogger::CircularLogFile::~CircularLogFile () {
38+ std::lock_guard<std::mutex> lock (mutex_);
39+ CloseFile ();
40+ }
41+ void FileLogger::CircularLogFile::writeLog (const char * logLine,
42+ const uint64_t len) {
43+ std::lock_guard<std::mutex> lock (mutex_);
44+ if (!fp_)
45+ return ;
46+
47+ std::string logString (logLine, len);
48+ std::istringstream iss (logString);
49+ std::string line;
50+ while (std::getline (iss, line)) {
51+ if (lineBuffer_.size () >= max_lines_) {
52+ lineBuffer_.pop_front ();
53+ }
54+ lineBuffer_.push_back (line);
55+ AppendToFile (line + " \n " );
56+ ++linesWrittenSinceLastTruncate_;
57+ if (linesWrittenSinceLastTruncate_.load () >= TRUNCATE_CHECK_INTERVAL) {
58+
59+ TruncateFileIfNeeded ();
60+ }
61+ }
62+ }
63+ void FileLogger::CircularLogFile::flush () {
64+ std::lock_guard<std::mutex> lock (mutex_);
65+ if (fp_) {
66+ fflush (fp_);
67+ }
68+ }
69+
70+ void FileLogger::CircularLogFile::TruncateFileIfNeeded () {
71+ // std::cout<<"Truncating file "<< totalLines_ <<std::endl;
72+ if (!fp_ || lineBuffer_.size () < max_lines_)
73+ return ;
74+
75+ // Close the current file
76+ fclose (fp_);
77+ fp_ = nullptr ;
78+
79+ // Open a temporary file for writing
80+ std::string tempFileName = file_name_ + " .temp" ;
81+ FILE* tempFile = fopen (tempFileName.c_str (), " w" );
82+ if (!tempFile) {
83+
84+ std::cout << " Error opening temporary file for truncation: "
85+ << strerror (errno) << std::endl;
86+ OpenFile (); // Reopen the original file
87+ return ;
88+ }
89+
90+ // Write only the last max_lines_ lines to the temporary file
91+ size_t startIndex =
92+ lineBuffer_.size () > max_lines_ ? lineBuffer_.size () - max_lines_ : 0 ;
93+
94+ for (size_t i = startIndex; i < lineBuffer_.size (); ++i) {
95+ fprintf (tempFile, " %s\n " , lineBuffer_[i].c_str ());
96+ }
97+
98+ fclose (tempFile);
99+
100+ // Replace the original file with the temporary file
101+ if (std::rename (tempFileName.c_str (), file_name_.c_str ()) != 0 ) {
102+ std::cout << " Error replacing original file with truncated file: "
103+ << strerror (errno) << std::endl;
104+ std::remove (tempFileName.c_str ()); // Clean up the temporary file
105+ }
106+ // else {
107+ // totalLines_.store(lineBuffer_.size() > max_lines_ ? max_lines_
108+ // : lineBuffer_.size());
109+ // }
110+
111+ // Reopen the file
112+ OpenFile ();
113+ // LoadExistingLines();
114+ linesWrittenSinceLastTruncate_.store (0 );
115+ }
116+
117+ void FileLogger::CircularLogFile::OpenFile () {
118+ #ifdef _WIN32
119+ auto wFileName = utils::toNativePath (file_name_);
120+ fp_ = _wfopen (wFileName.c_str (), L" r+" );
121+ #else
122+ fp_ = fopen (file_name_.c_str (), " r+" );
123+ #endif
124+
125+ if (!fp_) {
126+ // If file doesn't exist, create it
127+ #ifdef _WIN32
128+ fp_ = _wfopen (wFileName.c_str (), L" w+" );
129+ #else
130+ fp_ = fopen (file_name_.c_str (), " w+" );
131+ #endif
132+
133+ if (!fp_) {
134+ std::cerr << " Error opening file: " << strerror (errno) << std::endl;
135+ }
136+ }
137+ }
138+ void FileLogger::CircularLogFile::LoadExistingLines () {
139+ if (!fp_)
140+ return ;
141+
142+ // Move to the beginning of the file
143+ fseek (fp_, 0 , SEEK_SET);
144+
145+ lineBuffer_.clear ();
146+
147+ std::string line;
148+ char buffer[4096 ];
149+ while (fgets (buffer, sizeof (buffer), fp_) != nullptr ) {
150+ line = buffer;
151+ if (!line.empty () && line.back () == ' \n ' ) {
152+ line.pop_back (); // Remove trailing newline
153+ }
154+ if (lineBuffer_.size () >= max_lines_) {
155+ lineBuffer_.pop_front ();
156+ }
157+ lineBuffer_.push_back (line);
158+ }
159+
160+ // Move back to the end of the file for appending
161+ fseek (fp_, 0 , SEEK_END);
162+ }
163+ void FileLogger::CircularLogFile::AppendToFile (const std::string& line) {
164+ if (fp_) {
165+ fwrite (line.c_str (), 1 , line.length (), fp_);
166+ fflush (fp_);
167+ }
168+ }
169+
170+ void FileLogger::CircularLogFile::CloseFile () {
171+ if (fp_) {
172+ fclose (fp_);
173+ fp_ = nullptr ;
174+ }
175+ }
0 commit comments