khanat-opennel-code/code/nel/tools/misc/log_analyser/log_analyserDlg.cpp
2015-02-22 17:15:41 +01:00

1048 lines
24 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// log_analyserDlg.cpp : implementation file
//
#include "stdafx.h"
#include "log_analyser.h"
#include "log_analyserDlg.h"
//#include <nel/misc/config_file.h>
#include <fstream>
#include <algorithm>
using namespace std;
//using namespace NLMISC;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern CLog_analyserApp theApp;
CString LogDateString;
/*
* Keyboard handler (in edit box)
*/
afx_msg void CLAEdit::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
switch( nChar )
{
// Ctrl+G: Go to selected line number
case 'G':
CViewDialog *view = ((CLog_analyserDlg*)(GetParent()))->getCurrentView();
if ( view )
{
if ( (GetKeyState(VK_CONTROL) & 0x8000) != 0 )
{
// Get the selected line number
CString str;
GetWindowText(str);
int start, end;
GetSel( start, end );
str = str.Mid( start, end-start );
int lineNum = atoi( str );
if ( ! ((lineNum != 0) || (str == "0")) )
break;
// GoTo line
view->scrollTo( lineNum );
}
}
break;
}
// Transmit to Edit Box AND to main window
CEdit::OnKeyDown( nChar, nRepCnt, nFlags );
((CLog_analyserDlg*)(GetParent()))->OnKeyDown( nChar, nRepCnt, nFlags );
}
/*
* Keyboard handler (everywhere)
*/
void CLog_analyserDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CViewDialog *view = getCurrentView();
switch ( nChar )
{
// Bookmarks handling (TODO)
case VK_F2:
if ( view )
{
if ( (GetKeyState(VK_CONTROL) & 0x8000) != 0 )
view->addBookmark();
else
view->recallNextBookmark();
}
break;
// Ctrl+F, F3: Find
case VK_F3:
case 'F':
if ( view )
{
if ( (nChar==VK_F3) || ((GetKeyState(VK_CONTROL) & 0x8000) != 0) )
view->OnButtonFind();
}
break;
// Ctrl+L: Display back the file list
case 'L':
if ( (GetKeyState(VK_CONTROL) & 0x8000) != 0 )
{
displayFileList();
}
break;
}
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
/////////////////////////////////////////////////////////////////////////////
// CLog_analyserDlg dialog
CLog_analyserDlg::CLog_analyserDlg(CWnd* pParent /*=NULL*/)
: CDialog(CLog_analyserDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CLog_analyserDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CLog_analyserDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CLog_analyserDlg)
DDX_Control(pDX, IDC_SCROLLBAR1, m_ScrollBar);
DDX_Control(pDX, IDC_EDIT1, m_Edit);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CLog_analyserDlg, CDialog)
//{{AFX_MSG_MAP(CLog_analyserDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_AddView, OnAddView)
ON_BN_CLICKED(IDC_ADDTRACEVIEW, OnAddtraceview)
ON_BN_CLICKED(IDC_ComputeTraces, OnComputeTraces)
ON_WM_VSCROLL()
ON_BN_CLICKED(IDC_Reset, OnReset)
ON_WM_SIZE()
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_HelpBtn, OnHelpBtn)
ON_WM_LBUTTONUP()
ON_WM_DROPFILES()
ON_BN_CLICKED(IDC_DispLineHeaders, OnDispLineHeaders)
ON_BN_CLICKED(IDC_Analyse, OnAnalyse)
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CLAEdit, CEdit)
//{{AFX_MSG_MAP(CLAEdit)
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLog_analyserDlg message handlers
BOOL CLog_analyserDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CurrentView = NULL;
Trace = false;
ResizeViewInProgress = -1;
((CButton*)GetDlgItem( IDC_CheckSessions ))->SetCheck( 1 );
((CButton*)GetDlgItem( IDC_DispLineHeaders ))->SetCheck( 1 );
((CButton*)GetDlgItem( IDC_DetectCorruptedLines ))->SetCheck( 1 );
/*try
{
CConfigFile cf;
cf.load( "log_analyser.cfg" );
LogDateString = cf.getVar( "LogDateString" ).asString().c_str();
}
catch ( EConfigFile& )
{*/
LogDateString = "Log Starting [";
AnalyseFunc = NULL;
m_Edit.SetLimitText( ~0 );
//}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Add files given in command-line
string cmdLine = string(theApp.m_lpCmdLine);
vector<CString> v;
/*int pos = cmdLine.find_first_of(' '); // TODO: handle "" with blank characters
while ( pos != string::npos )
{
v.push_back( cmdLine.substr( 0, pos ).c_str() );
cmdLine = cmdLine.substr( pos );
pos = cmdLine.find_first_of(' ');
}*/
if ( ! cmdLine.empty() )
{
v.push_back( cmdLine.c_str() );
addView( v );
}
loadPluginConfiguration();
DragAcceptFiles( true );
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CLog_analyserDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLog_analyserDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
string getFileExtension (const string &filename)
{
string::size_type pos = filename.find_last_of ('.');
if (pos == string::npos)
return "";
else
return filename.substr (pos + 1);
}
/*
* Open in the same view
*/
void CLog_analyserDlg::OnDropFiles( HDROP hDropInfo )
{
UINT nbFiles = DragQueryFile( hDropInfo, 0xFFFFFFFF, NULL, 0 );
vector<CString> v;
for ( UINT i=0; i!=nbFiles; ++i )
{
CString filename;
DragQueryFile( hDropInfo, i, filename.GetBufferSetLength( 200 ), 200 );
// Plug-in DLL or log file
if ( getFileExtension( string(filename) ) == "dll" )
{
if ( addPlugIn( string(filename) ) )
AfxMessageBox( CString("Plugin added: ") + filename );
else
AfxMessageBox( CString("Plugin already registered: ") + filename );
}
else
{
v.push_back( filename );
}
}
if ( ! v.empty() )
addView( v );
}
/*
*
*/
bool CLog_analyserDlg::addPlugIn( const std::string& dllName )
{
int i = 0;
char pluginN [10] = "Plugin0";
CString pn = theApp.GetProfileString( _T(""), _T(pluginN) );
while ( ! pn.IsEmpty() )
{
if ( string(pn) == dllName )
return false; // already registered
++i;
smprintf( pluginN, 10, "Plugin%d", i );
pn = theApp.GetProfileString( _T(""), _T(pluginN) );
}
theApp.WriteProfileString( _T(""), _T(pluginN), dllName.c_str() );
Plugins.push_back( dllName.c_str() );
return true;
}
/*
*
*/
void CLog_analyserDlg::loadPluginConfiguration()
{
// Read from the registry
free( (void*)theApp.m_pszRegistryKey );
theApp.m_pszRegistryKey = _tcsdup( _T("Nevrax") );
CString pn = theApp.GetProfileString( _T(""), _T("Plugin0") );
char pluginN [10];
int i = 0;
while ( ! pn.IsEmpty() )
{
Plugins.push_back( pn );
++i;
smprintf( pluginN, 10, "Plugin%d", i );
pn = theApp.GetProfileString( _T(""), _T(pluginN) );
}
}
/*
*
*/
bool isNumberChar( char c )
{
return (c >= '0') && (c <= '9');
}
/*
*
*/
void CLog_analyserDlg::OnAddView()
{
vector<CString> v;
addView( v );
}
/*
*
*/
void CLog_analyserDlg::addView( std::vector<CString>& pathNames )
{
if ( pathNames.empty() )
{
CFileDialog openDialog( true, NULL, "log.log", OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT, "Log files (*.log)|*.log|All files|*.*||", this );
CString filenameList;
openDialog.m_ofn.lpstrFile = filenameList.GetBufferSetLength( 8192 );
openDialog.m_ofn.nMaxFile = 8192;
if ( openDialog.DoModal() == IDOK )
{
CWaitCursor wc;
// Get the selected filenames
CString pathName;
POSITION it = openDialog.GetStartPosition();
while ( it != NULL )
{
pathNames.push_back( openDialog.GetNextPathName( it ) );
}
if ( pathNames.empty() )
return;
}
else
return;
}
unsigned int i;
if ( pathNames.size() > 1 )
{
// Sort the filenames
for ( i=0; i!=pathNames.size(); ++i )
{
// Ensure that a log file without number comes *after* the ones with a number
string name = string(pathNames[i]);
string::size_type dotpos = name.find_last_of('.');
if ( (dotpos!=string::npos) && (dotpos > 2) )
{
if ( ! (isNumberChar(name[dotpos-1]) && isNumberChar(name[dotpos-2]) && isNumberChar(name[dotpos-3])) )
{
name = name.substr( 0, dotpos ) + "ZZZ" + name.substr( dotpos );
pathNames[i] = name.c_str();
}
}
}
sort( pathNames.begin(), pathNames.end() );
for ( i=0; i!=pathNames.size(); ++i )
{
// Set the original names back
string name = pathNames[i];
string::size_type tokenpos = name.find( "ZZZ." );
if ( tokenpos != string::npos )
{
name = name.substr( 0, tokenpos ) + name.substr( tokenpos + 3 );
pathNames[i] = name.c_str();
}
}
}
// Display the filenames
string names;
if ( isLogSeriesEnabled() )
names += "Loading series corresponding to :\r\n";
else
names += "Loading files:\r\n";
for ( i=0; i!=pathNames.size(); ++i )
names += string(pathNames[i]) + "\r\n";
displayCurrentLine( names.c_str() );
// Add view and browse sessions if needed
CViewDialog *view = onAddCommon( pathNames );
// Set filters
FilterDialog.Trace = false;
if ( FilterDialog.DoModal() == IDOK )
{
view->setFilters( FilterDialog.getPosFilter(), FilterDialog.getNegFilter() );
// Load file
view->reload();
}
}
/*
*
*/
void CLog_analyserDlg::OnAddtraceview()
{
CFileDialog openDialog( true, NULL, "log.log", OFN_HIDEREADONLY, "Log files (*.log)|*.log|All files|*.*||", this );
if ( openDialog.DoModal() == IDOK )
{
vector<CString> pathNames;
pathNames.push_back( openDialog.GetPathName() );
CViewDialog *view = onAddCommon( pathNames );
// Set filters
FilterDialog.Trace = true;
if ( FilterDialog.DoModal() == IDOK )
{
view->setFilters( FilterDialog.getPosFilter(), FilterDialog.getNegFilter() );
}
// Load file
view->reloadTrace();
}
}
/*
* Precondition: !filenames.empty()
*/
CViewDialog *CLog_analyserDlg::onAddCommon( const vector<CString>& filenames )
{
CWaitCursor wc;
// Create view
CViewDialog *view = new CViewDialog();
view->Create( IDD_View, this );
view->Index = (int)Views.size();
RECT editRect;
m_Edit.GetWindowRect( &editRect );
ScreenToClient( &editRect );
RECT parentRect;
GetClientRect( &parentRect );
Views.push_back( view );
int i, w = 0;
for ( i=0; i!=(int)Views.size(); ++i )
{
Views[i]->WidthR = 1.0f/(float)Views.size();
Views[i]->resizeView( (int)Views.size(), editRect.bottom+10, w );
w += (int)(Views[i]->WidthR*(parentRect.right-32));
}
view->ShowWindow( SW_SHOW );
// Set params
if ( filenames.size() == 1 )
{
// One file or a whole log series
view->Seriesname = filenames.front();
getLogSeries( filenames.front(), view->Filenames );
}
else
{
// Multiple files
view->Seriesname = filenames.front() + "...";
view->Filenames = filenames;
}
view->LogSessionStartDate = "";
LogSessionsDialog.clear();
if ( ((CButton*)GetDlgItem( IDC_CheckSessions ))->GetCheck() == 1 )
{
LogSessionsDialog.addLogSession( "Beginning" );
int nbsessions = 0;
for ( i=0; i!=(int)(view->Filenames.size()); ++i )
{
// Scan file for log sessions dates
ifstream ifs( view->Filenames[i] );
if ( ! ifs.fail() )
{
char line [1024];
while ( ! ifs.eof() )
{
ifs.getline( line, 1024 );
if ( strstr( line, LogDateString ) != NULL )
{
LogSessionsDialog.addLogSession( line );
++nbsessions;
}
}
}
}
// Heuristic to bypass the session choice if not needed
bool needToChooseSession;
switch ( nbsessions )
{
case 0:
// No 'Log Starting' in the file(s) => no choice needed
needToChooseSession = false;
break;
case 1:
{
// 1 'Log Starting' => no choice if it's at the beginning (1st line, or 2nd line with blank 1st)
ifstream ifs( view->Filenames[0] ); // 1 session => ! Filename.empty()
char line [1024];
ifs.getline( line, 1024 );
if ( ! ifs.fail() )
{
if ( strstr( line, LogDateString ) != NULL )
needToChooseSession = false;
else if ( string(line).empty() )
{
if ( ! ifs.fail() )
{
ifs.getline( line, 1024 );
needToChooseSession = (strstr( line, LogDateString ) == NULL);
}
else
needToChooseSession = true;
}
}
else
needToChooseSession = true;
}
break;
default:
// Several 'Log Starting' => always choice
needToChooseSession = true;
}
// Let the user choose the session (if needed)
if ( (needToChooseSession) && (LogSessionsDialog.DoModal() == IDOK) )
{
view->LogSessionStartDate = LogSessionsDialog.getStartDate();
}
}
setCurrentView( view->Index );
return view;
}
/*
* Code from NeL misc
*/
int smprintf( char *buffer, size_t count, const char *format, ... )
{
int ret;
va_list args;
va_start( args, format );
ret = vsnprintf( buffer, count, format, args );
if ( ret == -1 )
{
buffer[count-1] = '\0';
}
va_end( args );
return( ret );
}
/*
*
*/
void CLog_analyserDlg::getLogSeries( const CString& filenameStr, std::vector<CString>& filenameList )
{
if ( isLogSeriesEnabled() )
{
string filename = filenameStr;
unsigned int dotpos = filename.find_last_of ('.');
if ( dotpos != string::npos )
{
string start = filename.substr( 0, dotpos );
string end = filename.substr( dotpos );
char numchar [4];
unsigned int i = 0;
bool anymore = true;
while ( anymore )
{
// If filename is my_service.log, try my_service001.log..my_service999.log
string npath = start;
smprintf( numchar, 4, "%03d", i );
npath += numchar + end;
if ( ! ! fstream( npath.c_str(), ios::in ) )
{
// File exists => add it
filenameList.push_back( npath.c_str() );
if ( i == 999 )
{
filenameList.push_back( "<Too many log files in the series>" );
anymore = false;
}
++i;
}
else
{
// No more files
anymore = false;
}
}
}
}
// At last, add the filename
filenameList.push_back( filenameStr );
}
/*
*
*/
void CLog_analyserDlg::displayCurrentLine( const CString& line )
{
m_Edit.SetSel( 0, -1 );
m_Edit.Clear();
m_Edit.ReplaceSel( line, true );
}
/*
*
*/
bool CLog_analyserDlg::selectText( int lineNum, int colNum, int length )
{
int index = m_Edit.LineIndex( lineNum );
if ( index != -1 )
{
index += colNum;
m_Edit.SetSel( index, index + length );
m_Edit.SetFocus();
return true;
}
else
return false;
}
/*
*
*/
void CLog_analyserDlg::displayFileList()
{
if ( ! MemorizedFileList.IsEmpty() )
{
displayCurrentLine( MemorizedFileList );
}
}
/*
*
*/
void CLog_analyserDlg::insertTraceLine( int index, char *traceLine )
{
/*CString s0;
s0.Format( "%s", traceLine );
MessageBox( s0 );*/
char *line = strchr( traceLine, ':' );
char scycle [10];
strncpy( scycle, traceLine, line-traceLine );
int cycle = atoi(scycle);
TStampedLine stampedLine;
stampedLine.Index = index;
stampedLine.Line = CString(traceLine);
TraceMap.insert( make_pair( cycle, stampedLine ) );
/*CString s;
s.Format( "%d - %s", cycle, line );
MessageBox( s );*/
}
/*
*
*/
void CLog_analyserDlg::OnComputeTraces()
{
CWaitCursor wc;
if ( Views.empty() )
return;
Trace = true;
int j;
for ( j=0; j!=(int)Views.size(); ++j )
{
Views[j]->clear();
Views[j]->setRedraw( false );
}
multimap<int, TStampedLine>::iterator itm = TraceMap.begin(), itmU, itmC;
while ( itm != TraceMap.end() )
{
// Fill all the views for one cycle
itmU = TraceMap.upper_bound( (*itm).first );
for ( itmC=itm; itmC!=itmU; ++itmC )
{
TStampedLine& stampedLine = (*itmC).second;
Views[stampedLine.Index]->addLine( stampedLine.Line );
}
// Get the number of lines of the most filled view
int i, maxNbLines=0;
for ( i=0; i!=(int)Views.size(); ++i )
{
int vnb = Views[i]->getNbLines();
if ( vnb > maxNbLines )
{
maxNbLines = vnb;
}
}
// Fill the gaps with blank lines
for ( i=0; i!=(int)Views.size(); ++i )
{
Views[i]->fillGaps( maxNbLines );
}
itm = itmU;
}
for ( j=0; j!=(int)Views.size(); ++j )
{
Views[j]->commitAddedLines();
Views[j]->setRedraw( true );
}
m_ScrollBar.SetScrollRange( 0, Views[0]->getNbLines()-Views[0]->getNbVisibleLines() );
m_ScrollBar.ShowWindow( SW_SHOW );
}
/*
*
*/
void CLog_analyserDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if ( Trace )
{
int index;
switch ( nSBCode )
{
case SB_TOP : index = 0; break;
case SB_BOTTOM : index = Views[0]->getNbLines()-1; break;
case SB_ENDSCROLL : index = -1; break;
case SB_LINEDOWN : index = Views[0]->getScrollIndex()+1; break;
case SB_LINEUP : index = Views[0]->getScrollIndex()-1; break;
case SB_PAGEDOWN : index = Views[0]->getScrollIndex()+Views[0]->getNbVisibleLines(); break;
case SB_PAGEUP : index = Views[0]->getScrollIndex()-Views[0]->getNbVisibleLines(); break;
case SB_THUMBPOSITION :
case SB_THUMBTRACK :
index = nPos;
break;
}
if ( index != -1 )
{
// Scroll the views
for ( int i=0; i!=(int)Views.size(); ++i )
{
Views[i]->scrollTo( index );
}
pScrollBar->SetScrollPos( index );
}
}
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}
/*
*
*/
void CLog_analyserDlg::OnReset()
{
m_Edit.SetSel( 0, -1 );
m_Edit.Clear();
vector<CViewDialog*>::iterator iv;
for ( iv=Views.begin(); iv!=Views.end(); ++iv )
{
(*iv)->DestroyWindow();
delete (*iv);
}
Views.clear();
CurrentView = NULL;
Trace = false;
TraceMap.clear();
m_ScrollBar.ShowWindow( SW_HIDE );
}
/*
*
*/
void CLog_analyserDlg::OnDispLineHeaders()
{
vector<CViewDialog*>::iterator iv;
for ( iv=Views.begin(); iv!=Views.end(); ++iv )
{
(*iv)->Invalidate();
}
}
/*
*
*/
void CLog_analyserDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if ( ::IsWindow(m_Edit) )
{
RECT cltRect, editRect, sbRect;
GetClientRect( &cltRect ),
m_Edit.GetWindowRect( &editRect );
m_ScrollBar.GetWindowRect( &sbRect );
ScreenToClient( &editRect );
ScreenToClient( &sbRect );
editRect.right = cltRect.right-16;
sbRect.right += cltRect.right-28-sbRect.left;
sbRect.left = cltRect.right-28;
sbRect.bottom = cltRect.bottom-12;
m_Edit.MoveWindow( &editRect );
m_ScrollBar.MoveWindow( &sbRect );
resizeViews();
}
}
/*
*
*/
void CLog_analyserDlg::resizeViews()
{
RECT editRect;
m_Edit.GetWindowRect( &editRect );
ScreenToClient( &editRect );
RECT parentRect;
GetClientRect( &parentRect );
int i, w = 0;
for ( i=0; i!=(int)Views.size(); ++i )
{
Views[i]->resizeView( (int)Views.size(), editRect.bottom+10, w );
w += (int)(Views[i]->WidthR*(parentRect.right-32));
}
}
/*
*
*/
void CLog_analyserDlg::beginResizeView( int index )
{
ResizeViewInProgress = index;
SetCursor( theApp.LoadStandardCursor( IDC_SIZEWE ) );
SetCapture();
}
/*
*
*/
void CLog_analyserDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
if ( ResizeViewInProgress != -1 )
{
if ( ResizeViewInProgress > 0 )
{
RECT viewRect, appClientRect;
Views[ResizeViewInProgress]->GetWindowRect( &viewRect );
ScreenToClient( &viewRect );
GetClientRect( &appClientRect );
if ( point.x < 0 )
point.x = 10;
int deltaPosX = point.x - viewRect.left;
float deltaR = (float)deltaPosX / (float)(appClientRect.right-32);
if ( -deltaR > Views[ResizeViewInProgress-1]->WidthR )
deltaR = -Views[ResizeViewInProgress-1]->WidthR + 0.01f;
if ( deltaR > Views[ResizeViewInProgress]->WidthR )
deltaR = Views[ResizeViewInProgress]->WidthR - 0.01f;
Views[ResizeViewInProgress-1]->WidthR += deltaR;
Views[ResizeViewInProgress]->WidthR -= deltaR;
}
ResizeViewInProgress = -1;
ReleaseCapture();
SetCursor( theApp.LoadStandardCursor( IDC_ARROW ) );
resizeViews();
}
CDialog::OnLButtonUp(nFlags, point);
}
/*
*
*/
void CLog_analyserDlg::OnDestroy()
{
OnReset();
CDialog::OnDestroy();
}
/*
*
*/
void CLog_analyserDlg::OnHelpBtn()
{
CString s = "NeL Log Analyser v1.5.0\n(c) 2002-2003 Nevrax\n\n";
s += "Simple Mode: open one or more log files using the button 'Add View...'.\n";
s += "You can make a multiple selection, then the files will be sorted by log order.\n";
s += "If the file(s) being opened contain(s) several log sessions, you can choose one or\n";
s += "choose to display all sessions, if the checkbox 'Browse Log Sessions' is enabled. If the\n";
s += "checkbox 'Browse All File Series' is checked and you choose my_service.log, all log\n";
s += "files of the series beginning with my_service000.log up to the biggest number found,\n";
s += "and ending with my_service.log, will be opened in the same view. If 'Detect corrupted\n";
s += "files' is checked, the possibly malformed lines will be reported.\n";
s += "You can add some positive/negative filters. Finally, you may click a log line to display\n";
s += "it in its entirety in the top field.\n";
s += "Another way to open a file is to pass its filename as an argument. An alternative way to\n";
s += "open one or more files is to drag & drop them onto the main window!.\n";
s += "To actualize a file (which may have changed if a program is still writing into it), just\n";
s += "click 'Filter...' and OK.\n";
s += "Resizing a view is done by dragging its left border.\n";
s += "Line bookmarks: set/remove = Ctrl+F2, recall = F2. They are kept when changing the filter.\n\n";
s += "Trace Mode: open several log files in Trace Format (see below) using the button\n";
s += "'Add Trace View...', you can limit the lines loaded to the ones containing a\n";
s += "specific service shortname (see below). Then click the button 'Compute Traces'\n";
s += "to display the matching lines. The lines are sorted using their gamecycle and\n";
s += "blank lines are filled so that different parallel views have the same timeline.\n";
s += "Use the right scrollbar to scroll all the views at the same time.\n";
s += "The logs in Trace Format should contains some lines that have a substring sTRACE:n:\n";
s += "where s is an optional service name (e.g. FS) and n is the gamecycle of the action\n";
s += "(an integer).\n\n";
s += "Plug-in system: You can provide DLLs to perform some processing or analysis on the current\n";
s += "view. To register a new plug-in, drag-n-drop the DLL on the main window of the Log Analyser.\n";
s += "To unregister a plug-in, see HKEY_CURRENT_USER\\Software\\Nevrax\\log_analyser.INI in the\n";
s += "registry.";
MessageBox( s );
}
/*
* Plug-in activation
*/
void CLog_analyserDlg::OnAnalyse()
{
PlugInSelectorDialog.setPluginList( Plugins );
if ( PlugInSelectorDialog.DoModal() == IDOK )
{
if ( Views.empty() )
{
AfxMessageBox( "This plug-in needs to be applied on the first open view" );
return;
}
if ( ! PlugInSelectorDialog.AnalyseFunc )
{
AfxMessageBox( "Could not load function doAnalyse in dll" );
return;
}
// Call the plug-in function and get results
string resstr, logstr;
PlugInSelectorDialog.AnalyseFunc( *(const std::vector<const char *>*)(void*)&(getCurrentView()->Buffer), resstr, logstr );
if ( ! logstr.empty() )
{
vector<CString> pl;
pl.push_back( "Analyse log" );
onAddCommon( pl );
Views.back()->addText( logstr.c_str() );
Views.back()->commitAddedLines();
}
displayCurrentLine( resstr.c_str() );
// Debug checks
int nStartChar, nEndChar;
m_Edit.GetSel( nStartChar, nEndChar );
if ( nEndChar != (int)resstr.size() )
{
CString s;
s.Format( "Error: plug-in returned %u characters, only %d displayed", resstr.size(), nEndChar+1 );
AfxMessageBox( s );
}
}
}