gabime / spdlog

Fast C++ logging library.
Other
23.84k stars 4.47k forks source link

Core dump when outputting log into QTextEdit in qt5 #2054

Closed WorstCodeWay closed 3 years ago

WorstCodeWay commented 3 years ago

I want to implement a log viewer in qt with spdlog. But the output log in QTextEdit is messed up in format while is well-formed in a log file. My codes are:

MainWindow.h

#include <QMainWindow>
#include <QTextEdit>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include "log.h"

template<typename Mutex>
  class qtextedit_sink : public spdlog::sinks::base_sink<Mutex>
  {
  public:
    explicit qtextedit_sink(QSharedPointer<QTextEdit> qtTextEdit) : _qtTextEdit(qtTextEdit) {}
    qtextedit_sink(const qtextedit_sink &) = delete;
    qtextedit_sink &operator=(const qtextedit_sink &) = delete;

  protected:
    void sink_it_(const spdlog::details::log_msg &msg) override
    {
      spdlog::memory_buf_t formatted;
      spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);

      if (_qtTextEdit)
      {
        std::string tempMsg = fmt::to_string(formatted);

        _qtTextEdit->insertPlainText(fmt::to_string(formatted).c_str());
      }
    }

    void flush_() override {}

  private:
    QSharedPointer<QTextEdit>_qtTextEdit{ nullptr };
  };

  using qtextedit_sink_mt = qtextedit_sink<std::mutex>;
  using qtextedit_sink_st = qtextedit_sink<spdlog::details::null_mutex>;

  typedef std::shared_ptr<spdlog::sinks::daily_file_sink_mt> DailyFileSinkMtPtr;
  typedef std::shared_ptr<qtextedit_sink_mt> QTextEditSinkMtPtr;
  typedef std::shared_ptr<spdlog::logger> LoggerPtr;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
  Q_OBJECT

public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();

private:
  Ui::MainWindow *ui;
  QSharedPointer<QTextEdit> txt;
  std::shared_ptr<spdlog::logger> projectLogger;
  std::shared_ptr<spdlog::sinks::daily_file_sink_mt> projectFileSink;
  std::shared_ptr<vistrack::qtextedit_sink_mt> projectWidgetSink;
};

MainWindow.cpp

MainWindow::MainWindow(QWidget *parent)
  : QMainWindow(parent)
  , ui(new Ui::MainWindow)
{
  ui->setupUi(this);

  spdlog::sinks_init_list sinks;
  projectLogger = std::make_shared<spdlog::logger>("Project",  sinks);

  projectFileSink = std::make_shared<spdlog::sinks::daily_file_sink_mt>(
    "./logs", 23, 59);
    projectLogger->sinks().push_back(projectFileSink);

  txt = QSharedPointer<QTextEdit>(ui->textEdit);
  projectWidgetSink = std::make_shared<qtextedit_sink_mt>(txt);
  projectLogger->sinks().push_back(projectWidgetSink);

  projectLogger->set_pattern("[%L] [%n] [%Y/%m/%d %H:%M:%S:%e] %v");
  projectLogger->flush_on(spdlog::level::info);

  std::thread([&](){
    int i = 0;
    while(true)
    {
      i++;
      projectLogger->info("This is information {}", i);
      projectLogger->warn("This is warning {}", i);
      projectLogger->error("This is error {}", i);
      usleep(1000000);
    }
  }).detach();

  std::cout << "infromation output " << std::endl;
}

MainWindow::~MainWindow()
{
  delete ui;
}

Here is the core dump stack:

1   QFontEngineFT::shouldUseDesignMetrics        qfontengine_ft.cpp      1595 0x7ffff08ee451 
2   QFontEngineFT::recalcAdvances                qfontengine_ft.cpp      1634 0x7ffff08ee451 
3   _hb_qt_font_get_glyph_h_advance              qharfbuzzng.cpp         517  0x7ffff6f281c9 
4   hb_font_t::get_glyph_h_advance               hb-font-private.hh      221  0x7ffff72121d9 
5   hb_ot_position_default                       hb-ot-shape.cc          671  0x7ffff72121d9 
6   hb_ot_position                               hb-ot-shape.cc          769  0x7ffff72121d9 
7   hb_ot_shape_internal                         hb-ot-shape.cc          855  0x7ffff72121d9 
8   _hb_ot_shape                                 hb-ot-shape.cc          882  0x7ffff72121d9 
9   hb_shape_plan_execute                        hb-shaper-list.hh       43   0x7ffff71f517a 
10  hb_shape_full                                hb-shape.cc             132  0x7ffff71f4adc 
11  QTextEngine::shapeTextWithHarfbuzzNG         qtextengine.cpp         1706 0x7ffff6e8ccd3 
12  QTextEngine::shapeText                       qtextengine.cpp         1545 0x7ffff6e8f83d 
13  QTextEngine::shape                           qtextengine.cpp         2028 0x7ffff6e906e9 
14  QTextEngine::shapeLine                       qtextengine.cpp         1342 0x7ffff6e95cf3 
15  QTextLineItemIterator::QTextLineItemIterator qtextengine.cpp         4052 0x7ffff6e982ea 
16  QTextLine::draw                              qtextlayout.cpp         2539 0x7ffff6e9c0f0 
17  QTextLayout::draw                            qtextlayout.cpp         1262 0x7ffff6e9da2d 
18  QTextDocumentLayoutPrivate::drawBlock        qtextdocumentlayout.cpp 2090 0x7ffff6ee1e52 
19  QTextDocumentLayoutPrivate::drawFlow         qtextdocumentlayout.cpp 1988 0x7ffff6ee3dcd 
20  QTextDocumentLayoutPrivate::drawFrame        qtextdocumentlayout.cpp 1220 0x7ffff6ee3755 
... <More>                                                                                   
tt4g commented 3 years ago

Qt Widget is not guaranteed to work when operated from other than the GUI thread (usually the main thread). Logging from a different thread will crash it.

A workaround for the problem is provided by using the Qt event thread. See: https://doc.qt.io/qt-6/threads-synchronizing.html

Since spdlog::sinks::qt_sink has been added to spdlog (#2018), why don't you try using it?


By the way. If I remember correctly, the destructor of the parent Widget will also delete the child Widget, depending on the Qt design. There is a bug in QSharedPtr and delete ui; which does a double delete.

WorstCodeWay commented 3 years ago

@tt4g Thanks for quick reply. I will look into and try spdlog::sinks::qt_sink, but I wonder if this qt_sink can support colorful message?

And I will read thread you introduced. I think it will help a lot.

"If I remember correctly, the destructor of the parent Widget will also delete the child Widget, depending on the Qt design. There is a bug in QSharedPtr and delete ui; which does a double delete."

This is a problem indeed.

I will be back soon after I have some progress.

And you make such great a work!

tt4g commented 3 years ago

Change the color of QTextEdit when the log is received by Qt slot function. You should be able to add the log level as an argument with reference to 'spdlog::sinks::qt_sink'.

https://github.com/gabime/spdlog/blob/eb3220622e73a4889eee355ffa37972b3cac3df5/include/spdlog/sinks/qt_sinks.h#L34-L40

WorstCodeWay commented 3 years ago

That qt_sinks solved my problem. Learnt a lot! Thank you @tt4g.