1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00
Files
mariadb-columnstore-engine/utils/winport/WinSyslog.cpp
2022-02-11 12:24:40 +00:00

324 lines
9.1 KiB
C++

/* Copyright (C) 2014 InfiniDB, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2 of
the License.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA. */
#include <time.h>
#include <string>
#include <sys/types.h>
#include <map>
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
namespace fs = boost::filesystem;
using namespace std;
#include "unistd.h"
#include "stdint.h"
#include "sys/time.h"
#include "syslog.h"
#include "idbregistry.h"
#include "WinSysLog.h"
// WinSyslog class encapsulates what substitutes for the syslog in the
// InfiniDB Windows version.
// Singleton instance
WinSyslog* WinSyslog::fpSysLog = WinSyslog::instance();
// For archive coordination.
// We have two mutexes. Only one will be used. We want to use the inter-process
// mutex name fhMutex. If creation succeeds, we set fbGoodIPMutex so the code knows
// it's good. If we fail, fMutex (Only this processes threads) will be used - it's
// better than nothing.
boost::mutex WinSyslog::fMutex; // Failsafe Intra process mutex
HANDLE WinSyslog::fhMutex; // Interprocess named mutex
bool WinSyslog::fbGoodIPMutex = false;
#define WINSYSLOG_MUTEX_NAME "WinSysLogMutex" // For the Windows Named Mutex
// Constructor, wherein we initialize everything
WinSyslog::WinSyslog() : fLastArchiveTime(0), fLastArchiveDay(0), fLogLineheaderSize(0)
{
fLogDirName = IDBreadRegistry("") + "\\log";
fLogFileName = IDBreadRegistry("") + "\\log\\InfiniDBLog.txt";
fTimeFileName = IDBreadRegistry("") + "\\log\\InfiniDBLastArchive.dat";
// Set up a buffer with all the line header except the timestamp.
// Leave room in the front for the timestamp.
// This header will approximate the linux standard syslog log header.
memset(fLogLineHeader, 0, LOG_BUF_SIZE);
char* pEnd = fLogLineHeader + LOG_BUF_SIZE;
char* pHeader = fLogLineHeader + TIMESTAMP_SIZE;
char exePath[LOG_BUF_SIZE];
DWORD nameSize = (DWORD)(pEnd - pHeader);
GetComputerName(pHeader, &nameSize);
pHeader += nameSize;
*pHeader++ = ' ';
GetModuleFileName(0, exePath, LOG_BUF_SIZE);
_splitpath_s(exePath, NULL, 0, NULL, 0, pHeader, pEnd - pHeader, NULL, 0);
pHeader += strlen(pHeader);
;
*pHeader++ = '[';
int pid = GetCurrentProcessId();
snprintf(pHeader, pEnd - pHeader, "%d", pid);
pHeader += strlen(pHeader);
;
*pHeader++ = ']';
*pHeader++ = ':';
fLogLineheaderSize = (int)(pHeader - fLogLineHeader);
// Set up the date we compare against for archiving.
// This will either be today (if fTimeFileName doesn't exist)
// or the the date stored in fTimeFileName.
time_t now = time(0);
FILE* lf;
lf = fopen(fTimeFileName.c_str(), "r+b");
if (lf == NULL) // First time archiving.
{
fLastArchiveTime = now;
// Persist the timestamp of last archive.
lf = fopen(fTimeFileName.c_str(), "w+b");
if (lf != 0)
{
fwrite(&fLastArchiveTime, sizeof(time_t), 1, lf);
fclose(lf);
}
}
else
{
fread(&fLastArchiveTime, sizeof(time_t), 1, lf);
fclose(lf);
}
struct tm lasttm;
localtime_s(&lasttm, &fLastArchiveTime);
fLastArchiveDay = lasttm.tm_yday;
// Create an interprocess mutex to coordinate the archiving function
fhMutex = CreateMutex(NULL, FALSE, WINSYSLOG_MUTEX_NAME);
if (fhMutex == NULL)
{
syslog(LOG_ERR, "WinSyslog CreateMutex error: %d\n", GetLastError());
}
else
{
fbGoodIPMutex = true;
}
}
WinSyslog::~WinSyslog()
{
CloseHandle(fhMutex);
}
// Log() is called by ::syslog() to actually write stuff to the file
int WinSyslog::Log(int priority, const char* format, va_list& args)
{
struct tm nowtm;
FILE* f;
time_t now = time(0);
localtime_s(&nowtm, &now);
// If now isn't the same day as the last archive date, archve.
if (nowtm.tm_yday != fLastArchiveDay)
{
Archive(nowtm);
}
// Log the line.
strftime(fLogLineHeader, TIMESTAMP_SIZE, "%b %d %H:%M:%S", &nowtm);
fLogLineHeader[TIMESTAMP_SIZE - 1] = ' ';
f = fopen(fLogFileName.c_str(), "a+");
if (f == 0)
return -1;
fwrite(fLogLineHeader, 1, fLogLineheaderSize, f);
vfprintf(f, format, args);
fwrite("\n", 1, 1, f);
fclose(f);
return 0;
}
// Archive() is called by Log() when the date changes
// Here we rename our log file to an archive name and delete
// any old files.
void WinSyslog::Archive(const tm& nowtm)
{
struct tm yesterdaytm;
bool bArchive = true;
time_t writeTime;
char ctimebuf[TIME_BUF_SIZE] = {0};
DWORD dwWaitResult;
char* szError = "";
// Lock it so we don't have two threads archiving.
if (fbGoodIPMutex)
{
// We have a good interprocess mutex. Try for one second to
// lock it. Abandon trying after a second. Something's stuck.
dwWaitResult = WaitForSingleObject(fhMutex, 1000);
switch (dwWaitResult)
{
// The thread got ownership of the mutex. Continue on.
case WAIT_OBJECT_0: break;
// The thread got ownership of an abandoned mutex, most likely because
// some process crashed in the middle of archiving. Continue on.
case WAIT_ABANDONED: break;
// We timed out. Something's not right. Don't archive.
case WAIT_TIMEOUT:
szError = "WinSyslog::Archive WAIT_TIMEOUT";
bArchive = false;
break;
// Horrible failure. Don't archive
case WAIT_FAILED:
szError = "WinSyslog::Archive WAIT_FAILED";
bArchive = false;
break;
}
}
else
{
boost::mutex::scoped_lock lock(fMutex);
}
try
{
// Check to see if any other thread or process already archived.
// If the value in fTimeFileName is different than our saved value
// Then somebody else beat us to it. Don't do it again.
time_t storedLastArchiveTime;
FILE* lf;
lf = fopen(fTimeFileName.c_str(), "r+b");
if (lf != NULL)
{
fread(&storedLastArchiveTime, sizeof(time_t), 1, lf);
fclose(lf);
if (storedLastArchiveTime != fLastArchiveTime)
{
bArchive = false;
}
}
else
{
// If we had something go wrong earlier, and then failed to read the Time file,
// this should prevent retrying to archive every log line.
storedLastArchiveTime = fLastArchiveTime = time(NULL);
}
if (bArchive == false)
{
// Something's not right. Try to set the internals so we don't attempt
// archiving again.
fLastArchiveTime = storedLastArchiveTime;
fLastArchiveDay = nowtm.tm_yday;
if (szError)
{
syslog(LOG_ERR, szError);
}
return;
}
// Get last archive date (usually yesterday)
yesterdaytm = nowtm;
yesterdaytm.tm_mday -= 1; // May be 0. mktime() adjusts accordingly.
time_t yesterday = mktime(&yesterdaytm);
localtime_s(&yesterdaytm, &yesterday);
string archiveFileName = fLogFileName;
size_t tail = archiveFileName.find(".txt");
archiveFileName.erase(tail, 4);
strftime(ctimebuf, TIME_BUF_SIZE, "-%Y-%m-%d.txt", &yesterdaytm);
archiveFileName += ctimebuf;
rename(fLogFileName.c_str(), archiveFileName.c_str());
// Persist the timestamp of last archive. In case of reboot, we
// can still archive properly
lf = fopen(fTimeFileName.c_str(), "w+b");
if (lf != 0)
{
fLastArchiveTime = time(0);
fwrite(&fLastArchiveTime, sizeof(time_t), 1, lf);
fclose(lf);
}
fLastArchiveDay = nowtm.tm_yday;
// Get the dates of all archive files.
fs::path sourceDir(fLogDirName);
fs::directory_iterator iter(sourceDir);
fs::directory_iterator end_iter;
std::multimap<std::time_t, fs::path> fileSet; // Stays sorted
while (iter != end_iter)
{
fs::path archiveFile = *iter;
if (fs::is_regular_file(archiveFile))
{
if (archiveFile.extension() == ".txt")
{
writeTime = fs::last_write_time(archiveFile);
fileSet.insert(make_pair(writeTime, archiveFile));
}
}
++iter;
}
// Delete anything past the first seven newest files
typedef std::multimap<std::time_t, fs::path>::iterator PATH_ITER;
PATH_ITER fileIter = fileSet.begin();
PATH_ITER fileIterEnd = fileSet.end();
std::reverse_iterator<PATH_ITER> revEnd(fileIter);
std::reverse_iterator<PATH_ITER> revIter(fileIterEnd);
int cnt = 0;
for (; revIter != revEnd; ++revIter)
{
if (cnt++ < 7)
continue;
_wunlink(revIter->second.c_str());
}
}
catch (exception& e)
{
syslog(LOG_ERR, "WinSyslog::Archive Exception %s", e.what());
}
catch (...)
{
syslog(LOG_ERR, "WinSyslog::Archive Exception ...");
}
if (fbGoodIPMutex)
{
if (!ReleaseMutex(fhMutex))
syslog(LOG_ERR, "WinSyslog::Archive ReleaseMutex failed %d", GetLastError());
}
}