Add Log view in CSV mode

Добавлено окно просмотра лога БД в CSV формате с фильтрами, группами.
Описание в Readme.md
This commit is contained in:
lsv 2021-09-15 20:21:42 +05:00
parent 50f53a96bb
commit 4b240f8cc0
30 changed files with 3155 additions and 14 deletions

View file

@ -164,6 +164,43 @@ This text Russian language.
Размер icon 32х32
2. Задать для сервера цвет. Фон icon будет окрашен в цвет сервера.
19.08.2021
- добавлено окно просмотра CSV лога базы.
Окно вызывается из контекстного меню сервера "Log view ...".
После открытия окна читается непосредственно файл лога функцией pg_read_binary_file
Выбирается файл с самой свежей датой изменения. Проверка новых сообщений проводиться каждые 5 секунд.
Можно добавить другие сервера на панели "Settings". Настройки применяются после закрытия окна и повторного его открытия.
Если окно лога не активное и приходит сообщения уровня Error и выше, то иконка отмечается красным квадратом.
Если на заладке "Settings" выбрано несколько серверов, то происходить автоматическое подключение к ним.
После подключения все открытые сервера в дереве объектов можно закрыть одной командой контекстного меню
"Disconnect all servers".
ВНИМАНИЕ: память требуемая для хранения логов ни чем не ограничивается (кроме фильтрации на этапе загрузки лога) и
возможно выделения большого количества памяти.
Отображаются строки лога в двух режимах:
* Простой. Отобразаются все полученные строки лога
* Групповой. Строки с похожими сообщениями объединяются в группу и видимой строкой является самая последняя строка
в группе. Для просмотра всех строк группы нужно установить флаг "View detail group".
Сообщения будут похожими если они отличаются только числами и если они не в двойных кавычках.
В групповом режиме в поле host показываются счетчик свежих сообщений попавших в группу. Счетчик сбрасывается при
установке курсора на строку группы.
Для исключения из просмотра ненужных строк используются поколоночные фильтры. Для включения фильтра нужно:
* Щелкнуть правой кнопкой мыши по полю. Для инверсии фильтра нужно удерживать Ctrl.
* Выбрать значение в контекстном меню заголовка колонки. Там отображаются 20 самых частых значения в колонке с указанием
количества этих значений.
* Ввести в поле значения для фильтра, выделить это значение и нажать Enter. Для фильтра используется только выделенный
текст. Такой фильтр будет работать на поиск выделенного вхождения в поле. Если в выделенной строке
первым символом будет "!" то фильтер инверсируется.
* каждое отдельное значение фильтра можно удалить через контекстное меню заголовка колонки.
Для более высокой производительности рекомендуется проводить загрузку логов с включенным "Mode group".
Или сбрасывать "Mode group", но при установленных фильтрах.
Отображение большого число строк (более 10000 ) происходит несколько секунд и более.
* Есть возможность отсеять строки на этапе загрузки. Для этого установите фильтры на строки и нажмите
"Add Filter Ignore" этот фильтр будет записан в файл filter_load.txt.
13.09.2021
- Добавлено меню закрытия всех открытых серверов "Disconnect all servers"
-

View file

@ -548,7 +548,8 @@ bool pgConn::HasFeature(int featureNo, bool forceCheck)
wxT(" WHERE proname IN ('pg_tablespace_size', 'pg_file_read', 'pg_logfile_rotate',")
wxT( " 'pg_postmaster_starttime', 'pg_terminate_backend', 'pg_reload_conf',")
wxT( " 'pgstattuple', 'pgstatindex','bt_index_parent_check')\n")
wxT(" AND nspname IN ('pg_catalog', 'public')");
wxT(" AND nspname IN ('pg_catalog', 'public')")
wxT(" union all select current_setting('log_destination'),555,null,null,null");
pgSet *set = ExecuteSet(sql);
@ -579,7 +580,8 @@ bool pgConn::HasFeature(int featureNo, bool forceCheck)
features[FEATURE_PGSTATINDEX] = true;
else if (proname == wxT("bt_index_parent_check") && pronargs == 2 )
features[FEATURE_PGCHECKINDEX] = true;
else if (proname == wxT("csvlog") && pronargs == 555)
features[FEATURE_CSVLOG] = true;
set->MoveNext();
}
delete set;

638
frm/frmLog.cpp Normal file
View file

