11#include " logging.hpp"
22#include < array>
3+ #include < cerrno>
34#include < cstdio>
45
6+ #include < fcntl.h>
57#include < qbytearrayview.h>
8+ #include < qcoreapplication.h>
69#include < qdatetime.h>
710#include < qendian.h>
11+ #include < qfilesystemwatcher.h>
812#include < qhash.h>
913#include < qhashfunctions.h>
1014#include < qlist.h>
@@ -356,6 +360,18 @@ void ThreadLogging::initFs() {
356360 delete detailedFile;
357361 detailedFile = nullptr ;
358362 } else {
363+ auto lock = flock {
364+ .l_type = F_WRLCK,
365+ .l_whence = SEEK_SET,
366+ .l_start = 0 ,
367+ .l_len = 0 ,
368+ };
369+
370+ if (fcntl (detailedFile->handle (), F_SETLK, &lock) != 0 ) { // NOLINT
371+ qCWarning (logLogging) << " Unable to set lock marker on detailed log file. --follow from "
372+ " other instances will not work." ;
373+ }
374+
359375 qCInfo (logLogging) << " Saving detailed logs to" << path;
360376 }
361377
@@ -737,22 +753,13 @@ bool EncodedLogReader::registerCategory() {
737753 return true ;
738754}
739755
740- bool readEncodedLogs (QIODevice* device, bool timestamps, int tail, const QString& rulespec) {
741- QList<QLoggingRule> rules;
742-
743- {
744- QLoggingSettingsParser parser;
745- parser.setContent (rulespec);
746- rules = parser.rules ();
747- }
748-
749- auto reader = EncodedLogReader ();
750- reader.setDevice (device);
756+ bool LogReader::initialize () {
757+ this ->reader .setDevice (this ->file );
751758
752759 bool readable = false ;
753760 quint8 logVersion = 0 ;
754761 quint8 readerVersion = 0 ;
755- if (!reader.readHeader (&readable, &logVersion, &readerVersion)) {
762+ if (!this -> reader .readHeader (&readable, &logVersion, &readerVersion)) {
756763 qCritical () << " Failed to read log header." ;
757764 return false ;
758765 }
@@ -765,49 +772,131 @@ bool readEncodedLogs(QIODevice* device, bool timestamps, int tail, const QString
765772 return false ;
766773 }
767774
768- auto color = LogManager::instance ()->colorLogs ;
769-
770- auto filters = QHash<quint16, CategoryFilter>();
775+ return true ;
776+ }
771777
772- auto tailRing = RingBuffer<LogMessage>(tail);
778+ bool LogReader::continueReading () {
779+ auto color = LogManager::instance ()->colorLogs ;
780+ auto tailRing = RingBuffer<LogMessage>(this ->remainingTail );
773781
774782 LogMessage message;
775783 auto stream = QTextStream (stdout);
776- while (reader.read (&message)) {
784+ auto readCursor = this ->file ->pos ();
785+ while (this ->reader .read (&message)) {
786+ readCursor = this ->file ->pos ();
787+
777788 CategoryFilter filter;
778- if (filters.contains (message.readCategoryId )) {
779- filter = filters.value (message.readCategoryId );
789+ if (this -> filters .contains (message.readCategoryId )) {
790+ filter = this -> filters .value (message.readCategoryId );
780791 } else {
781- for (const auto & rule: rules) {
792+ for (const auto & rule: this -> rules ) {
782793 filter.applyRule (message.category , rule);
783794 }
784795
785- filters.insert (message.readCategoryId , filter);
796+ this -> filters .insert (message.readCategoryId , filter);
786797 }
787798
788799 if (filter.shouldDisplay (message.type )) {
789- if (tail == 0 ) {
790- LogMessage::formatMessage (stream, message, color, timestamps);
800+ if (this -> remainingTail == 0 ) {
801+ LogMessage::formatMessage (stream, message, color, this -> timestamps );
791802 stream << ' \n ' ;
792803 } else {
793804 tailRing.emplace (message);
794805 }
795806 }
796807 }
797808
798- if (tail != 0 ) {
809+ if (this -> remainingTail != 0 ) {
799810 for (auto i = tailRing.size () - 1 ; i != -1 ; i--) {
800811 auto & message = tailRing.at (i);
801- LogMessage::formatMessage (stream, message, color, timestamps);
812+ LogMessage::formatMessage (stream, message, color, this -> timestamps );
802813 stream << ' \n ' ;
803814 }
804815 }
805816
806817 stream << Qt::flush;
807818
808- if (!device-> atEnd () ) {
819+ if (this -> file -> pos () != readCursor ) {
809820 qCritical () << " An error occurred parsing the end of this log file." ;
810- qCritical () << " Remaining data:" << device->readAll ();
821+ qCritical () << " Remaining data:" << this ->file ->readAll ();
822+ return false ;
823+ }
824+
825+ return true ;
826+ }
827+
828+ void LogFollower::FcntlWaitThread::run () {
829+ auto lock = flock {
830+ .l_type = F_RDLCK, // won't block other read locks when we take it
831+ .l_whence = SEEK_SET,
832+ .l_start = 0 ,
833+ .l_len = 0 ,
834+ };
835+
836+ auto r = fcntl (this ->follower ->reader ->file ->handle (), F_SETLKW, &lock); // NOLINT
837+
838+ if (r != 0 ) {
839+ qCWarning (logLogging).nospace ()
840+ << " Failed to wait for write locks to be removed from log file with error code " << errno
841+ << " : " << qt_error_string ();
842+ }
843+ }
844+
845+ bool LogFollower::follow () {
846+ QObject::connect (&this ->waitThread , &QThread::finished, this , &LogFollower::onFileLocked);
847+
848+ QObject::connect (
849+ &this ->fileWatcher ,
850+ &QFileSystemWatcher::fileChanged,
851+ this ,
852+ &LogFollower::onFileChanged
853+ );
854+
855+ this ->fileWatcher .addPath (this ->path );
856+ this ->waitThread .start ();
857+
858+ auto r = QCoreApplication::exec ();
859+ return r == 0 ;
860+ }
861+
862+ void LogFollower::onFileChanged () {
863+ if (!this ->reader ->continueReading ()) {
864+ QCoreApplication::exit (1 );
865+ }
866+ }
867+
868+ void LogFollower::onFileLocked () {
869+ if (!this ->reader ->continueReading ()) {
870+ QCoreApplication::exit (1 );
871+ } else {
872+ QCoreApplication::exit (0 );
873+ }
874+ }
875+
876+ bool readEncodedLogs (
877+ QFile* file,
878+ const QString& path,
879+ bool timestamps,
880+ int tail,
881+ bool follow,
882+ const QString& rulespec
883+ ) {
884+ QList<QLoggingRule> rules;
885+
886+ {
887+ QLoggingSettingsParser parser;
888+ parser.setContent (rulespec);
889+ rules = parser.rules ();
890+ }
891+
892+ auto reader = LogReader (file, timestamps, tail, rules);
893+
894+ if (!reader.initialize ()) return false ;
895+ if (!reader.continueReading ()) return false ;
896+
897+ if (follow) {
898+ auto follower = LogFollower (&reader, path);
899+ return follower.follow ();
811900 }
812901
813902 return true ;
0 commit comments