@ -0,0 +1,638 @@
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/regex.h>
#include <wx/xrc/xmlres.h>
#include <wx/image.h>
#include <wx/textbuf.h>
#include <wx/sysopt.h>
// wxAUI
#include <wx/aui/aui.h>
// App headers
#include "frm/frmMain.h"
#include "frm/frmLog.h"
#include "db/pgConn.h"
#include "utils/pgfeatures.h"
#include "schema/pgServer.h"
#include "schema/pgUser.h"
#include "ctl/ctlMenuToolbar.h"
#include "ctl/ctlAuiNotebook.h"
#include "utils/csvfiles.h"
#include "log/StorageModel.h"
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(RemoteConnArray2);
wxBEGIN_EVENT_TABLE(frmLog, pgFrame)
// test
EVT_CHECKBOX(ID_SET_GROUP, frmLog::OnSetGroup)
EVT_CHECKBOX(ID_SET_DETAILGROUP, frmLog::OnSetDetailGroup)
EVT_BUTTON(ID_CLEAR_ALL_FILTER, frmLog::OnClearAllFilter)
EVT_BUTTON(ID_ADD_FILTER, frmLog::OnAddFilterIgnore)
EVT_SET_FOCUS(frmLog::OnSetFocus)
EVT_KILL_FOCUS(frmLog::OnKillFocus)
EVT_ACTIVATE(frmLog::OnActivate)
wxEND_EVENT_TABLE()
//#include <wx/arrimpl.cpp>
//WX_DEFINE_OBJARRAY(RemoteConnArray);
void frmLog::OnActivate(wxActivateEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
event.Skip();
}
void frmLog::OnSetFocus(wxFocusEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
}
void frmLog::OnKillFocus(wxFocusEvent& event) {
m_storage_model->getStorage()->SetErrMsgFlag(false);
seticon(false);
}
// Class declarations
void frmLog::OnClearAllFilter(wxCommandEvent& event) {
my_view->ClearAllFilter();
}
void frmLog::OnAddFilterIgnore(wxCommandEvent& event) {
my_view->AddFilterIgnore();
}
void frmLog::OnSetGroup(wxCommandEvent& event)
{
//wxDataViewColumn* const col = m_ctrl[Page_List]->GetColumn(0);
if (event.IsChecked())
{
//wxLogMessage("Group set check");
my_view->setGroupMode(true);
detail->SetValue(false);
}
else {
//wxLogMessage("Group unset check");
my_view->setGroupMode(false);
}
detail->Enable(event.IsChecked());
}
bool frmLog::CheckConn(wxString host,int port) {
for (size_t i = 0; i < conArray.GetCount(); i++)
{
if ((conArray[i].conn->GetHostName() == host)&&(conArray[i].conn->GetPort() == port))
return true;
}
return false;
}
pgConn* frmLog::createConn(pgServer* srv) {
pgConn* conn;
if (!srv->GetConnected()) mainForm->ReconnectServer(srv, false);
conn = srv->CreateConn(wxEmptyString, 0, "Log conn");
return conn;
}
void frmLog::AddNewConn(pgConn* con) {
if (con != NULL) {
if (!con->HasFeature(FEATURE_CSVLOG)) return;
logfileName.Add("");
savedPartialLine.Add("");
logfileLength.Add(0);
len.Add(0);
conArray.Add(new RemoteConn2(con));
}
}
void frmLog::OnSetDetailGroup(wxCommandEvent& event)
{
//wxDataViewColumn* const col = m_ctrl[Page_List]->GetColumn(0);
if (event.IsChecked())
{
my_view->ViewGroup(true);
}
else {
my_view->ViewGroup(false);
}
}
void frmLog::getFilename() {
pgSet* set;
wxString namepage;
for (size_t i = 0;i< conArray.GetCount(); i++) {
if (!conArray[i].conn->IsAlive()) continue;
set = conArray[i].conn->ExecuteSet(
wxT("select current_setting('log_directory')||'/'||name filename,modification filetime,size len\n")
wxT(" FROM pg_ls_logdir() where name ~ '.csv' ORDER BY modification DESC"));
if (set)
{
//logfileTimestamp = set->GetDateTime(wxT("filetime"));
len[i] = set->GetLong(wxT("len"));
if (!namepage.IsEmpty()) namepage += ",";
namepage += conArray[i].conn->GetDbname();
m_storage_model->getStorage()->SetHost(conArray[i].conn->GetHostName());
wxString fn = set->GetVal(wxT("filename"));
if (fn != logfileName[i]) {
logfileLength[i] = 0;
logfileName[i] = fn;
}
/// addLogFile(logfileName, logfileTimestamp, len, logfileLength, skipFirst);
delete set;
readLogFile(logfileName[i],len[i],logfileLength[i],savedPartialLine[i],conArray[i].conn);
}
}
if (namepage.IsEmpty()) namepage = "not connect";
if (m_notebook->GetPageText(0) != namepage) m_notebook->SetPageText(0, namepage);
}
void frmLog::readLogFile(wxString logfileName,long& lenfile,long& logfileLength,wxString& savedPartialLine,pgConn* conn) {
wxString line;
// If GPDB 3.3 and later, log is normally in CSV format. Let's get a whole log line before calling addLogLine,
// so we can do things smarter.
// PostgreSQL can log in CSV format, as well as regular format. Normally, we'd only see
// the regular format logs here, because pg_logdir_ls only returns those. But if pg_logdir_ls is
// changed to return the csv format log files, we should handle it.
bool csv_log_format = logfileName.Right(4) == wxT(".csv");
if (csv_log_format && savedPartialLine.length() > 0)
{
if (logfileLength == 0) // Starting at beginning of log file
savedPartialLine.clear();
else
line = savedPartialLine;
}
wxString funcname = "pg_read_binary_file(";
while (lenfile > logfileLength)
{
//statusBar->SetStatusText(_("Reading log from server..."));
pgSet* set = conn->ExecuteSet(wxT("SELECT ") + funcname +
conn->qtDbString(logfileName) + wxT(", ") + NumToStr(logfileLength) + wxT(", 50000)"));
if (!set)
{
conn->IsAlive();
return;
}
char* raw1 = set->GetCharPtr(0);
if (!raw1 || !*raw1)
{
delete set;
break;
}
char* raw;
unsigned char m[50001];
if (settings->GetASUTPstyle()) {
raw = (char*)&m[0];
unsigned char c;
unsigned char* startChar;
int pos = 0;
raw1 = raw1 + 2;
int utf8charLen = 0;
while (*raw1 != 0) {
c = *raw1;
c = c - '0';
if (c > 9) c = *raw1 - 'a' + 10;
raw1++;
m[pos] = c << 4;
c = *raw1 - '0';
if (c > 9) c = *raw1 - 'a' + 10;
c = c | m[pos];
m[pos] = c;
// check utf-8 char
if (utf8charLen == 0) {
startChar = &m[pos];
if (c >> 7 == 0)
utf8charLen = 1;
else if (c >> 5 == 0x6)
utf8charLen = 2;
else if (c >> 4 == 0xE)
utf8charLen = 3;
else if (c >> 5 == 0x1E)
utf8charLen = 4;
else
utf8charLen = 0;
// bad utf8 format
}
pos++;
raw1++;
utf8charLen--;
}
//
if (utf8charLen != 0) {
//read = startChar - &m[0];
// remove bad utf-8 char
*startChar = 0;
}
else
m[pos] = 0;
}
else {
raw = raw1;
}
int l= strlen(raw);
logfileLength += l;
status->SetLabelText(wxString::Format("Load bytes %ld", logfileLength));
wxString str;
str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix);
//if (wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8).Len() > 0)
// str = line + wxString(wxString(raw, wxConvLibc).wx_str(), wxConvUTF8);
//else {
// str = line + wxTextBuffer::Translate(wxString(raw, set->GetConversion()), wxTextFileType_Unix);
//}
delete set;
if (str.Len() == 0)
{
wxString msgstr = _("The server log contains entries in multiple encodings and cannot be displayed by pgAdmin.");
wxMessageBox(msgstr);
return;
}
if (csv_log_format)
{
// This will work for any DB using CSV format logs
CSVLineTokenizer tk(str);
my_view->Freeze();
while (tk.HasMoreLines())
{
line.Clear();
bool partial;
str = tk.GetNextLine(partial);
if (partial)
{
line = str; // Start of a log line, but not complete. Loop back, Read more data.
break;
}
// Looks like we have a good complete CSV log record.
//addLogLine(str.Trim(), true, true);
my_view->AddRow(str.Trim());
}
my_view->Thaw();
}
else
{
}
}
savedPartialLine.clear();
if (!line.IsEmpty())
{
// We finished reading to the end of the log file, but still have some data left
if (csv_log_format)
{
savedPartialLine = line; // Save partial log line for next read of the data file.
line.Clear();
}
else
my_view->AddRow(line.Trim());
}
}
void frmLog::OnTimer(wxTimerEvent& event) {
Storage* st = m_storage_model->getStorage();
//int rows = st->getCountStore();
int ra, ri;
st->ClearRowsStat();
getFilename();
st->GetRowsStat(ra, ri);
//int newrows = st->getCountStore();
//if (loglen !=logfileLength)
if (m_storage_model->getStorage()->GetErrMsgFlag()) {
seticon(true);
}
status->SetLabelText(wxString::Format("Add rows %d ignore %d. View rows %d", ra,ri, m_storage_model->GetRowCount()) );
}
#include "log/log_xpm.xpm"
#include "log/log_red_xpm.xpm"
void frmLog::seticon(bool errflag) {
//wxImage img = *sql_32_png_img;
if (errflag) {
SetIcon(idefRed);
}
else {
SetIcon(idef);
}
return;
wxBitmap* b = new wxBitmap(log_xpm);
//wxIcon ico=img.
wxMemoryDC dc(*b);
dc.SetBrush(*wxYELLOW_BRUSH);
//dc.SetBackground(*wxYELLOW_BRUSH);
dc.SetBackground(*wxRED_BRUSH);
dc.SetBrush(*wxRED_BRUSH);
//dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetTextForeground(*wxRED);
dc.SetPen(*wxRED_PEN);
wxRect rect(7,4,7, 7);
if (errflag) dc.DrawRoundedRectangle(rect, 0);
// wxFont font = wxFont(5, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
// wxFontStyle s;
// font.SetStyle(wxFONTFLAG_ANTIALIASED);
// dc.SetFont(font);
wxImage img = b->ConvertToImage();
dc.SelectObject(wxNullBitmap);
int w = img.GetWidth();
int h = img.GetHeight();
wxColor p;
wxColor c= wxColor(0,255,0);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
p = wxColour(img.GetRed(x, y),
img.GetGreen(x, y),
img.GetBlue(x, y));
//if (img.IsTransparent(x, y)) img.SetRGB(x, y, 255, 255, 255);
if (p == c) {
//img.SetTra
img.SetAlpha(x, y, 255);
img.SetRGB(x, y, img.GetMaskRed(), img.GetMaskGreen(), img.GetMaskBlue());
;
};
}
//wxIcon* ico = new wxIcon();
wxIcon ico=GetIcon();
wxBitmap* bmp = new wxBitmap(img);
ico.CopyFromBitmap(*bmp);
//SetIcon(*sql_32_png_ico);
SetIcon(ico);
}
frmLog::frmLog(frmMain *form, const wxString &_title, pgServer *srv) : pgFrame(NULL, _title)
{
dlgName = wxT("frmLog");
RestorePosition(-1, -1, 700, 500, 700, 500);
//SetIcon(wxIcon(log_xpm));
idef = wxIcon(log_xpm);
idefRed = wxIcon(log_red_xpm);
seticon(false);
mainForm = form;
m_notebook = new wxNotebook( this, wxID_ANY );
wxPanel* testPanel = new wxPanel(m_notebook, wxID_ANY);
//BuildDataViewCtrl(testPanel, Page_Test);
my_view = new MyDataViewCtrl(testPanel, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxDV_VARIABLE_LINE_HEIGHT | wxDV_HORIZ_RULES | wxDV_VERT_RULES);
my_view->GetMainWindow()->Bind(wxEVT_MOTION, &MyDataViewCtrl::OnMouseMove, my_view);
my_view->GetMainWindow()->Bind(wxEVT_KEY_DOWN, &MyDataViewCtrl::OnKEY_DOWN, my_view);
my_view->GetMainWindow()->Bind(wxEVT_KEY_UP, &MyDataViewCtrl::OnKEY_DOWN, my_view);
my_view->Bind(wxEVT_DATAVIEW_COLUMN_HEADER_CLICK, &MyDataViewCtrl::OnEVT_DATAVIEW_COLUMN_HEADER_CLICK, my_view);
my_view->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MyDataViewCtrl::OnEVT_DATAVIEW_SELECTION_CHANGED, my_view);
my_view->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyDataViewCtrl::OnContextMenu, my_view);
// my_view->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyDataViewCtrl::OnContextMenu, my_view);
m_timer.Bind(wxEVT_TIMER, &frmLog::OnTimer, this);
my_view->Bind(wxEVT_MENU, &MyDataViewCtrl::OnEVT_DATAVIEW_CONTEXT_MENU, my_view);
m_storage_model = new StorageModel(my_view);
my_view->AssociateModel(m_storage_model.get());
m_storage_model->BuildColumns(my_view);
wxString s;
s=settings->Read(dlgName + "/ColsWidth","");
if (s.Len()>0) my_view->setSettingString(s);
//settings->Write(dlgName + "/ColsWidth", s);
wxSizer* zeroPanelSz = new wxBoxSizer(wxVERTICAL);
my_view->SetMinSize(wxSize(-1, 200));
zeroPanelSz->Add(my_view, 1, wxGROW | wxALL, 5);
status = new wxStaticText(testPanel, wxID_ANY, "status text");
zeroPanelSz->Add(
status,
0, wxGROW | wxALL, 5);
//zeroPanelSz->Add(button_sizer);
//zeroPanelSz->Add(sizerCurrent);
my_view->setStatusObj(status);
wxBoxSizer* sSizer = new wxBoxSizer(wxHORIZONTAL);
group = new wxCheckBox(testPanel, ID_SET_GROUP, "Mode group");
sSizer->Add(group,
wxSizerFlags().Centre().DoubleBorder());
detail = new wxCheckBox(testPanel, ID_SET_DETAILGROUP, "View detail group");
sSizer->Add(detail,
wxSizerFlags().Centre().DoubleBorder());
const wxSizerFlags border1 = wxSizerFlags().DoubleBorder();
//wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
sSizer->Add(new wxButton(testPanel, ID_CLEAR_ALL_FILTER, "Clear All Filter"), border1);
//sSizer->Add(new wxButton(testPanel, ID_DELETE_SEL, "Delete selected"), border);
sSizer->Add(new wxButton(testPanel, ID_ADD_FILTER, "Add Filter Ignore"), border1);
zeroPanelSz->Add(sSizer);
testPanel->SetSizerAndFit(zeroPanelSz);
m_notebook->AddPage(testPanel, "Log");
wxPanel* settingPanel = new wxPanel(m_notebook, wxID_ANY);
lb = new wxCheckListBox(settingPanel,wxID_ANY);
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
std::vector<wxString> vec;
folderitem = mainForm->GetBrowser()->GetFirstChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (mainForm->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = mainForm->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = mainForm->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
server = (pgServer*)object;
wxString srvname = wxString::Format("%s:%d", server->GetName(), server->GetPort());
vec.push_back(srvname);
}
serveritem = mainForm->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = mainForm->GetBrowser()->GetNextChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
}
lb->Append(vec);
wxString srvs;
wxString srvname = wxString::Format("%s:%d", srv->GetName(), srv->GetPort());
settings->Read(dlgName + "/AutoConnect", &srvs, "");
if (!srvs.IsEmpty()) srvs += ";";
srvs += srvname;
wxStringTokenizer tk(srvs, ";", wxTOKEN_RET_EMPTY_ALL);
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
pgServer* s = getServer(l);
if (s != NULL) {
if (!CheckConn(s->GetName(), s->GetPort())) {
pgConn* conn = createConn(s);
AddNewConn(conn);
for (unsigned int x = 0; x < lb->GetCount(); x++)
if (l==lb->GetString(x)) lb->Check(x, true);
}
}
}
wxSizer* zeroPanelSz2 = new wxBoxSizer(wxVERTICAL);
lb->SetMinSize(wxSize(-1, 200));
zeroPanelSz2->Add(lb, 1, wxGROW | wxALL, 5);
settingPanel->SetSizerAndFit(zeroPanelSz2);
m_notebook->AddPage(settingPanel, "Settings");
bool b=true;
settings->Read(dlgName + "/Mode",&b, false);
group->SetValue(b);
my_view->setGroupMode(b);
// if (mainForm) getFilename();
m_timer.Start(timerInterval);
}
pgServer* frmLog::getServer(wxString& strserver) {
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
folderitem = mainForm->GetBrowser()->GetFirstChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (mainForm->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = mainForm->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = mainForm->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
server = (pgServer*)object;
wxString srvname = wxString::Format("%s:%d", server->GetName(), server->GetPort());
if (srvname == strserver) {
return server;
}
}
serveritem = mainForm->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = mainForm->GetBrowser()->GetNextChild(mainForm->GetBrowser()->GetRootItem(), foldercookie);
}
return 0;
}
frmLog::~frmLog()
{
// If the status window wasn't launched in standalone mode...
if (mainForm)
mainForm->RemoveFrame(this);
// If connection is still available, delete it
SavePosition();
wxString srvs;
for (unsigned int x = 0; x < lb->GetCount(); x++)
if (lb->IsChecked(x)) {
if (!srvs.IsEmpty()) srvs += ";";
srvs += lb->GetString(x);
}
settings->Write(dlgName + "/AutoConnect", srvs);
wxString s = my_view->getSettingString();
settings->Write(dlgName+"/ColsWidth",s);
settings->WriteBool(dlgName + "/Mode", group->IsChecked());
Storage *st=m_storage_model->getStorage();
st->saveFilters();
mainForm->Logfrm = NULL;
}
void frmLog::Go()
{
// Show the window
Show(true);
}
LogFactory::LogFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Log view..."), _("Log view CSV format"));
}
wxWindow* LogFactory::StartDialog(frmMain* form, pgObject* obj)
{
pgServer* srv = (pgServer *) obj;
wxString txt = "";
if (form->Logfrm != NULL) {
if (!form->Logfrm->CheckConn(srv->GetName(), srv->GetPort())) {
pgConn* conn = form->Logfrm->createConn(srv);
form->Logfrm->AddNewConn(conn);
}
}
else {
form->Logfrm = new frmLog(form, txt, srv);
if (form->Logfrm!=NULL)
form->AddFrame(form->Logfrm);
}
//frmLog* frm = new frmLog(form, obj);
form->Logfrm->Go();
return 0;
}
bool LogFactory::CheckEnable(pgObject* obj)
{
if (!obj)
return false;
if (obj->GetMetaType() == PGM_SERVER) {
// if (!((pgServer*)obj)->GetConnected())
// return false;
return true;
}
return false;
}

View file

@ -63,6 +63,7 @@
#include "frm/frmReport.h"
#include "frm/frmMaintenance.h"
#include "frm/frmStatus.h"
#include "frm/frmLog.h"
#include "frm/frmPassword.h"
#ifdef DATABASEDESIGNER
#include "frm/frmDatabaseDesigner.h"
@ -109,7 +110,7 @@ frmMain::frmMain(const wxString &title)
lastPluginUtility = NULL;
pluginUtilityCount = 0;
m_refreshing = false;
Logfrm = NULL;
dlgName = wxT("frmMain");
SetMinSize(wxSize(600, 450));
RestorePosition(50, 50, 750, 550, 600, 450);
@ -375,6 +376,7 @@ void frmMain::CreateMenus()
new connectServerFactory(menuFactories, toolsMenu, 0);
new disconnectServerFactory(menuFactories, toolsMenu, 0);
new disconnectServerFactoryAll(menuFactories, toolsMenu, 0);
new disconnectDatabaseFactory(menuFactories, toolsMenu, 0);
new startServiceFactory(menuFactories, toolsMenu, 0);
@ -448,6 +450,7 @@ void frmMain::CreateMenus()
new backupFactory(menuFactories, toolsMenu, 0);
new backupGlobalsFactory(menuFactories, toolsMenu, 0);
new LogFactory(menuFactories, cfgMenu, 0);
new backupServerFactory(menuFactories, toolsMenu, 0);
new restoreFactory(menuFactories, toolsMenu, 0);
new importFactory(menuFactories, toolsMenu, 0);

134
include/frm/frmLog.h Normal file
View file

@ -0,0 +1,134 @@
//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// frmStatus.h - Status Screen
//
//////////////////////////////////////////////////////////////////////////
#ifndef __FRMLOG_H
#define __FRMLOG_H
// wxWindows headers
#include <wx/wx.h>
#include <wx/image.h>
#include <wx/listctrl.h>
#include <wx/notebook.h>
#include <wx/notebook.h>
#include <wx/dynarray.h>
// wxAUI
#include <wx/aui/aui.h>
#include "dlg/dlgClasses.h"
#include "utils/factory.h"
#include "ctl/ctlAuiNotebook.h"
#include "log/StorageModel.h"
#include "log/MyDataViewCtrl.h"
class RemoteConn2
{
public:
RemoteConn2(pgConn* c)
{
conn = c;
}
~RemoteConn2()
{
if (conn) delete conn;
}
pgConn* conn;
};
WX_DECLARE_OBJARRAY(RemoteConn2, RemoteConnArray2);
enum
{
ID_SET_GROUP = 207,
ID_CLEAR_ALL_FILTER = 208,
ID_SET_DETAILGROUP = 209,
ID_ADD_FILTER = 210,
ID_NEXT_MAX
};
//
// This number MUST be incremented if changing any of the default perspectives
//
#define FRMLOG_PERSPECTIVE_VER wxT("8274")
#ifdef __WXMAC__
#define FRMLOG_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6293500;dir=4;layer=0;row=0;pos=0;prop=100000;bestw=321;besth=244;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=462;floaty=165;floatw=595;floath=282|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=0;pos=1;prop=100000;bestw=321;besth=244;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-231;floaty=235;floatw=595;floath=282|name=Transactions;caption=Transactions;state=6293500;dir=4;layer=0;row=0;pos=2;prop=100000;bestw=0;besth=0;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=461;floaty=527;floatw=595;floath=282|name=Logfile;caption=Logfile;state=6293500;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=0;besth=0;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-103;floaty=351;floatw=595;floath=282|name=toolBar;caption=Tool bar;state=2124528;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=808;besth=33;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=888;floaty=829;floatw=558;floath=49|dock_size(4,0,0)=583|dock_size(5,0,0)=10|dock_size(1,10,0)=35|")
#else
#ifdef __WXGTK__
#define FRMLOG_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6293500;dir=4;layer=0;row=1;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=174;floaty=216;floatw=578;floath=282|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=1;pos=2;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=136;floaty=339;floatw=576;floath=283|name=Transactions;caption=Transactions;state=6293500;dir=4;layer=0;row=1;pos=3;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=133;floaty=645;floatw=577;floath=283|name=Querystate;caption=Query State;state=6309884;dir=4;layer=0;row=1;pos=1;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=154;floaty=255;floatw=1360;floath=751|name=Logfile;caption=Logfile;state=6293500;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=toolBar;caption=toolBar;state=2108144;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=716;besth=23;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=586;floaty=525;floatw=483;floath=49|dock_size(1,10,0)=25|dock_size(4,0,1)=1115|dock_size(5,0,0)=22|")
#else
#define FRMLOG_DEFAULT_PERSPECTIVE wxT("layout2|name=Activity;caption=Activity;state=6293500;dir=4;layer=0;row=1;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=174;floaty=216;floatw=578;floath=282|name=Locks;caption=Locks;state=6293500;dir=4;layer=0;row=1;pos=2;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=136;floaty=339;floatw=576;floath=283|name=Transactions;caption=Transactions;state=6293500;dir=4;layer=0;row=1;pos=3;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=133;floaty=645;floatw=577;floath=283|name=Querystate;caption=Query State;state=6309884;dir=4;layer=0;row=1;pos=1;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=154;floaty=255;floatw=1360;floath=751|name=Logfile;caption=Logfile;state=6293500;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=toolBar;caption=toolBar;state=2108144;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=716;besth=23;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=586;floaty=525;floatw=483;floath=49|dock_size(1,10,0)=25|dock_size(4,0,1)=1115|dock_size(5,0,0)=22|")
#endif
#endif
class frmLog : public pgFrame
{
public:
frmLog(frmMain *form, const wxString &_title, pgServer *srv);
~frmLog();
void Go();
void getFilename();
void readLogFile(wxString logfileName, long& lenfile, long& logfileLength, wxString& savedPartialLine, pgConn* conn);
void AddNewConn(pgConn* con);
pgServer* getServer(wxString& strserver);
pgConn* createConn(pgServer* srv);
bool CheckConn(wxString host, int port);
private:
static const int timerInterval = 5000; // 1000 ms
wxTimer m_timer;
wxAuiManager manager;
frmMain *mainForm;
pgConn *connection;
RemoteConnArray2 conArray;
MyDataViewCtrl* my_view;
wxNotebook* m_notebook;
wxStaticText* status;
wxCheckBox *group, *detail;
wxCheckListBox* lb;
wxObjectDataPtr<StorageModel> m_storage_model;
wxArrayString logfileName;
wxArrayString savedPartialLine;
wxArrayLong logfileLength;
wxArrayLong len;
void OnSetGroup(wxCommandEvent& event);
void OnSetDetailGroup(wxCommandEvent& event);
void OnClearAllFilter(wxCommandEvent& event);
void OnAddFilterIgnore(wxCommandEvent& event);
void OnSetFocus(wxFocusEvent& event);
void OnKillFocus(wxFocusEvent& event);
void OnActivate(wxActivateEvent& event);
void OnTimer(wxTimerEvent& event);
void seticon(bool errflag);
wxIcon idef;
wxIcon idefRed;
DECLARE_EVENT_TABLE()
};
class LogFactory : public contextActionFactory
{
public:
LogFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar);
wxWindow* StartDialog(frmMain* form, pgObject* obj);
bool CheckEnable(pgObject* obj);
};
#endif

View file

@ -24,6 +24,7 @@
#include "frm/frmQuery.h"
#include "dlg/dlgClasses.h"
#include "utils/factory.h"
#include "frm/frmLog.h"
//
// This number MUST be incremented if changing any of the default perspectives
@ -41,6 +42,7 @@
#endif
#endif
class pgServer;
class Logfrm;
class pgServerCollection;
class ctlSQLBox;
class ctlTree;
@ -94,7 +96,6 @@ class frmMain : public pgFrame
public:
frmMain(const wxString &title);
~frmMain();
void OnAction(wxCommandEvent &ev);
void OnReport(wxCommandEvent &ev);
wxString GetHelpPage() const;
@ -191,12 +192,14 @@ public:
void ObjectBrowserRefreshing(bool refresh)
{
m_refreshing = refresh;
}
};
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
void OnSSHTunnelEvent(wxCommandEvent &event);
#endif
public:
frmLog* Logfrm;
private:
wxAuiManager manager;
ctlTree *browser;

View file

@ -0,0 +1,53 @@
#pragma once
#include "wx\dataview.h"
#include "wx/wx.h"
#include "Storage.h"
class MyDataViewCtrl :
public wxDataViewCtrl
{
public:
MyDataViewCtrl(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxASCII_STR(wxDataViewCtrlNameStr)
) : wxDataViewCtrl(parent, id, pos, size, style, validator, name)
{
}
void setStatusObj(wxStaticText* o) {
st = o;
}
int GetLastMouseRow() { return lastrow; };
int GetLastMouseCol() { return lastcol; };
wxString getSettingString();
void setSettingString(wxString setstr);
void setGroupMode(bool mode);
void ViewGroup(bool view);
void ClearAllFilter();
void AddFilterIgnore();
void AddRow(wxString csvtext);
void OnMouseMove(wxMouseEvent& event);
void OnKEY_DOWN(wxKeyEvent& event);
#ifdef MYTEST
void OnTimer(wxTimerEvent& event);
#endif
void OnEVT_DATAVIEW_COLUMN_HEADER_CLICK(wxDataViewEvent& event);
void OnEVT_DATAVIEW_CONTEXT_MENU(wxCommandEvent& event);
void OnEVT_DATAVIEW_SELECTION_CHANGED(wxDataViewEvent& event);
void OnContextMenu(wxDataViewEvent& event);
DECLARE_EVENT_TABLE()
private:
int lastrow = -1, lastcol = 1;
wxDataViewItem selectRowGroup;
bool modctrl = false;
wxStaticText* st;
//bool visibleEndLine = false;
#ifdef MYTEST
int linenumber = -1;
wxArrayString logadd;
#endif
};

183
include/log/Storage.h Normal file
View file

@ -0,0 +1,183 @@
#pragma once
#include <deque>
#include <wx/wx.h>
#include "wx/hashmap.h"
#include "utils/csvfiles.h"
#define MYTEST 1
#define FL_REVERSE 1
#define FL_CONTAINS 2
WX_DECLARE_HASH_MAP(int, int, wxIntegerHash, wxIntegerEqual, MyHashToRow);
namespace MyConst {
enum colField {
logtime,
loguser,
logdb,
logpid,
loghost,
logtag,
logSessiontime,
logSeverity,
logSqlstate,
logMessage,
logDetail,
logHint,
logappname,
logbtype,
Col_Max
};
enum iconIndex {
log,
war,
user,
error,
fatal,
panic,
MAX_COL
};
enum ltype {
SIMPLE_TEXT,
InGroup,
Group
};
}
struct ps {
unsigned short int s;
unsigned short int l;
};
struct Line {
unsigned short type : 3;
unsigned short icon : 3;
unsigned short visible : 1;
int prevRowGroup = -1;
int hash;
ps logtime = { 0,0 };
ps loguser = { 0,0 };
ps logdb = { 0,0 };
ps logpid = { 0,0 };
ps loghost = { 0,0 };
ps logtag = { 0,0 };
ps logSessiontime = { 0,0 };
ps logSeverity = { 0,0 };
ps logSqlstate = { 0,0 };
ps logMessage = { 0,0 };
ps logDetail = { 0,0 };
ps logHint = { 0,0 };
ps logappname = { 0,0 };
ps logbtype = { 0,0 };
wxString text;
};
struct LineFilter {
int col;
int flags;
wxString val;
};
class Storage
{
public:
bool AddLineTextCSV(const wxString& strcsv);
wxString GetField(int row, MyConst::colField col);
wxString GetFieldStorage(int row, MyConst::colField col, bool filter);
Storage();
int GetSeverityIndex(int row);
void SetHost(wxString& host) { currhost = host; };
wxString GetHost() { return currhost; };
void SetErrMsgFlag(bool flag) { err_msg = flag; };
bool GetErrMsgFlag() { return err_msg; };
void GetRowsStat(int& Rowsadd, int& Rowsignore) {
Rowsadd=rowsadd;
Rowsignore=rowsignore;
};
void ClearRowsStat() {
rowsadd = 0;
rowsignore = 0;
};
wxColor& GetBgColorLine(int row);
// óñòàíîâêà ôèëüòðà íà êîëîíêó
int SetFilter(int colfld, wxString& val, int flags);
// ïðèìèíèòü ôèëüòð äëÿ ñòðîêè èëè äëÿ âñå ñòðîê õðàíèëèùà
// true åñëè ñòðîêà íå îòôèëüòðîâàëàñü (âèäíà)
bool ApplyFilter(int row = -1);
wxString getStrGroup(wxString source);
int testFilter(MyConst::colField col, int position);
wxString GetStringFilterExpr(int positionArrayFilter,bool addNumCol=false);
void addLineFilterStr(wxString strflt);
wxString _strwhere(int flags);
void saveFilters();
wxString LineFilterToStr(LineFilter& lf);
//
int getHashString(wxString str) {
std::hash<wxString> string_hash;
return string_hash(str);
}
void DropColFilter(int index);
void setDetailGroupRow(int rowGroup);
bool IsGroupFilter() {
return groupFilterUse;
};
bool IsFilter() {
return fCol.size() > 0 || IsGroupFilter();
};
void setGroupFilter(bool val) {
detailGroup = -1;
groupFilterUse = val;
MyHashToRow::iterator it;
for (it = hashKeyToCount.begin(); it != hashKeyToCount.end(); ++it)
{
int keyhash = it->first, lastrow = it->second;
// do something useful with key and value
it->second = 0;
}
m_cacheIndex = -1;
frows.clear();
}
void ClearCount(int rowfilter);
bool IsAddGroupNew() {
return faddgroup;
};
int getLastRowIndex() { return m_cacheIndex; }
// âñåãî ñòðîê â õðàíèëèùå
int getCountStore();
int getCountFilter();
int getCountGroup(int row);
int GetTotalCountGroup(int rowfilter);
private:
bool checkFilter(Line& l);
Line getLineParse(const wxString& str, bool csv = false);
wxString get_field(Line& l, MyConst::colField col);
LineFilter getLineFilter(wxString strflt);
void getLineToCache(int row, bool filter = true);
bool CompareFilterLine(int row, bool filter);
std::deque<Line> storage;
std::deque<LineFilter> filterload;
// hash Group to row
MyHashToRow hashKeyToRow;
MyHashToRow hashKeyToCount;
MyHashToRow hashKeyTotal;
//filter setting
std::deque<int> frows;
wxArrayInt fCol;
wxArrayInt fFlags;
wxArrayString fVal;
// ïðèçíàê îøèáîê.
bool err_msg;
// ðåæèì ãðóïïèðîâêè
bool groupFilterUse = false;
bool faddgroup = false;
int prevRow = -1;
int detailGroup = -1;
//
int m_cacheIndex = -1;
Line m_cacheLine;
//
int rowsadd;
int rowsignore;
wxColor bgErr[MyConst::iconIndex::MAX_COL];
wxString currhost;
};

101
include/log/StorageModel.h Normal file
View file

@ -0,0 +1,101 @@
#pragma once
#include "pgAdmin3.h"
#include "wx/hashmap.h"
#include "wx/vector.h"
#include "Storage.h"
#include "MyDataViewCtrl.h"
WX_DECLARE_STRING_HASH_MAP(int, MyHashCount);
class StorageModel : public wxDataViewVirtualListModel
{
public:
enum cols
{
Col_ToggleIconText,
Col_LogTime,
Col_User,
Col_Db,
Col_PID,
Col_Host,
Col_App,
Col_Hint,
Col_Detail,
Col_Message,
Col_Max
};
StorageModel(MyDataViewCtrl* view);
// helper methods to change the model
bool Prepend(const wxString& text);
void DeleteItem(const wxDataViewItem& item);
void DeleteItems(const wxDataViewItemArray& items);
void AddMany();
bool setFilter(int col, wxString val, int flags, MyDataViewCtrl* view);
void DropColFilter(int index);
int testFilter(int col, int position);
void ApplyFilter();
bool getGroupFilter()
{
return store->IsGroupFilter();
}
void setGroupFilter(bool val)
{
store->setGroupFilter(val);
ApplyFilter();
}
void BuildColumns(MyDataViewCtrl* ctrl);
void IncCountFreq(int col, wxString &val) {
MyHashCount::const_iterator it = freqValues[col].find(val);
int cnt=0;
if (it != freqValues[col].end())
cnt = it->second;
cnt++;
freqValues[col][val] = cnt;
}
// implementation of base class virtuals to define model
virtual unsigned int GetColumnCount() const wxOVERRIDE
{
return Col_Max;
}
unsigned int GetRowCount() const
{
return store->getCountFilter();
}
virtual wxString GetColumnType(unsigned int col) const wxOVERRIDE
{
if (col == Col_ToggleIconText)
return wxDataViewCheckIconTextRenderer::GetDefaultType();
return "string";
}
Storage* getStorage() {
return store;
}
virtual void GetValueByRow(wxVariant& variant,
unsigned int row, unsigned int col) const wxOVERRIDE;
virtual bool GetAttrByRow(unsigned int row, unsigned int col,
wxDataViewItemAttr& attr) const wxOVERRIDE;
virtual bool SetValueByRow(const wxVariant& variant,
unsigned int row, unsigned int col) wxOVERRIDE;
MyHashCount freqValues[Col_Max];
wxBitmap bitmapflt;
unsigned int lastrow;
MyDataViewCtrl* m_view;
private:
///IntToStringMap m_customColValues;
wxIcon m_icon[MyConst::iconIndex::MAX_COL];
Storage* store;
MyConst::colField colmap[Col_Max];
};

25
include/log/error_xpm.xpm Normal file
View file

@ -0,0 +1,25 @@
/* XPM */
static char *error_xpm[] = {
"16 16 5 1 0 0",
" c #000000",
"! c #FF0000",
"# c #333333",
"$ c #FF8080",
"% c None",
"################",
"#%%%%%%%%%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!$%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!!!!%%%%%%%#",
"#%%!!!!!%%%%%%%#",
"#%%!!$%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%%%%%%%%%%%%%#",
"################"
};

View file

@ -0,0 +1,25 @@
/* XPM */
static char *errorl_xpm[] = {
"16 16 5 1 0 0",
" c #000000",
"! c #FF0000",
"# c #333333",
"$ c #FF8080",
"% c None",
"################",
"#%%%%%%%%%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!$%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!!!!%%%%%%%#",
"#%%!!!!!%%%%%%%#",
"#%%!!$%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!%%%%%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%!!!!!!%%%%%%#",
"#%%%%%%%%%%%%%%#",
"################"
};

26
include/log/fatal_xpm.xpm Normal file
View file

@ -0,0 +1,26 @@
/* XPM */
static char *fatal_xpm[] = {
"16 16 6 1 0 0",
" c #000000",
"! c #FF0000",
"# c #333333",
"$ c #FF8080",
"% c #FFFFFF",
"& c None",
"################",
"#&%%%%%%%%&&&&&#",
"#&%!!!!!!%&&&&&#",
"#&%!!!!!!%&&&&&#",
"#&%!!$%%%%&&&&&#",
"#&%!!%&&&&&&&&&#",
"#&%!!%%%%&&&&&&#",
"#&%!!!!!%&&&&&&#",
"#&%!!!!!%&&&&&&#",
"#&%!!$%%%&&&&&&#",
"#&%!!%&&&&&&&&&#",
"#&%!!%&&&&&&&&&#",
"#&%!!%&&&&&&&&&#",
"#&%!!%&&&&&&&&&#",
"#&%%%%&&&&&&&&&#",
"################"
};

View file

@ -0,0 +1,34 @@
/* XPM */
static char *filter_xpm[] = {
"16 16 14 1 0 0",
" c #000000",
"! c #151515",
"# c #181818",
"$ c #202020",
"% c #242424",
"& c #2C2C2C",
"' c #323232",
"( c #333333",
") c #474747",
"* c #848484",
"+ c #898989",
", c #929292",
"- c #FFFFFF",
". c None",
"................",
"..)&((((((((#!..",
"..* ........ ..",
".. %,...... ...",
"... '..... ....",
"....$+.... .....",
".... ... .....",
"..... ... ......",
"...... .. ......",
"...... .. ......",
"...... .. ......",
"...... .. ......",
"...... .. ......",
"...... .. ......",
"...... ......",
"................"
};

View file

@ -0,0 +1,27 @@
/* XPM */
static char *log_red_xpm[] = {
"16 16 7 1 0 0",
" c #000000",
"! c #FF0000",
"# c #2B2B2B",
"$ c #4E4E4E",
"% c #717171",
"& c #FFFFFF",
"' c None",
" ",
" '''''''''''''' ",
" '& %''''''''' ",
" '& #''''''''' ",
" '& #'!!!!!!'' ",
" '& #'!!!!!!'' ",
" '& #'!!!!!!'' ",
" '& #'!!!!!!'' ",
" '& #'!!!!!!'' ",
" '& #'!!!!!!'' ",
" '& #''''''''' ",
" '& #''''''''' ",
" '& $''' ",
" '& $''' ",
" '&&&&&&&&&'''' ",
" "
};

26
include/log/log_xpm.xpm Normal file
View file

@ -0,0 +1,26 @@
/* XPM */
static char *log_xpm[] = {
"16 16 6 1 0 0",
" c #000000",
"! c #2B2B2B",
"# c #4E4E4E",
"$ c #717171",
"% c #FFFFFF",
"& c None",
" ",
" &&&&&&&&&&&&&& ",
" &% $&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% !&&&&&&&&& ",
" &% #&&& ",
" &% #&&& ",
" &%%%%%%%%%&&&& ",
" "
};

30
include/log/null.xpm Normal file
View file

@ -0,0 +1,30 @@
/* XPM */
static const char *const null_xpm[] = {
"16 16 11 1",
" c None",
". c #000000",
"+ c #FFFFFF",
"@ c #FDE4E4",
"# c #F45C5C",
"$ c #EF0B0B",
"% c #F12727",
"& c #F67878",
"* c #FAAEAE",
"= c #FBC9C9",
"- c #F89393",
"................",
".++++++++++++++.",
".++++++++++++++.",
".++++@#$$%&++++.",
".+++@%$$$$$&+++.",
".+++&$$#*%$$+++.",
".+++=-#+@%$%+++.",
".++++++@%$$*+++.",
".++++++%$%@++++.",
".+++++*$$*+++++.",
".+++++@**@+++++.",
".+++++*$$*+++++.",
".+++++*$$*+++++.",
".+++++@**@+++++.",
".++++++++++++++.",
"................"};

26
include/log/panic_xpm.xpm Normal file
View file

@ -0,0 +1,26 @@
/* XPM */
static char *panic_xpm[] = {
"16 16 6 1 0 0",
" c #000000",
"! c #FF0000",
"# c #333333",
"$ c #FF8080",
"% c #FFFFFF",
"& c None",
"################",
"#&%%%%%%%%%&&&&#",
"#&%!!!!!!!!%%&&#",
"#&%!!!!!!!!!%&&#",
"#&%!!$%%%%!!%&&#",
"#&%!!$%&&%!!%&&#",
"#&%!!$%&&%!!%&&#",
"#&%!!$%%%!!!%&&#",
"#&%!!!!!!!!%%&&#",
"#&%!!!!!!!%%&&&#",
"#&%!!$%%%%%&&&&#",
"#&%!!$%&&&&&&&&#",
"#&%!!$%&&&&&&&&#",
"#&%!!$%&&&&&&&&#",
"#&%%%%&&&&&&&&&#",
"################"
};

23
include/log/user_xpm.xpm Normal file
View file

@ -0,0 +1,23 @@
/* XPM */
static char *user_xpm[] = {
"16 16 3 1 0 0",
" c #000000",
"! c #3F28D9",
"# c None",
" ",
" ############## ",
" ############## ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ##!!###!!##### ",
" ###!!!!!###### ",
" ############## ",
" ############## ",
" "
};

23
include/log/war_xpm.xpm Normal file
View file

@ -0,0 +1,23 @@
/* XPM */
static char *war_xpm[] = {
"16 16 3 1 0 0",
" c #000000",
"! c #C0C0C0",
"# c None",
" ",
" ############## ",
" ############## ",
" ############## ",
" ### ### ! ",
" !## ### ! ",
" # !# ! !#! !! ",
" # !! ! # !# ",
" # ! ! # !# ",
" # ! !# # !# ",
" ## ! !!# ! !!# ",
" ## !## !## ",
" ## !## !## ",
" ## !## !## ",
" ############## ",
" "
};

28
include/log/wx_small.xpm Normal file
View file

@ -0,0 +1,28 @@
/* XPM */
static const char *wx_small_xpm[] = {
/* columns rows colors chars-per-pixel */
"16 16 6 1",
". c Black",
"o c #FFFFFF",
"X c #000080",
"O c #FFFF00",
" c None",
"+ c #FF0000",
/* pixels */
" ",
" ",
" ",
" ....... ",
" .XXXXX. ",
" .oXXXX. ",
" .oXXX.......",
".....oXXX.OOOOO.",
".+++.XXXX.oOOOO.",
".o++......oOOOO.",
".o++++. .oOOOO.",
".o++++. .OOOOO.",
".+++++. .......",
"....... ",
" ",
" "
};

View file

@ -666,12 +666,19 @@ public:
};
class disconnectServerFactoryAll : public contextActionFactory
{
public:
disconnectServerFactoryAll(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar);
wxWindow *StartDialog(frmMain *form, pgObject *obj);
bool CheckEnable(pgObject *obj);
};
class disconnectServerFactory : public contextActionFactory
{
public:
disconnectServerFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar);
wxWindow *StartDialog(frmMain *form, pgObject *obj);
bool CheckEnable(pgObject *obj);
disconnectServerFactory(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar);
wxWindow* StartDialog(frmMain* form, pgObject* obj);
bool CheckEnable(pgObject* obj);
};
class reloadconfServiceFactory : public contextActionFactory

View file

@ -26,6 +26,7 @@ enum
FEATURE_PGSTATINDEX,
FEATURE_PGCHECKINDEX,
FEATURE_FUNCTION_DEFAULTS,
FEATURE_CSVLOG,
FEATURE_LAST
};

View file

@ -54,6 +54,7 @@
#include "frm/frmConfig.h"
#include "frm/frmQuery.h"
#include "frm/frmStatus.h"
#include "frm/frmLog.h"
#ifdef DATABASEDESIGNER
#include "frm/frmDatabaseDesigner.h"
#endif
@ -281,6 +282,7 @@ bool pgAdmin3::OnInit()
{wxCMD_LINE_SWITCH, "h", "help", _("show this help message, and quit"), wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
{wxCMD_LINE_OPTION, "s", "server", _("auto-connect to specified server"), wxCMD_LINE_VAL_STRING},
{wxCMD_LINE_SWITCH, "S", "serverstatus", _("open server status window"), wxCMD_LINE_VAL_NONE},
{wxCMD_LINE_SWITCH, "L", "log window", _("open server log window"), wxCMD_LINE_VAL_NONE},
{wxCMD_LINE_OPTION, "Sc", "serverstatusconnect", _("connect server status window to database"), wxCMD_LINE_VAL_STRING},
{wxCMD_LINE_SWITCH, "q", "query", _("open query tool"), wxCMD_LINE_VAL_NONE},
{wxCMD_LINE_OPTION, "qc", "queryconnect", _("connect query tool to database"), wxCMD_LINE_VAL_STRING},
@ -575,10 +577,11 @@ bool pgAdmin3::OnInit()
}
if (!conn)
return false;
wxString txt = _("Server Status - ") + conn->GetName();
frmStatus *fq = new frmStatus(NULL, txt, conn);
fq->Go();
wxString txt = _("Server Status - ") + conn->GetName();
frmStatus* fq = new frmStatus(NULL, txt, conn);
fq->Go();
}
#ifdef DATABASEDESIGNER
@ -793,6 +796,14 @@ bool pgAdmin3::OnInit()
if (cmdParser.Found(wxT("s"), &str))
{
pgServer *srv = winMain->ConnectToServer(str, !cmdParser.Found(wxT("q")));
if (srv && cmdParser.Found(wxT("L"))) {
//PgConn* newcon = srv->GetConnection()->Duplicate("Log conn");
wxString txt = _("Log - ") + srv->GetConnection()->GetName();
winMain->Logfrm = new frmLog(winMain, txt, srv);
winMain->Logfrm->Go();
}
if (srv && cmdParser.Found(wxT("q")))
{
pgConn *conn;

View file

@ -905,6 +905,7 @@
<ClCompile Include="frm\frmHbaConfig.cpp" />
<ClCompile Include="frm\frmHint.cpp" />
<ClCompile Include="frm\frmImport.cpp" />
<ClCompile Include="frm\frmLog.cpp" />
<ClCompile Include="frm\frmMain.cpp" />
<ClCompile Include="frm\frmMainConfig.cpp" />
<ClCompile Include="frm\frmMaintenance.cpp" />
@ -1012,6 +1013,9 @@
<ClCompile Include="utils\diff_match_patch.cc" />
<ClCompile Include="utils\factory.cpp" />
<ClCompile Include="utils\favourites.cpp" />
<ClCompile Include="utils\log\MyDataViewCtrl.cpp" />
<ClCompile Include="utils\log\Storage.cpp" />
<ClCompile Include="utils\log\StorageModel.cpp" />
<ClCompile Include="utils\macros.cpp" />
<ClCompile Include="utils\misc.cpp" />
<ClCompile Include="utils\pgconfig.cpp" />
@ -1353,6 +1357,16 @@
<None Include="db\module.mk" />
<None Include="dlg\module.mk" />
<None Include="frm\module.mk" />
<None Include="include\log\errorl_xpm.xpm" />
<None Include="include\log\error_xpm.xpm" />
<None Include="include\log\fatal_xpm.xpm" />
<None Include="include\log\filter_xpm.xpm" />
<None Include="include\log\log_xpm.xpm" />
<None Include="include\log\null.xpm" />
<None Include="include\log\panic_xpm.xpm" />
<None Include="include\log\user_xpm.xpm" />
<None Include="include\log\war_xpm.xpm" />
<None Include="include\log\wx_small.xpm" />
<None Include="include\module.mk" />
<None Include="include\utils\module.mk" />
<None Include="include\ctl\module.mk" />
@ -1528,6 +1542,10 @@
<ClInclude Include="include\copyright.h" />
<ClInclude Include="include\dlg\dlgProJob.h" />
<ClInclude Include="include\dlg\dlgResourceGroup.h" />
<ClInclude Include="include\frm\frmLog.h" />
<ClInclude Include="include\log\MyDataViewCtrl.h" />
<ClInclude Include="include\log\Storage.h" />
<ClInclude Include="include\log\StorageModel.h" />
<ClInclude Include="include\pgAdmin3.h" />
<ClInclude Include="include\postgres.h" />
<ClInclude Include="include\precomp.h" />

View file

@ -251,6 +251,9 @@
<Filter Include="include\pro_scheduler">
<UniqueIdentifier>{bc8bb60a-46b2-4f26-b86f-f711fcc2a385}</UniqueIdentifier>
</Filter>
<Filter Include="include\log">
<UniqueIdentifier>{e2f5428c-112b-47e2-8394-cf4a5c1a8d8a}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="agent\dlgJob.cpp">
@ -1635,6 +1638,18 @@
<ClCompile Include="dlg\dlgProJob.cpp">
<Filter>dlg</Filter>
</ClCompile>
<ClCompile Include="frm\frmLog.cpp">
<Filter>frm</Filter>
</ClCompile>
<ClCompile Include="utils\log\Storage.cpp">
<Filter>utils</Filter>
</ClCompile>
<ClCompile Include="utils\log\StorageModel.cpp">
<Filter>utils</Filter>
</ClCompile>
<ClCompile Include="utils\log\MyDataViewCtrl.cpp">
<Filter>utils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="agent\module.mk">
@ -2154,6 +2169,36 @@
<None Include="ui\dlgMoveTablespace.xrc">
<Filter>ui</Filter>
</None>
<None Include="include\log\error_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\errorl_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\fatal_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\filter_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\log_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\null.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\panic_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\user_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\war_xpm.xpm">
<Filter>include\log</Filter>
</None>
<None Include="include\log\wx_small.xpm">
<Filter>include\log</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\copyright.h">
@ -3491,6 +3536,18 @@
<ClInclude Include="include\dlg\dlgProJob.h">
<Filter>include\dlg</Filter>
</ClInclude>
<ClInclude Include="include\frm\frmLog.h">
<Filter>include\frm</Filter>
</ClInclude>
<ClInclude Include="include\log\Storage.h">
<Filter>include\log</Filter>
</ClInclude>
<ClInclude Include="include\log\StorageModel.h">
<Filter>include\log</Filter>
</ClInclude>
<ClInclude Include="include\log\MyDataViewCtrl.h">
<Filter>include\log</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<png2c Include="include\images\aggregate-sm.png">

View file

@ -2170,6 +2170,67 @@ bool disconnectServerFactory::CheckEnable(pgObject *obj)
return false;
}
disconnectServerFactoryAll::disconnectServerFactoryAll(menuFactoryList* list, wxMenu* mnu, ctlMenuToolbar* toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Disconnec&t all servers"), _("Disconnect from all servers."));
}
wxWindow* disconnectServerFactoryAll::StartDialog(frmMain* form, pgObject* obj)
{
wxTreeItemIdValue foldercookie, servercookie;
wxTreeItemId folderitem, serveritem;
pgObject* object;
pgServer* server;
folderitem = form->GetBrowser()->GetFirstChild(form->GetBrowser()->GetRootItem(), foldercookie);
while (folderitem)
{
if (form->GetBrowser()->ItemHasChildren(folderitem))
{
serveritem = form->GetBrowser()->GetFirstChild(folderitem, servercookie);
while (serveritem)
{
object = form->GetBrowser()->GetObject(serveritem);
if (object && object->IsCreatedBy(serverFactory))
{
if (CheckEnable(object)) {
if (object->CheckOpenDialogs(form->GetBrowser(), serveritem))
{
wxString msg = _("There are properties dialogues open for one or more objects belonging to a database which will be disconnected. Please close the properties dialogues and try again.");
wxMessageBox(msg, _("Cannot disconnect database"), wxICON_WARNING | wxOK);
}
else
{
server = (pgServer*)object;
server->Disconnect(form);
server->UpdateIcon(form->GetBrowser());
form->GetBrowser()->DeleteChildren(object->GetId());
form->execSelChange(object->GetId(), true);
}
}
}
serveritem = form->GetBrowser()->GetNextChild(folderitem, servercookie);
}
}
folderitem = form->GetBrowser()->GetNextChild(form->GetBrowser()->GetRootItem(), foldercookie);
}
return 0;
}
bool disconnectServerFactoryAll::CheckEnable(pgObject* obj)
{
if (obj && obj->IsCreatedBy(serverFactory))
return ((pgServer*)obj)->GetConnected();
return false;
}
reloadconfServiceFactory::reloadconfServiceFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
{
mnu->Append(id, _("Reload configuration"), _("Reload configuration"));

View file

@ -0,0 +1,470 @@
#include "pgAdmin3.h"
#include "log/MyDataViewCtrl.h"
#include "log/StorageModel.h"
#include "wx/headerctrl.h"
#include "wx/itemattr.h"
#include "wx/dataview.h"
//#include <map>
#include <set>
BEGIN_EVENT_TABLE(MyDataViewCtrl, wxDataViewCtrl)
EVT_MOTION(MyDataViewCtrl::OnMouseMove)
END_EVENT_TABLE()
#ifdef MYTEST
#include <wx/textfile.h>
void MyDataViewCtrl::OnTimer(wxTimerEvent& event) {
// wxLogMessage("OnTimer called.");
if (linenumber == -1) {
wxTextFile tfile;
wxString str;
tfile.Open("logAdd.csv");
// read the first line
str = tfile.GetFirstLine() + "\n";
// read all lines one by one
// until the end of the file
while (!tfile.Eof())
{
str += tfile.GetNextLine() + "\n";
}
tfile.Close();
CSVLineTokenizer tk(str);
wxString line;
while (tk.HasMoreLines())
{
line.Clear();
bool partial;
line = tk.GetNextLine(partial);
if (partial)
{
break;
}
//Prepend(line);
logadd.Add(line);
}
linenumber = 0;
}
if (logadd.GetCount() > linenumber) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
//m->Prepend(logadd[linenumber++]);
AddRow(logadd[linenumber++]);
wxString l = wxString::Format("rows %d", m->GetCount());
st->SetLabelText(l);
if (m->getGroupFilter()) Refresh();
}
}
#endif
void MyDataViewCtrl::setSettingString(wxString setstr) {
wxString s;
wxStringTokenizer tokenizer(setstr, ";");
for (int i = 0; i < GetColumnCount(); i++) {
if (!tokenizer.HasMoreTokens()) break;
wxString token = tokenizer.GetNextToken();
wxDataViewColumn* vc = GetColumn(i);
int w;
w = wxAtoi(token);
vc->SetWidth(w);
}
return;
}
wxString MyDataViewCtrl::getSettingString() {
wxString s;
for (int i = 0; i < GetColumnCount(); i++) {
wxDataViewColumn* vc = GetColumn(i);
int width = vc->GetWidth();
if (!s.IsEmpty()) s = s + ";";
s = s + wxString::Format("%d", width);
}
//s = s + wxString::Format(";%d", modeGroup);
return s;
}
void MyDataViewCtrl::setGroupMode(bool mode) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
m->setGroupFilter(mode);
wxItemAttr attr;
if (m->getGroupFilter())
{
//attr.SetTextColour(*wxRED);
//attr.GetFont().Underlined();
attr.SetFont(attr.GetFont().Underlined());
//attr.GetFont().
//wxFontStyle s=attr.GetFont().GetStyle();
//attr.SetFont(attr.GetFont().Strikethrough());
}
//else: leave it as default to disable custom header attributes
wxString l = wxString::Format("rows %d", m->GetRowCount());
st->SetLabelText(l);
if (!SetHeaderAttr(attr))
wxLogMessage("Sorry, header attributes not supported on this platform");
}
void MyDataViewCtrl::ViewGroup(bool view) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
Storage* sta = m->getStorage();
if (view) {
selectRowGroup = GetSelection();
if (!selectRowGroup.IsOk()) return;
int rowselectGroup = m->GetRow(selectRowGroup);
sta->setDetailGroupRow(rowselectGroup);
}
else sta->setDetailGroupRow(-1);
m->ApplyFilter();
if (!view) {
SetCurrentItem(selectRowGroup);
EnsureVisible(selectRowGroup);
}
wxString l = wxString::Format("rows %d", m->GetRowCount());
st->SetLabelText(l);
}
void MyDataViewCtrl::AddRow(wxString csvtext) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
wxDataViewItem select;
if (HasSelection()) select = GetSelection();
if (m->Prepend(csvtext)) {
if (!select.IsOk()) return;
int rowselect = m->GetRow(select);
// add visible row
int rowlast = m->GetRowCount() - 1;
if ((rowlast - 1) == rowselect) {
//m->GetItem();
select = m->GetItem(rowlast);
SetCurrentItem(select);
EnsureVisible(select);
ScrollPages(1);
}
}
}
void MyDataViewCtrl::OnEVT_DATAVIEW_SELECTION_CHANGED(wxDataViewEvent& event) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
int r = m->GetRow(event.GetItem());
if (m->getGroupFilter() && r != -1) {
Storage* sta = m->getStorage();
sta->ClearCount(r);
int a=sta->GetTotalCountGroup(r);
wxString l = wxString::Format("Total rows in group %d", a);
st->SetLabelText(l);
//EnsureVisible(event.GetItem());
}
//wxLogMessage("wxEVT_DATAVIEW_SELECTION_CHANGED, First selected Item row: %d", r);
}
void MyDataViewCtrl::ClearAllFilter() {
int colt = 0;
int col = 0;
bool all = false;
bool clear = false;
wxArrayInt fCol;
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
for (int j = 0; j < GetColumnCount(); j++) {
col = j;
clear = false;
colt = 0;
while (colt != -1) {
colt = m->testFilter(col, colt);
if (colt >= 0) {
m->DropColFilter(colt);
clear = true;
all = true;
colt++;
}
}
if (clear) {
// m->ApplyFilter();
fCol.Add(col);
// wxDataViewColumn* vc = GetColumn(col);
// vc->SetBitmap(wxNullBitmap);
}
}
if (all) {
m->ApplyFilter();
for (auto i : fCol) {
wxDataViewColumn* vc = GetColumn(i);
vc->SetBitmap(wxNullBitmap);
}
}
wxString l = wxString::Format("rows %d", m->GetRowCount());
st->SetLabelText(l);
}
void MyDataViewCtrl::AddFilterIgnore() {
int colt = 0;
int col = 0;
bool all = false;
bool clear = false;
wxArrayInt fCol;
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
Storage* st = m->getStorage();
wxString text;
for (int j = 0; j < GetColumnCount(); j++) {
col = j;
clear = false;
colt = 0;
while (colt != -1) {
colt = m->testFilter(col, colt);
if (colt >= 0) {
//m->DropColFilter(colt);
wxString expr=st->GetStringFilterExpr(colt,true);
st->addLineFilterStr(expr);
text = text + expr + "\r\n";
all = true;
colt++;
}
}
}
if (all) {
st->addLineFilterStr("");
wxMessageBox("Create load filter\r\n\r\n"+text, _("Warning"), wxICON_INFORMATION | wxOK);
}
else {
wxMessageBox("Filter empty.", _("Warning"), wxICON_INFORMATION | wxOK);
}
}
void MyDataViewCtrl::OnEVT_DATAVIEW_CONTEXT_MENU(wxCommandEvent& event) {
int id = event.GetId();
wxMenu* mi = static_cast<wxMenu*>(event.GetEventObject());
wxString label = mi->GetLabelText(id);
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
int col = (int)mi->GetClientData();
if (id > 100) {
int colt = 0;
bool clear = false;
//int pos = 0;
bool all = (label == "Clear All");
Storage* stor = m->getStorage();
wxString expr;
while (colt != -1) {
colt = m->testFilter(col, colt);
if (colt >= 0) {
expr = stor->GetStringFilterExpr(colt);
if (expr == label || all) {
m->DropColFilter(colt);
clear = true;
}
colt++;
}
}
if (clear) {
m->ApplyFilter();
if (m->testFilter(col, 0) == -1) {
wxDataViewColumn* vc = GetColumn(col);
vc->SetBitmap(wxNullBitmap);
}
}
}
else {
m->setFilter(col, label.AfterFirst(' '), 0, this);
//wxDataViewColumn* vc=GetColumn(col);
//vc->SetBitmap(m->bitmapflt);
}
wxString l = wxString::Format("rows %d", m->GetRowCount());
st->SetLabelText(l);
if (col == StorageModel::Col_User) {
//m->setFilter(col, label.AfterFirst(' '));
// wxMessageBox("You have selected Item 1", "Your selection", wxOK | wxICON_INFORMATION);
}
if (event.GetId() == 2) {
// wxMessageBox("You have selected Item 2", "Your selection", wxOK | wxICON_INFORMATION);
}
}
// ïðàâàÿ êíîïêà íà ÿ÷åéêå
void MyDataViewCtrl::OnContextMenu(wxDataViewEvent& event) {
//wxString title = m_music_model->GetTitle(event.GetItem());
//wxLogMessage("wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, Item: %s", title);
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
int row = m->GetRow(event.GetItem());
int ncol;
wxVariant vr;
if (row >= 0)
{
//int ncol = GetColumn(event.GetColumn())->GetModelColumn();
ncol = event.GetColumn();
m->GetValueByRow(vr, row, ncol);
if (ncol == 0) {
wxDataViewIconText ic;
ic << vr;
wxString str = ic.GetText();
vr = str;
}
}
int pos = GetColumnPosition(event.GetDataViewColumn());
wxString str = wxString::Format("col: %d row: %d val: %s", pos, row, vr.GetString());
int flags = 0;
if (modctrl) flags = FL_REVERSE;
m->setFilter(ncol, vr.GetString(), flags, this);
//wxDataViewColumn* vc = GetColumn(event.GetColumn());
//vc->SetBitmap(m->bitmapflt);
//wxMessageBox(str, "Your selection", wxOK | wxICON_INFORMATION);
}
auto cmp = [](std::pair<std::string, int> const& a, std::pair<std::string, int> const& b)
{
return a.second != b.second ? a.second < b.second : a.first < b.first;
};
void MyDataViewCtrl::OnEVT_DATAVIEW_COLUMN_HEADER_CLICK(wxDataViewEvent& event) {
StorageModel* m = dynamic_cast<StorageModel*>(GetModel());
int col = event.GetColumn();
if (event.GetColumn() == StorageModel::Col_User)
{
}
const int rowChanged = m->GetRow(event.GetItem());
wxMenu menu;
//menu.SetTitle(event.GetDataViewColumn()->GetTitle());
int i = 1;
int colt = m->testFilter(col, 0);
if (colt >= 0) {
wxMenu* menus = new wxMenu();
menus->SetClientData((void*)col);
Storage* stor = m->getStorage();
menus->Append(101 + i++, "Clear All");
wxString expr;
while (colt != -1) {
expr = stor->GetStringFilterExpr(colt);
menus->Append(101 + i++, expr);
colt = m->testFilter(col, colt + 1);
}
menu.AppendSubMenu(menus, "Clear filters");
}
std::set<std::pair<int, std::string>> items;
std::pair<int, std::string> v;
int cc = 0;
MyHashCount::iterator it;
for (it = m->freqValues[col].begin(); it != m->freqValues[col].end(); ++it)
{
v = std::make_pair(it->second, it->first.ToStdString());
items.emplace(v);
//ma.emplace(it->first.ToStdString(), it->second);
cc++;
}
//std::sort(items.begin(), items.end(),cmp);
cc = cc - 20;
int cc0 = 0;
for (auto const& vk : items) {
cc0++;
if (cc0 < cc) continue;
wxString key = wxString(vk.second);
int value = vk.first;
wxString str = wxString::Format("%d %s", value, key);
menu.Append(i++, str);
}
//for (it = m->freqValues[col].begin(); it != m->freqValues[col].end(); ++it)
//{
// wxString key = it->first;
// int value = it->second;
// wxString str = wxString::Format("%d %s",value,key);
// menu.Append(i++, str);
//}
menu.SetClientData((void*)col);
this->PopupMenu(&menu);
event.Skip(false);
return;
event.Skip(true);
}
void MyDataViewCtrl::OnKEY_DOWN(wxKeyEvent& event) {
if ((event.GetModifiers() & wxMOD_CONTROL) == wxMOD_CONTROL) {
modctrl = true;
}
else modctrl = false;
event.Skip(true);
}
void MyDataViewCtrl::OnMouseMove(wxMouseEvent& event) {
//event.Skip(true);
if (event.Dragging())
return;
wxClientDC dc(GetMainWindow());
PrepareDC(dc);
//wxPoint logPos(event.GetLogicalPosition(dc));
//wxPoint mc= GetMainWindow()->ScreenToClient(event.GetPosition());
wxPoint mc = event.GetPosition();
wxString position;
//position = wxString::Format("x=%d y=%d", logPos.x, logPos.y);
//wxLogMessage("Mouse pos %s", position);
wxHeaderCtrl* const header = GenericGetHeader();
int dy = 0;
wxSize sz;
if (header) {
sz = header->GetSize();
//header->Refresh();
dy = sz.GetHeight();
}
mc.y += dy;
wxDataViewItem item;
wxDataViewColumn* column;
HitTest(mc, item, column);
if (item != NULL && column != NULL)
{
StorageModel* m;
m = dynamic_cast<StorageModel*>(GetModel());
int row = m->GetRow(item);
if (row >= 0)
{
wxVariant vr;
int ncol = column->GetModelColumn();
if ((lastcol != ncol) || (lastrow != row)) {
m->GetValueByRow(vr, row, ncol);
wxSize szext = dc.GetTextExtent(vr.GetString());
int w = column->GetWidth();
if (szext.GetWidth() > w)
{
GetMainWindow()->SetToolTip(vr.GetString());
}
else
GetMainWindow()->UnsetToolTip();
lastrow = row;
lastcol = ncol;
}
}
else
{
GetMainWindow()->UnsetToolTip();
}
}
else
{
GetMainWindow()->UnsetToolTip();
}
}

583
utils/log/Storage.cpp Normal file
View file

@ -0,0 +1,583 @@
#include "pgAdmin3.h"
#include "log/Storage.h"
#include "utils/utffile.h"
#include <wx/filename.h>
#include <wx/stdpaths.h>
int Storage::getCountStore() {
return storage.size();
}
int Storage::getCountFilter() {
int cnt = frows.size();
if (cnt == 0 && !IsFilter()) cnt = getCountStore();
return cnt;
}
LineFilter Storage::getLineFilter(wxString strflt) {
LineFilter lf;
lf.col = -1;
if (strflt.IsEmpty()) {
return lf;
}
int col = wxAtoi(strflt.BeforeFirst(':'));
lf.col = col;
wxString l = strflt.AfterFirst(':');
wxString val = l.AfterFirst(' ');
int flags = 0;
int i = 0;
if (l[i] == '!') { flags = FL_REVERSE; i++; }
if (l[i] == '~') flags = flags | FL_CONTAINS;
lf.flags = flags;
lf.val = val;
return lf;
}
wxString Storage::_strwhere(int flags) {
wxString expr = "= ";
if ((flags & FL_CONTAINS) == FL_CONTAINS) expr = "~ ";
if ((flags & FL_REVERSE) == FL_REVERSE) expr = "!" + expr;
return expr;
}
wxString Storage::LineFilterToStr(LineFilter& lf) {
if (lf.col == -1) {
return "";
}
wxString expr = _strwhere(lf.flags)+lf.val;
expr = wxString::Format("%d:%s", lf.col, expr);
return expr;
}
void Storage::addLineFilterStr(wxString strflt) {
LineFilter lf = getLineFilter(strflt);
filterload.push_back(lf);
}
Storage::Storage() {
bgErr[MyConst::iconIndex::log] = wxNullColour;
bgErr[MyConst::iconIndex::war] = wxNullColour;
bgErr[MyConst::iconIndex::user] = wxColor(250, 196, 186);
bgErr[MyConst::iconIndex::error] = wxColor(220, 150, 150);
bgErr[MyConst::iconIndex::fatal] = wxColor(209, 100, 100);
bgErr[MyConst::iconIndex::panic] = wxColor(246, 10, 10);
// load filter
wxString tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxT("\\postgresql\\");
wxString f = tempDir + "filter_load.txt";
if (wxFileExists(f)) {
wxString str;
wxUtfFile file(f, wxFile::read, wxFONTENCODING_UTF8);
if (file.IsOpened()) {
file.Read(str);
file.Close();
wxStringTokenizer tk(str, "\n", wxTOKEN_RET_EMPTY_ALL);
bool end = false;
LineFilter lf;
while (tk.HasMoreTokens())
{
wxString l = tk.GetNextToken();
if (end && l.IsEmpty()) continue;
addLineFilterStr(l);
end = l.IsEmpty();
}
if (!end) addLineFilterStr("");
}
}
}
void Storage::saveFilters() {
if (filterload.size() == 0) return;
wxString tempDir = wxStandardPaths::Get().GetUserConfigDir() + wxT("\\postgresql\\");
wxString f = tempDir + "filter_load.txt";
wxUtfFile file(f, wxFile::write, wxFONTENCODING_UTF8);
if (file.IsOpened())
{
wxString text;
for (auto lf:filterload) {
wxString s = LineFilterToStr(lf);
if (!s.IsEmpty()) text.Append(s);
text.Append("\n");
}
if ((file.Write(text) == 0))
wxMessageBox(_("Query text incomplete.\nQuery contained characters that could not be converted to the local charset.\nPlease correct the data or try using UTF8 instead."));
file.Close();
}
}
void Storage::getLineToCache(int row, bool filter) {
int r = row;
if (filter && IsFilter()) row = frows[row];
m_cacheLine = storage.at(row);
m_cacheIndex = r;
}
int Storage::GetSeverityIndex(int rowvisual) {
if (rowvisual != m_cacheIndex) getLineToCache(rowvisual, true);
return m_cacheLine.icon;
}
wxColor& Storage::GetBgColorLine(int rowvisual) {
if (rowvisual != m_cacheIndex) getLineToCache(rowvisual, true);
int i = m_cacheLine.icon;
return bgErr[i];
}
void Storage::DropColFilter(int index) {
fCol.RemoveAt(index);
fVal.RemoveAt(index);
fFlags.RemoveAt(index);
// îñòàâøèåñÿ ôèëüòðû áóäóò ïðèìåíåíû ïî âñåìó storage
frows.clear();
}
wxString Storage::GetStringFilterExpr(int positionArrayFilter,bool addNumCol) {
wxString expr;
expr = _strwhere(fFlags[positionArrayFilter]);
expr = expr + fVal[positionArrayFilter];
if (addNumCol) {
expr = wxString::Format("%d:%s", fCol[positionArrayFilter], expr);
return expr;
}
if (expr.Len() > 50) expr = expr.substr(0, 50);
return expr;
}
int Storage::testFilter(MyConst::colField col, int position) {
int k = 0;
for (auto i : fCol) {
if (i == col && (position <= k)) return k;
k++;
}
return -1;
}
bool Storage::CompareFilterLine(int row, bool filter) {
int c = 0;
MyConst::colField f;
wxString valFlt;
wxString valField;
bool reverse = false;
bool rez = false;
int flags = 0;
for (auto i : fCol) {
flags = fFlags[c];
reverse = (flags & FL_REVERSE) == FL_REVERSE;
valFlt = fVal[c++];
f = (MyConst::colField) i;
valField = GetFieldStorage(row, f, filter);
if ((flags & FL_CONTAINS) == 0) rez = valField != valFlt;
else
rez = !valField.Contains(valFlt);
if (reverse) rez = !rez;
if (rez) {
return false;
}
}
// Ïîêàçûâàåì ñòðîêó òîëüêî åñëè îíà ñòàëà íîâîé ãðóïïîé
if (IsGroupFilter())
{
// åñëè ïðîâåðÿåì íå äîáàâëåííóþ ñòðîêó òî íèêàèõ ïðîâåðîê
if (filter) return true;
// â äåòàëüíîì ðåæèìå íîâûå ãðóïïû íå ïîêàçûâàåì
if (faddgroup && detailGroup != -1) return false;
// Åñëè ýòî íå äåòàëüíàÿ èíôîðìàöèÿ òî òîæå íå ïîêàçûâàåì
if (detailGroup != -1 && detailGroup == prevRow && !filter) {
// íîâàÿ ñòðîêà ïîïàäàåò â äåòàëüíóþ ãðóïïó
// ñäâèíåì âåðøèíó íà íîâóþ ñòðîêó
detailGroup = row;
return true;
}
if (!faddgroup) return false;
}
return true;
}
//íîìåð ñòðîêà èç èç îòôèëüòðîâàííûõ
void Storage::setDetailGroupRow(int rowGroup) {
if (IsGroupFilter()) {
//
if (rowGroup != -1) detailGroup = frows[rowGroup]; else detailGroup = -1;
}
else detailGroup = -1;
}
// äëÿ óêàçàííîé ñòðîêè ïðîâåðÿåì èç strage (áåç ôèëüòðà)
bool Storage::ApplyFilter(int row) {
//if (!IsFilter()) return true;
if (row != -1) {
// verify row
if (!IsFilter()) return true;
// filter enable
if (CompareFilterLine(row, false)) {
frows.push_back(row);
faddgroup = false;
return true;
}
if (detailGroup != -1) return false;
if (IsGroupFilter()) {
// ïîäìåíèòü â ôèëüòðå ñòðîê íà íîâóþ
for (int i = 0; i < frows.size(); i++) {
// òóò ïîòåíöèàëüíàÿ ïðîáëåìà ïðîèçâîäèòåëüíîñòè
if (frows[i] != prevRow) continue;
frows[i] = row;
return false;
}
}
// no visible
return false;
}
if (!IsFilter()) { frows.clear(); return true; }
bool f = false;
faddgroup = true;
if (IsGroupFilter()) {
// ïðè âêëþ÷åííîì GroupFilter ñìîòðèì òîëüêî ñòðîêè hashKeyToRow
frows.clear();
MyHashToRow::iterator it;
for (it = hashKeyToRow.begin(); it != hashKeyToRow.end(); ++it)
{
int keyhash = it->first, lastrow = it->second;
// do something useful with key and value
if (detailGroup != -1) {
if (detailGroup == lastrow)
{
int l = lastrow;
while (l != -1) {
frows.push_back(l);
if (l != m_cacheIndex) getLineToCache(l, false);
l = m_cacheLine.prevRowGroup;
}
break;
}
}
else
frows.push_back(lastrow);
//frows.push_back(keyhash);
}
sort(frows.begin(), frows.end());
}
if (frows.size() > 0) {
// íàáîð ôèëüòðà èçìåíèëñÿ ïåðåïðîâåðèì îòôèëüòðîâàííûå ñòðîêè åù¸ ðàç
std::deque<int> tmp;
for (int i = 0; i < frows.size(); i++) {
if (CompareFilterLine(i, true)) {
tmp.push_back(frows[i]);
f = true;
};
}
if (f) frows = tmp;
else frows.clear();
}
else
{
// íàáîð ôèëüòðîûâííûõ ñòðîê ïóñòîé ïðîâåðèì âñå ñòðîêè íà ñîîòâåòñòâèå ôèëüòðó
for (int i = 0; i < storage.size(); i++) {
if (CompareFilterLine(i, false)) {
frows.push_back(i);
f = true;
};
}
}
faddgroup = false;
return f;
}
int Storage::SetFilter(int col, wxString& val, int flags) {
fCol.Add(col);
fVal.Add(val);
fFlags.Add(flags);
ApplyFilter();
return frows.size();
}
wxString Storage::GetField(int row, MyConst::colField col) {
return GetFieldStorage(row, col, IsFilter());
}
wxString Storage::get_field(Line& l, MyConst::colField col) {
wxString val;
switch (col)
{
case MyConst::colField::logtime:
return l.text.substr(l.logtime.s, l.logtime.l);
break;
case MyConst::colField::loguser:
return l.text.substr(l.loguser.s, l.loguser.l);
break;
case MyConst::colField::logdb:
return l.text.substr(l.logdb.s, l.logdb.l);
break;
case MyConst::colField::logpid:
return l.text.substr(l.logpid.s, l.logpid.l);
break;
case MyConst::colField::loghost:
return l.text.substr(l.loghost.s, l.loghost.l);
break;
case MyConst::colField::logtag:
return l.text.substr(l.logtag.s, l.logtag.l);
break;
case MyConst::colField::logSessiontime:
return l.text.substr(l.logSessiontime.s, l.logSessiontime.l);
break;
case MyConst::colField::logSeverity:
return l.text.substr(l.logSeverity.s, l.logSeverity.l);
break;
case MyConst::colField::logSqlstate:
return l.text.substr(l.logSqlstate.s, l.logSqlstate.l);
break;
case MyConst::colField::logMessage:
return l.text.substr(l.logMessage.s, l.logMessage.l);
break;
case MyConst::colField::logDetail:
return l.text.substr(l.logDetail.s, l.logDetail.l);
break;
case MyConst::colField::logHint:
return l.text.substr(l.logHint.s, l.logHint.l);
break;
case MyConst::colField::logappname:
val = l.text.substr(l.logappname.s, l.logappname.l);
if (val.IsEmpty()) val = l.text.substr(l.logbtype.s, l.logbtype.l);
return val;
break;
case MyConst::colField::logbtype:
return l.text.substr(l.logbtype.s, l.logbtype.l);
break;
default:
break;
}
return "bad";
}
wxString Storage::GetFieldStorage(int row, MyConst::colField col, bool filter) {
if (row != m_cacheIndex) getLineToCache(row, filter);
return get_field(m_cacheLine, col);
}
Line Storage::getLineParse(const wxString& str, bool csv) {
Line st;
if (csv) {
CSVTokenizer tk(str);
// Get the fields from the CSV log.
wxString t;
wxString logTime = tk.GetNextToken();
//fields.Add(logTime);
st.logtime = { 0,static_cast<unsigned short int>(logTime.Len()) };
wxString logUser = tk.GetNextToken();
t = logTime;
st.loguser = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logUser.Len()) };
t += logUser;
wxString logDatabase = tk.GetNextToken();
if (logDatabase.IsEmpty()) logDatabase = GetHost();
st.logdb = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logDatabase.Len()) };
t += logDatabase;
wxString logPid = tk.GetNextToken();
st.logpid = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logPid.Len()) };
t += logPid;
wxString logSession;
wxString logCmdcount;
wxString logSegment;
wxString logHost = tk.GetNextToken(); // Postgres puts port with Hostname
// delete port
logHost = logHost.BeforeFirst(':');
st.loghost = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logHost.Len()) };
t += logHost;
logSession = tk.GetNextToken();
wxString logLineNumber = tk.GetNextToken();
wxString logTag = tk.GetNextToken();
st.logtag = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logTag.Len()) };
t += logTag;
wxString logSessiontime = tk.GetNextToken();
st.loghost = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logHost.Len()) };
t += logHost;
wxString logVXid = tk.GetNextToken();
wxString logTransaction = tk.GetNextToken();
wxString logSeverity = tk.GetNextToken();
//fields.Add(logSeverity);
st.logSeverity = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logSeverity.Len()) };
t += logSeverity;
wxString logState = tk.GetNextToken();
//fields.Add(logState);
st.logSqlstate = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logState.Len()) };
t += logState;
wxString logMessage = tk.GetNextToken();
//fields.Add(logMessage);
wxString logDetail = tk.GetNextToken();
//fields.Add(logDetail);
st.logDetail = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logDetail.Len()) };
t += logDetail;
wxString logHint = tk.GetNextToken();
//fields.Add(logHint);
wxString logQuery = tk.GetNextToken();
wxString logQuerypos = tk.GetNextToken();
wxString logContext = tk.GetNextToken();
wxString logDebug = tk.GetNextToken();
wxString logCursorpos = tk.GetNextToken();
if (!logDebug.IsEmpty() && logHint.IsEmpty())
logHint = logDebug;
st.logHint = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logHint.Len()) };
t += logHint;
st.logMessage = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logMessage.Len()) };
t += logMessage;
//location
logCursorpos = tk.GetNextToken();
wxString logApp = tk.GetNextToken();
//fields.Add(logApp);
st.logappname = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logApp.Len()) };
t += logApp;
wxString logType = tk.GetNextToken();
st.logbtype = { static_cast<unsigned short int>(t.Len()),static_cast<unsigned short int>(logType.Len()) };
t += logType;
//fields.Add(logType);
//st.logType = { t.Len(),logType.Len() };
//t += logType;
st.type = MyConst::ltype::SIMPLE_TEXT;
int i = MyConst::iconIndex::log;
if (logSeverity == "WARNING") i = MyConst::iconIndex::war;
if (logSeverity == "ERROR") i = MyConst::iconIndex::error;
if (logSeverity == "FATAL") i = MyConst::iconIndex::fatal;
if (logSeverity == "PANIC") i = MyConst::iconIndex::panic;
st.icon = i;
st.visible = 1;
st.text = t;
}
return st;
}
// ïîëó÷åíèå îáîáùåííîé êëþ÷åâîé ñòðîêè
wxString Storage::getStrGroup(wxString source) {
int i = 0;
int l = source.Length();
wxString n;
bool quote = false;
while (i < l) {
char c = source[i++];
if (c >= '0' && c <= '9' && !quote) continue;
if (c == '"') {
if ((i < l) && source[i] == c) {
n.Append(c, 2);
//n.Append(c);
i++;
continue;
}
else
quote = !quote;
}
n.Append(c);
}
return n;
}
int Storage::GetTotalCountGroup(int rowfilter) {
if (rowfilter != m_cacheIndex) getLineToCache(rowfilter, true);
MyHashToRow::const_iterator it = hashKeyTotal.find(m_cacheLine.hash);
int a = 0;
if (it != hashKeyTotal.end())
{
a = hashKeyTotal[m_cacheLine.hash];
}
if (a==0) {
int h = m_cacheLine.hash;
int lastrow=hashKeyToRow[h];
a = 0;
m_cacheIndex = -1;
while (lastrow!=-1) {
getLineToCache(lastrow, false);
lastrow = m_cacheLine.prevRowGroup;
a++;
}
hashKeyTotal[h]=a;
m_cacheIndex = -1;
}
return a;
}
void Storage::ClearCount(int rowfilter) {
if (rowfilter != m_cacheIndex) getLineToCache(rowfilter, true);
MyHashToRow::const_iterator it = hashKeyTotal.find(m_cacheLine.hash);
int cnt = hashKeyToCount[m_cacheLine.hash];
int a = 0;
if (it != hashKeyTotal.end())
{
a = hashKeyTotal[m_cacheLine.hash];
}
hashKeyTotal[m_cacheLine.hash] = a + cnt;
hashKeyToCount[m_cacheLine.hash] = 0;
}
int Storage::getCountGroup(int row) {
wxString valField = GetFieldStorage(row, MyConst::colField::logtime, false);
int h = m_cacheLine.hash;
int c = hashKeyToCount[h];
return c;
}
bool Storage::checkFilter(Line& l) {
if (filterload.size() > 0) {
wxString sf;
bool rez=true;
int last = filterload.size() - 1;
int i = 0;
for (auto fl:filterload) {
if (fl.col == -1) {
if (rez || (last==i)) break;
rez = true;
}
else {
if (rez) {
sf = get_field(l, (MyConst::colField)fl.col);
int flags = fl.flags;
bool reverse = (flags & FL_REVERSE) == FL_REVERSE;
if ((flags & FL_CONTAINS) == 0) rez = sf == fl.val;
else
rez = sf.Contains(fl.val);
if (reverse) rez = !rez;
}
}
i++;
}
return rez;
}
return false;
}
bool Storage::AddLineTextCSV(const wxString& strcsv) {
Line st = getLineParse(strcsv, true);
if (checkFilter(st)) {
rowsignore++;
return false;
}
rowsadd++;
wxString msg = st.text.substr(st.logMessage.s, st.logMessage.l);
if (st.icon > MyConst::iconIndex::war) SetErrMsgFlag(true);
wxString gstr = getStrGroup(msg);
int h = getHashString(gstr);
MyHashToRow::const_iterator it = hashKeyToRow.find(h);
prevRow = -1;
int cnt = 1;
faddgroup = true;
if (it != hashKeyToRow.end())
{
faddgroup = false;
prevRow = it->second;
cnt = hashKeyToCount[h];
cnt++;
}
int nextrow = storage.size();
st.prevRowGroup = prevRow;
st.hash = h;
hashKeyToRow[h] = nextrow;
hashKeyToCount[h] = cnt;
storage.push_back(st);
return true;
}

486
utils/log/StorageModel.cpp Normal file
View file

@ -0,0 +1,486 @@
#include "pgAdmin3.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/dataview.h"
#include "log/Storage.h"
#include "log/StorageModel.h"
// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------
#include "log/null.xpm"
#include "log/log_xpm.xpm"
#include "log/war_xpm.xpm"
#include "log/errorl_xpm.xpm"
#include "log/fatal_xpm.xpm"
#include "log/panic_xpm.xpm"
#include "log/user_xpm.xpm"
#include "log/wx_small.xpm"
// ----------------------------------------------------------------------------
// MyCustomRendererText
// ----------------------------------------------------------------------------
class MyCustomRendererText : public wxDataViewCustomRenderer
{
public:
StorageModel::cols col;
Storage* st;
int row;
int countRows = 0;
// This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRendererText(wxDataViewCellMode mode, StorageModel::cols column)
: wxDataViewCustomRenderer("string", mode, wxALIGN_LEFT)
{
EnableEllipsize(wxELLIPSIZE_END);
col = column;
st = NULL;
}
virtual bool Render(wxRect rect, wxDC* dc, int state) wxOVERRIDE
{
//dc->SetBrush( *wxLIGHT_GREY_BRUSH );
dc->SetBrush(*wxYELLOW_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(2);
wxRect orig = rect;
//dc->DrawRoundedRectangle( rect, 3 );
wxString s = m_value, t;
wxString rest;
wxString* pointer = &rest;
int x = 0, y = 0;
//dc->GetMultiLineTextExtent(m_value);
wxArrayInt arr;
wxSize h = dc->GetTextExtent("H");
dc->GetPartialTextExtents(m_value, arr);
bool ex = false;
int i = 0;
int startX = 0;
wxRect rectCol;
while (i < s.Len()) {
t = "";
x = 0;
bool inquote = false;
rectCol.x = rect.x;
rectCol.y = rect.y;
rectCol.height = h.GetHeight();
rectCol.width = 0;
while (i < s.Len())
{
if (s[i] == '\n') {
startX = arr[i];
i++;
break;
}
if (s[i] == '"') {
if (inquote) {
// çàêðûòèå êàâû÷åê
rectCol.width = arr[i] - rectCol.x;
rectCol.x = rectCol.x - startX + rect.x;
dc->DrawRoundedRectangle(rectCol, 3);
rectCol.x = arr[i];
rectCol.width = 0;
}
else
rectCol.x = arr[i];
inquote = !inquote;
}
t += s[i];
i++;
}
RenderText(t,
x, // no offset
//wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
rect,
dc,
state);
rect.y = rect.y + h.GetHeight() + 0;
rect.height = rect.height - (h.GetHeight() + 0);
}
if (countRows > 0 && col == StorageModel::cols::Col_Host) {
dc->SetBrush(*wxGREEN_BRUSH);
wxString str = wxString::Format("%d", countRows);
wxSize sz = dc->GetTextExtent(str);
sz.SetWidth(sz.GetX() + 3);
orig.SetLeft(orig.GetLeft() + (orig.GetWidth() - sz.GetWidth()));
orig.SetHeight(sz.GetHeight());
orig.SetWidth(sz.GetWidth());
dc->DrawRoundedRectangle(orig, 2);
RenderText(str,
1, // no offset
//wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
orig,
dc,
state);
}
return true;
RenderText(m_value,
0, // no offset
//wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
rect,
dc,
state);
return true;
}
virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel* WXUNUSED(model),
const wxDataViewItem& WXUNUSED(item),
unsigned int WXUNUSED(col),
const wxMouseEvent* mouseEvent) wxOVERRIDE
{
wxString position;
if (mouseEvent)
position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y);
else
position = "from keyboard";
wxLogMessage("MyCustomRendererText ActivateCell() %s", position);
return false;
}
virtual wxSize GetSize() const wxOVERRIDE
{
wxSize txtSize = GetTextExtent(m_value);
//wxSize txtSize = GetDC()->GetMultiLineTextExtent(GetMultiLineTextExtent);
//wxSize txtSize = wxDataViewRenderer::GetDC()->GetMultiLineTextExtent(m_value);
int lines = m_value.Freq('\n') + 1;
if (lines > 1) {
wxString position;
position = wxString::Format("lines %d,hieght 1 row %d full h=%d", lines, txtSize.GetHeight(), txtSize.GetHeight() * lines + 1 * lines);
// wxLogMessage("MyCustomRendererText GetSize() %s", position);
txtSize.SetHeight(txtSize.GetHeight() * lines + 1 * lines);
}
else
txtSize.SetHeight(-1);
txtSize.SetWidth(-1);
return txtSize;
//return GetView()->FromDIP(wxSize(60, 20));
}
virtual bool SetValue(const wxVariant& value) wxOVERRIDE
{
m_value = value.GetString();
if (!st) {
StorageModel* m = dynamic_cast<StorageModel*>(GetView()->GetModel());
st = m->getStorage();
}
if (st->IsGroupFilter()) {
row = st->getLastRowIndex();
countRows = st->getCountGroup(row);
}
else countRows = -1;
return true;
}
virtual bool GetValue(wxVariant& WXUNUSED(value)) const wxOVERRIDE { return true; }
#if wxUSE_ACCESSIBILITY
virtual wxString GetAccessibleDescription() const wxOVERRIDE
{
return m_value;
}
#endif // wxUSE_ACCESSIBILITY
virtual bool HasEditorCtrl() const wxOVERRIDE { return true; }
virtual wxWindow*
CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) wxOVERRIDE
{
wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value,
labelRect.GetPosition(),
labelRect.GetSize(),
wxTE_PROCESS_ENTER);
text->SetInsertionPointEnd();
return text;
}
virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) wxOVERRIDE
{
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text)
return false;
wxString sel = text->GetStringSelection();
if (sel.IsEmpty()) return false;
value = sel;
return true;
}
private:
wxString m_value;
};
// ----------------------------------------------------------------------------
// StorageModel
// ----------------------------------------------------------------------------
static int my_sort_reverse(int* v1, int* v2)
{
return *v2 - *v1;
}
static int my_sort(int* v1, int* v2)
{
return *v1 - *v2;
}
#define INITIAL_NUMBER_OF_ITEMS 0
void StorageModel::BuildColumns(MyDataViewCtrl* ctrl) {
wxDataViewColumn* const colIconText = new wxDataViewColumn
(
"Severity",
new wxDataViewIconTextRenderer(),
StorageModel::Col_ToggleIconText,
wxCOL_WIDTH_AUTOSIZE, wxALIGN_CENTER_VERTICAL
);
colmap[StorageModel::Col_ToggleIconText] = MyConst::colField::logSqlstate;
ctrl->AppendColumn(colIconText);
ctrl->AppendColumn(
new wxDataViewColumn("logTime",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_LogTime),
StorageModel::Col_LogTime,
wxCOL_WIDTH_AUTOSIZE,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_LogTime] = MyConst::colField::logtime;
ctrl->AppendColumn(
new wxDataViewColumn("UserName",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_User),
StorageModel::Col_User,
wxCOL_WIDTH_AUTOSIZE,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_User] = MyConst::colField::loguser;
ctrl->AppendColumn(
new wxDataViewColumn("DbName",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_Db),
StorageModel::Col_Db,
30,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_Db] = MyConst::colField::logdb;
ctrl->AppendColumn(
new wxDataViewColumn("Pid",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_PID),
StorageModel::Col_PID,
60,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_PID] = MyConst::colField::logpid;
ctrl->AppendColumn(
new wxDataViewColumn("Host",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_Host),
StorageModel::Col_Host,
90,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_Host] = MyConst::colField::loghost;
ctrl->AppendColumn(
new wxDataViewColumn("AppName",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_App),
StorageModel::Col_App,
100,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_App] = MyConst::colField::logappname;
ctrl->AppendColumn(
new wxDataViewColumn("Hint",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_Hint),
StorageModel::Col_Hint,
90,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_Hint] = MyConst::colField::logHint;
ctrl->AppendColumn(
new wxDataViewColumn("Detail",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_Detail),
StorageModel::Col_Detail,
60,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_Detail] = MyConst::colField::logDetail;
ctrl->AppendColumn(
new wxDataViewColumn("Message",
new MyCustomRendererText(wxDATAVIEW_CELL_EDITABLE, StorageModel::Col_Message),
StorageModel::Col_Message,
wxCOL_WIDTH_AUTOSIZE,
wxALIGN_LEFT,
wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE
));
colmap[StorageModel::Col_Message] = MyConst::colField::logMessage;
}
#include <wx/textfile.h>
#include "log/filter_xpm.xpm"
StorageModel::StorageModel(MyDataViewCtrl* view) :
wxDataViewVirtualListModel(INITIAL_NUMBER_OF_ITEMS)
{
m_view = view;
//m_icon[0] = wxIcon(null_xpm);
m_icon[MyConst::iconIndex::log] = wxIcon(log_xpm);
m_icon[MyConst::iconIndex::war] = wxIcon(war_xpm);
m_icon[MyConst::iconIndex::user] = wxIcon(user_xpm);
m_icon[MyConst::iconIndex::error] = wxIcon(errorl_xpm);
m_icon[MyConst::iconIndex::fatal] = wxIcon(fatal_xpm);
m_icon[MyConst::iconIndex::panic] = wxIcon(panic_xpm);
bitmapflt = wxBitmap((wxIcon(filter_xpm)));
store = new Storage();
}
bool StorageModel::setFilter(int col, wxString val, int flags, MyDataViewCtrl* view) {
bool r = store->SetFilter(colmap[col], val, flags);
Reset(store->getCountFilter());
if (col != -1) {
wxDataViewColumn* vc = view->GetColumn(col);
vc->SetBitmap(bitmapflt);
}
return r;
}
int StorageModel::testFilter(int col, int position = 0) {
int idx = 0;
idx = store->testFilter(colmap[col], position);
return idx;
}
void StorageModel::ApplyFilter() {
store->ApplyFilter();
Reset(store->getCountFilter());
}
void StorageModel::DropColFilter(int index) {
if (index >= 0) store->DropColFilter(index);
}
bool StorageModel::Prepend(const wxString& text)
{
//m_toggleColValues.insert(m_toggleColValues.begin(), 0);
//m_textColValues.Insert(text, 0);
bool add=store->AddLineTextCSV(text);
if (!add) return false;
int row = store->getCountStore() - 1;
wxString val = store->GetFieldStorage(row, MyConst::colField::loguser, false);
IncCountFreq(StorageModel::Col_User, val);
IncCountFreq(StorageModel::Col_Db, store->GetFieldStorage(row, MyConst::colField::logdb, false));
IncCountFreq(StorageModel::Col_Host, store->GetFieldStorage(row, MyConst::colField::loghost, false));
val = store->GetFieldStorage(row, MyConst::colField::logappname, false);
//if (val.IsEmpty()) val = store->GetFieldStorage((int)row, MyConst::colField::logbtype,false);
IncCountFreq(StorageModel::Col_App, val);
IncCountFreq(StorageModel::Col_PID, store->GetFieldStorage(row, MyConst::colField::logpid, false));
IncCountFreq(StorageModel::Col_Hint, store->GetFieldStorage(row, MyConst::colField::logHint, false));
val = store->GetFieldStorage(row, MyConst::colField::logSqlstate, false);
IncCountFreq(StorageModel::Col_ToggleIconText, val);
if (store->ApplyFilter(row)) {
RowAppended();
//RowPrepended();
return true;
}
if (store->IsGroupFilter()) {
}
return false;
}
void StorageModel::DeleteItem(const wxDataViewItem& item)
{
unsigned int row = GetRow(item);
RowDeleted(row);
}
void StorageModel::DeleteItems(const wxDataViewItemArray& items)
{
unsigned i;
wxArrayInt rows;
}
void StorageModel::AddMany()
{
Reset(GetCount() + 1000);
}
void StorageModel::GetValueByRow(wxVariant& variant,
unsigned int row, unsigned int col) const
{
MyConst::colField fldcsv = colmap[col];
wxString val;
val = store->GetField((int)row, fldcsv);
if (col == StorageModel::Col_ToggleIconText) {
int i = store->GetSeverityIndex((int)row);
variant << wxDataViewIconText(val, m_icon[i]);
return;
}
else if (col == StorageModel::Col_App) {
//if (val.IsEmpty()) val = store->GetField((int)row, MyConst::colField::logbtype);
}
else if (col == StorageModel::Col_User) {
};
variant = val;
}
bool StorageModel::GetAttrByRow(unsigned int row, unsigned int col,
wxDataViewItemAttr& attr) const
{
//attr.SetBackgroundColour(*wxLIGHT_GREY);
wxColor c = store->GetBgColorLine((int)row);
if (c.IsOk()) {
attr.SetBackgroundColour(c);
return true;
}
return false;
}
bool StorageModel::SetValueByRow(const wxVariant& variant,
unsigned int row, unsigned int col)
{
wxString flt = variant.GetString();
int flags = FL_CONTAINS;
if (flt[0] == '!') {
flags = flags | FL_REVERSE;
flt = flt.substr(1);
}
setFilter(col, flt, flags, m_view);
return false;
}

Binary file not shown.