/*##############################################################################
Copyright (C) 2011 HPCC Systems.
All rights reserved. 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 .
############################################################################## */
#include "platform.h"
#include
#include "stdio.h"
#include "jlog.hpp"
#include "jlog.ipp"
#include "jmutex.hpp"
#include "jarray.hpp"
#include "jsocket.hpp"
#include "jmisc.hpp"
#define MSGCOMP_NUMBER 1000
#define FILE_LOG_ENABLES_QUEUEUING
#ifndef _WIN32
#define AUDIT_DATA_LOG_TEMPLATE "/var/log/seisint/log_data_XXXXXX"
#endif
// Time, in nanoseconds, after which the clock field loops --- 3600000000000ns = 1hr
#define CLOCK_LOOP_NANOSECONDS I64C(3600000000000)
// LogMsgSysInfo
static FILE *getNullHandle()
{
#ifdef _WIN32
return fopen("nul","w");
#else
return fopen("/dev/null","w");
#endif
}
LogMsgSysInfo::LogMsgSysInfo(LogMsgId _id, unsigned port, LogMsgSessionId session)
{
id = _id;
time(&timeStarted);
processID = GetCurrentProcessId();
threadID = threadLogID();
sessionID = session;
node.setLocalHost(port);
#ifdef JLOG_PROVIDES_CLOCK
nanoTime = cycle_to_nanosec(get_cycles_now()) % CLOCK_LOOP_NANOSECONDS;
#endif
}
void LogMsgSysInfo::serialize(MemoryBuffer & out) const
{
out.append(id).append((unsigned)timeStarted).append(processID).append(threadID).append(sessionID); node.serialize(out);
}
void LogMsgSysInfo::deserialize(MemoryBuffer & in)
{
unsigned t;
in.read(id).read(t).read(processID).read(threadID).read(sessionID); node.deserialize(in);
timeStarted = t;
}
// LogMsg
StringBuffer & LogMsg::toStringPlain(StringBuffer & out, unsigned fields) const
{
out.ensureCapacity(LOG_MSG_FORMAT_BUFFER_LENGTH);
if(fields & MSGFIELD_audience)
out.append("aud=").append(LogMsgAudienceToVarString(category.queryAudience())).append(' ');
if(fields & MSGFIELD_class)
out.append("cls=").append(LogMsgClassToVarString(category.queryClass())).append(' ');
if(fields & MSGFIELD_detail)
out.appendf("det=%d ", category.queryDetail());
if(fields & MSGFIELD_msgID)
out.appendf("id=%X ", sysInfo.queryMsgID());
if(fields & MSGFIELD_timeDate)
{
time_t timeNum = sysInfo.queryTime();
char timeString[12];
struct tm timeStruct;
localtime_r(&timeNum, &timeStruct);
if(fields & MSGFIELD_date)
{
strftime(timeString, 12, "%Y-%m-%d ", &timeStruct);
out.append(timeString);
}
if(fields & MSGFIELD_time)
{
strftime(timeString, 12, "%H:%M:%S ", &timeStruct);
out.append(timeString);
}
}
if(fields & MSGFIELD_process)
out.appendf("pid=%d ",sysInfo.queryProcessID());
if(fields & MSGFIELD_thread)
out.appendf("tid=%d ",sysInfo.queryThreadID());
if(fields & MSGFIELD_session)
if(sysInfo.querySessionID() == UnknownSession)
out.append("sid=unknown ");
else
out.appendf("sid=%"I64F"u ", sysInfo.querySessionID());
if(fields & MSGFIELD_node)
{
size32_t len = out.length();
sysInfo.queryNode()->getUrlStr(out);
out.append(" ");
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
out.appendf("%"I64F"dns ", sysInfo.queryClock());
else if(fields & MSGFIELD_microTime)
out.appendf("%"I64F"dus ", sysInfo.queryClock()/1000);
else if(fields & MSGFIELD_milliTime)
out.appendf("%"I64F"dms ", sysInfo.queryClock()/1000000);
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
out.append("job=unknown ");
else
out.appendf("job=%"I64F"u ", jobInfo.queryJobID());
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
out.append("usr=unknown ");
else
out.appendf("usr=%"I64F"u ", jobInfo.queryUserID());
if(fields & MSGFIELD_component)
out.appendf("cmp=%u ", component);
if (fields & MSGFIELD_quote)
out.append('"');
if (fields & MSGFIELD_prefix)
out.append(msgPrefix(category.queryClass()));
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
out.append(msgCode).append(": ").append(text.str());
else
out.append(text.str());
if (fields & MSGFIELD_quote)
out.append('"');
return out;
}
StringBuffer & LogMsg::toStringXML(StringBuffer & out, unsigned fields) const
{
out.ensureCapacity(LOG_MSG_FORMAT_BUFFER_LENGTH);
out.append("getUrlStr(out);
out.append("\" ");
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
out.append("Clock=\"").append(sysInfo.queryClock()).append("ns\" ");
else if(fields & MSGFIELD_microTime)
out.append("Clock=\"").append(sysInfo.queryClock()/1000).append("us\" ");
else if(fields & MSGFIELD_milliTime)
out.append("Clock=\"").append(sysInfo.queryClock()/1000000).append("ms\" ");
#endif
#ifdef LOG_MSG_NEWLINE
if(fields & MSGFIELD_allSysInfo) out.append("\n ");
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
out.append("JobID=\"unknown\" ");
else
out.append("JobID=\"").append(jobInfo.queryJobID()).append("\" ");
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
out.append("UserID=\"unknown\" ");
else
out.append("UserID=\"").append(jobInfo.queryUserID()).append("\" ");
#ifdef LOG_MSG_NEWLINE
if(fields & MSGFIELD_allJobInfo) out.append("\n ");
#endif
if(fields & MSGFIELD_component) out.append("Component=\"").append(component).append("\" ");
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
out.append("code=\"").append(msgCode).append("\" ");
out.append("text=\"").append(text.str()).append("\" />\n");
return out;
}
StringBuffer & LogMsg::toStringTable(StringBuffer & out, unsigned fields) const
{
out.ensureCapacity(LOG_MSG_FORMAT_BUFFER_LENGTH);
if(fields & MSGFIELD_audience)
out.append(LogMsgAudienceToFixString(category.queryAudience()));
if(fields & MSGFIELD_class)
out.append(LogMsgClassToFixString(category.queryClass()));
if(fields & MSGFIELD_detail)
out.appendf("%10d ", category.queryDetail());
if(fields & MSGFIELD_msgID)
out.appendf("%8X ", sysInfo.queryMsgID());
if(fields & MSGFIELD_timeDate)
{
time_t timeNum = sysInfo.queryTime();
char timeString[12];
struct tm timeStruct;
localtime_r(&timeNum, &timeStruct);
if(fields & MSGFIELD_date)
{
strftime(timeString, 12, "%Y-%m-%d ", &timeStruct);
out.append(timeString);
}
if(fields & MSGFIELD_time)
{
strftime(timeString, 12, "%H:%M:%S ", &timeStruct);
out.append(timeString);
}
}
if(fields & MSGFIELD_process)
out.appendf("%5d ",sysInfo.queryProcessID());
if(fields & MSGFIELD_thread)
out.appendf("%5d ",sysInfo.queryThreadID());
if(fields & MSGFIELD_session)
if(sysInfo.querySessionID() == UnknownSession)
out.append(" unknown ");
else
out.appendf("%20"I64F"u ", sysInfo.querySessionID());
if(fields & MSGFIELD_node)
{
size32_t len = out.length();
sysInfo.queryNode()->getUrlStr(out);
out.appendN(20 + len - out.length(), ' ');
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
out.appendf("%13"I64F"d ", sysInfo.queryClock());
else if(fields & MSGFIELD_microTime)
out.appendf("%10"I64F"d ", sysInfo.queryClock()/1000);
else if(fields & MSGFIELD_milliTime)
out.appendf("%7"I64F"d ", sysInfo.queryClock()/1000000);
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
out.append("unknown ");
else
out.appendf("%7"I64F"u ", jobInfo.queryJobID());
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
out.append("unknown ");
else
out.appendf("%7"I64F"u ", jobInfo.queryUserID());
if(fields & MSGFIELD_component)
out.appendf("%6u ", component);
if (fields & MSGFIELD_quote)
out.append('"');
if (fields & MSGFIELD_prefix)
out.append(msgPrefix(category.queryClass()));
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
out.append(msgCode).append(": ").append(text.str());
else
out.append(text.str());
if (fields & MSGFIELD_quote)
out.append('"');
out.append('\n');
return out;
}
StringBuffer & LogMsg::toStringTableHead(StringBuffer & out, unsigned fields)
{
if(fields & MSGFIELD_audience)
out.append("Audience ");
if(fields & MSGFIELD_class)
out.append("Class ");
if(fields & MSGFIELD_detail)
out.append(" Detail ");
if(fields & MSGFIELD_msgID)
out.append(" MsgID ");
if(fields & MSGFIELD_date)
out.append(" Date ");
if(fields & MSGFIELD_time)
out.append(" Time ");
if(fields & MSGFIELD_process)
out.append(" PID ");
if(fields & MSGFIELD_thread)
out.append(" TID ");
if(fields & MSGFIELD_session)
out.append(" SessionID ");
if(fields & MSGFIELD_node)
out.append(" Node ");
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
out.append(" Clock/ns ");
else if(fields & MSGFIELD_microTime)
out.append(" Clock/us ");
else if(fields & MSGFIELD_milliTime)
out.append("Clock/ms ");
#endif
if(fields & MSGFIELD_job)
out.append(" JobID ");
if(fields & MSGFIELD_user)
out.append(" UserID ");
if(fields & MSGFIELD_component)
out.append(" Compo ");
out.append("\n\n");
return out;
}
void LogMsg::fprintPlain(FILE * handle, unsigned fields) const
{
if(fields & MSGFIELD_audience)
fprintf(handle, "aud=%s", LogMsgAudienceToVarString(category.queryAudience()));
if(fields & MSGFIELD_class)
fprintf(handle, "cls=%s", LogMsgClassToVarString(category.queryClass()));
if(fields & MSGFIELD_detail)
fprintf(handle, "det=%d ", category.queryDetail());
if(fields & MSGFIELD_msgID)
fprintf(handle, "id=%X ", sysInfo.queryMsgID());
if(fields & MSGFIELD_timeDate)
{
time_t timeNum = sysInfo.queryTime();
char timeString[12];
struct tm timeStruct;
localtime_r(&timeNum, &timeStruct);
if(fields & MSGFIELD_date)
{
strftime(timeString, 12, "%Y-%m-%d ", &timeStruct);
fputs(timeString, handle);
}
if(fields & MSGFIELD_time)
{
strftime(timeString, 12, "%H:%M:%S ", &timeStruct);
fputs(timeString, handle);
}
}
if(fields & MSGFIELD_process)
fprintf(handle, "pid=%d ",sysInfo.queryProcessID());
if(fields & MSGFIELD_thread)
fprintf(handle, "tid=%d ",sysInfo.queryThreadID());
if(fields & MSGFIELD_session)
if(sysInfo.querySessionID() == UnknownSession)
fprintf(handle, "sid=unknown ");
else
fprintf(handle, "sid=%"I64F"u ", sysInfo.querySessionID());
if(fields & MSGFIELD_node)
{
StringBuffer buff;
sysInfo.queryNode()->getUrlStr(buff);
fprintf(handle, "%s ", buff.str());
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
fprintf(handle, "%"I64F"dns ", sysInfo.queryClock());
else if(fields & MSGFIELD_microTime)
fprintf(handle, "%"I64F"dus ", sysInfo.queryClock()/1000);
else if(fields & MSGFIELD_milliTime)
fprintf(handle, "%"I64F"dms ", sysInfo.queryClock()/1000000);
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
fprintf(handle, "job=unknown ");
else
fprintf(handle, "job=%"I64F"u ", jobInfo.queryJobID());
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
fprintf(handle, "usr=unknown ");
else
fprintf(handle, "usr=%"I64F"u ", jobInfo.queryUserID());
if(fields & MSGFIELD_component)
fprintf(handle, "cmp=%u ", component);
const char * quote = (fields & MSGFIELD_quote) ? "\"" : "";
const char * prefix = (fields & MSGFIELD_prefix) ? msgPrefix(category.queryClass()) : "";
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
fprintf(handle, "%s%s%d: %s%s", quote, prefix, msgCode, text.str(), quote);
else
fprintf(handle, "%s%s%s%s", quote, prefix, text.str(), quote);
}
void LogMsg::fprintXML(FILE * handle, unsigned fields) const
{
fprintf(handle, "getUrlStr(buff);
fprintf(handle, "Node=\"%s\" ", buff.str());
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
fprintf(handle, "Clock=\"%"I64F"d ns\" ", sysInfo.queryClock());
else if(fields & MSGFIELD_nanoTime)
fprintf(handle, "Clock=\"%"I64F"d us\" ", sysInfo.queryClock()/1000);
else if(fields & MSGFIELD_nanoTime)
fprintf(handle, "Clock=\"%"I64F"d ms\" ", sysInfo.queryClock()/1000000);
#endif
#ifdef LOG_MSG_NEWLINE
if(fields & MSGFIELD_allSysInfo) fprintf(handle, "\n ");
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
fprintf(handle, "JobID=\"unknown\" ");
else
fprintf(handle, "JobID=\"%"I64F"u\" ", jobInfo.queryJobID());
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
fprintf(handle, "UserID=\"unknown\" ");
else
fprintf(handle, "UserID=\"%"I64F"u\" ", jobInfo.queryUserID());
if(fields & MSGFIELD_component)
fprintf(handle, "Component=\"%6u\" ", component);
#ifdef LOG_MSG_NEWLINE
if(fields & MSGFIELD_allJobInfo) fprintf(handle, "\n ");
#endif
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
fprintf(handle, "code=\"%d\" ", msgCode);
fprintf(handle, "text=\"%s\" />\n", text.str());
}
void LogMsg::fprintTable(FILE * handle, unsigned fields) const
{
if(fields & MSGFIELD_audience)
fputs(LogMsgAudienceToFixString(category.queryAudience()), handle);
if(fields & MSGFIELD_class)
fputs(LogMsgClassToFixString(category.queryClass()), handle);
if(fields & MSGFIELD_detail)
fprintf(handle, "%10d ", category.queryDetail());
if(fields & MSGFIELD_msgID)
fprintf(handle, "%08X ", sysInfo.queryMsgID());
if(fields & MSGFIELD_timeDate)
{
time_t timeNum = sysInfo.queryTime();
char timeString[12];
struct tm timeStruct;
localtime_r(&timeNum, &timeStruct);
if(fields & MSGFIELD_date)
{
strftime(timeString, 12, "%Y-%m-%d ", &timeStruct);
fputs(timeString, handle);
}
if(fields & MSGFIELD_time)
{
strftime(timeString, 12, "%H:%M:%S ", &timeStruct);
fputs(timeString, handle);
}
}
if(fields & MSGFIELD_process)
fprintf(handle, "%5d ",sysInfo.queryProcessID());
if(fields & MSGFIELD_thread)
fprintf(handle, "%5d ",sysInfo.queryThreadID());
if(fields & MSGFIELD_session)
if(sysInfo.querySessionID() == UnknownSession)
fprintf(handle, " unknown ");
else
fprintf(handle, "%20"I64F"u ", sysInfo.querySessionID());
if(fields & MSGFIELD_node)
{
StringBuffer buff;
static const char * twenty_spaces = " ";
sysInfo.queryNode()->getUrlStr(buff);
fprintf(handle, "%s%s", buff.str(), (buff.length()<=20) ? twenty_spaces+buff.length() : "");
}
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
fprintf(handle, "%13"I64F"d ", sysInfo.queryClock());
else if(fields & MSGFIELD_microTime)
fprintf(handle, "%10"I64F"d ", sysInfo.queryClock()/1000);
else if(fields & MSGFIELD_milliTime)
fprintf(handle, "%7"I64F"d ", sysInfo.queryClock()/1000000);
#endif
if(fields & MSGFIELD_job)
if(jobInfo.queryJobID() == UnknownJob)
fprintf(handle, "unknown ");
else
fprintf(handle, "%7"I64F"u ", jobInfo.queryJobID());
if(fields & MSGFIELD_user)
if(jobInfo.queryUserID() == UnknownUser)
fprintf(handle, "unknown ");
else
fprintf(handle, "%7"I64F"u ", jobInfo.queryUserID());
if(fields & MSGFIELD_component)
fprintf(handle, "%6u ", component);
const char * quote = (fields & MSGFIELD_quote) ? "\"" : "";
const char * prefix = (fields & MSGFIELD_prefix) ? msgPrefix(category.queryClass()) : "";
if((fields & MSGFIELD_code) && (msgCode != NoLogMsgCode))
fprintf(handle, "%s%s%d: %s%s\n", quote, prefix, msgCode, text.str(), quote);
else
fprintf(handle, "%s%s%s%s\n", quote, prefix, text.str(), quote);
}
void LogMsg::fprintTableHead(FILE * handle, unsigned fields)
{
if(fields & MSGFIELD_audience)
fprintf(handle, "Audience ");
if(fields & MSGFIELD_class)
fprintf(handle, "Class ");
if(fields & MSGFIELD_detail)
fprintf(handle, " Detail ");
if(fields & MSGFIELD_msgID)
fprintf(handle, " MsgID ");
if(fields & MSGFIELD_date)
fprintf(handle, " Date ");
if(fields & MSGFIELD_time)
fprintf(handle, " Time ");
if(fields & MSGFIELD_process)
fprintf(handle, " PID ");
if(fields & MSGFIELD_thread)
fprintf(handle, " TID ");
if(fields & MSGFIELD_session)
fprintf(handle, " SessionID ");
if(fields & MSGFIELD_node)
fprintf(handle, " Node ");
#ifdef JLOG_PROVIDES_CLOCK
if(fields & MSGFIELD_nanoTime)
fprintf(handle, " Clock/ns ");
else if(fields & MSGFIELD_microTime)
fprintf(handle, " Clock/us ");
else if(fields & MSGFIELD_milliTime)
fprintf(handle, "Clock/ms ");
#endif
if(fields & MSGFIELD_job)
fprintf(handle, " JobID ");
if(fields & MSGFIELD_user)
fprintf(handle, " UserID ");
if(fields & MSGFIELD_component)
fprintf(handle, " Compo ");
fprintf(handle, "\n\n");
}
// Implementations of ILogMsgFilter
void PassAllLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "all");
tree->addPropTree("filter", filterTree);
}
void PassLocalLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "local");
tree->addPropTree("filter", filterTree);
}
void PassNoneLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "none");
tree->addPropTree("filter", filterTree);
}
void CategoryLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "category");
filterTree->setPropInt("@audience", audienceMask);
filterTree->setPropInt("@class", classMask);
filterTree->setPropInt("@detail", maxDetail);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void PIDLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "pid");
filterTree->setPropInt("@pid", pid);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void TIDLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "tid");
filterTree->setPropInt("@tid", tid);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void NodeLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "node");
StringBuffer buff;
node.getIpText(buff);
filterTree->setProp("@ip", buff.str());
filterTree->setPropInt("@port", node.port);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void IpLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "ip");
StringBuffer buff;
ip.getIpText(buff);
filterTree->setProp("@ip", buff.str());
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void JobLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "job");
filterTree->setPropInt("@job", (int)job);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void UserLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "user");
filterTree->setPropInt("@user", (int)user);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void SessionLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "session");
filterTree->setPropInt("@session", (int)session);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void ComponentLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "component");
filterTree->setPropInt("@component", component);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
bool RegexLogMsgFilter::includeMessage(const LogMsg & msg) const
{
if(localFlag && msg.queryRemoteFlag()) return false;
SpinBlock b(lock);
return const_cast(regex).find(msg.queryText()) != NULL;
}
void RegexLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "regex");
filterTree->setProp("@regex", regexText);
if(localFlag) filterTree->setPropInt("@local", 1);
tree->addPropTree("filter", filterTree);
}
void NotLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "not");
arg->addToPTree(filterTree);
tree->addPropTree("filter", filterTree);
}
void AndLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "and");
arg1->addToPTree(filterTree);
arg2->addToPTree(filterTree);
tree->addPropTree("filter", filterTree);
}
void OrLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "or");
arg1->addToPTree(filterTree);
arg2->addToPTree(filterTree);
tree->addPropTree("filter", filterTree);
}
void SwitchLogMsgFilter::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * filterTree = createPTree(ipt_caseInsensitive);
filterTree->setProp("@type", "switch");
cond->addToPTree(filterTree);
yes->addToPTree(filterTree);
no->addToPTree(filterTree);
tree->addPropTree("filter", filterTree);
}
void CategoryLogMsgFilter::orWithFilter(const ILogMsgFilter * filter)
{
audienceMask |= filter->queryAudienceMask();
classMask |= filter->queryClassMask();
maxDetail = std::max(maxDetail, filter->queryMaxDetail());
}
void CategoryLogMsgFilter::reset()
{
audienceMask = 0;
classMask = 0;
maxDetail = 0;
}
// HandleLogMsgHandler
void HandleLogMsgHandlerTable::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
if(handle==stderr)
handlerTree->setProp("@type", "stderr");
else
handlerTree->setProp("@type", "mischandle");
handlerTree->setPropInt("@fields", messageFields);
tree->addPropTree("handler", handlerTree);
}
void HandleLogMsgHandlerXML::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
if(handle==stderr)
handlerTree->setProp("@type", "stderr");
else
handlerTree->setProp("@type", "mischandle");
handlerTree->setPropInt("@fields", messageFields);
handlerTree->setProp("@writeXML", "true");
tree->addPropTree("handler", handlerTree);
}
// FileLogMsgHandler
FileLogMsgHandler::FileLogMsgHandler(const char * _filename, const char * _headerText, unsigned _fields, bool _append, bool _flushes)
: messageFields(_fields), filename(_filename), headerText(_headerText), append(_append), flushes(_flushes)
{
recursiveCreateDirectoryForFile(filename);
if(append)
handle = fopen(filename, "a");
else
handle = fopen(filename, "w");
if(!handle) {
handle = getNullHandle();
StringBuffer err;
err.appendf("LOGGING: could not open file '%s' for output",filename.get());
ERRLOG("%s",err.str()); // make sure doesn't get lost!
throw MakeStringException(3000,"%s",err.str()); // 3000: internal error
}
if(headerText) fprintf(handle, "--- %s ---\n", (const char *)headerText);
}
static void closeAndDeleteEmpty(const char * filename, FILE *handle)
{
if (handle) {
fpos_t pos;
bool del = (fgetpos(handle, &pos)==0)&&
#if defined( _WIN32) || defined(__FreeBSD__) || defined(__APPLE__)
(pos==0);
#else
(pos.__pos==0);
#endif
fclose(handle);
if (del)
remove(filename);
}
}
FileLogMsgHandler::~FileLogMsgHandler()
{
closeAndDeleteEmpty(filename,handle);
}
char const * FileLogMsgHandler::disable()
{
crit.enter();
fclose(handle);
handle = NULL;
return filename;
}
void FileLogMsgHandler::enable()
{
recursiveCreateDirectoryForFile(filename);
handle = fopen(filename, "a");
if(!handle) {
handle = getNullHandle();
assertex(!"FileLogMsgHandler::enable : could not open file for output");
}
crit.leave();
}
void FileLogMsgHandlerTable::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
handlerTree->setProp("@type", "file");
handlerTree->setProp("@filename", filename.get());
if(headerText) handlerTree->setProp("@headertext", headerText.get());
handlerTree->setPropInt("@fields", messageFields);
handlerTree->setProp("@writeTable", "true");
if(append) handlerTree->setProp("@append", "true");
if(flushes) handlerTree->setProp("@flushes", "true");
tree->addPropTree("handler", handlerTree);
}
void FileLogMsgHandlerXML::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
handlerTree->setProp("@type", "file");
handlerTree->setProp("@filename", filename.get());
if(headerText) handlerTree->setProp("@headertext", headerText.get());
handlerTree->setPropInt("@fields", messageFields);
if(append) handlerTree->setProp("@append", "true");
if(flushes) handlerTree->setProp("@flushes", "true");
tree->addPropTree("handler", handlerTree);
}
// FileLogMsgHandler
RollingFileLogMsgHandler::RollingFileLogMsgHandler(const char * _filebase, const char * _fileextn, unsigned _fields, bool _append, bool _flushes, const char *initialName, const char *_alias, bool daily) : messageFields(_fields), filebase(_filebase), fileextn(_fileextn), append(_append), flushes(_flushes), handle(0), alias(_alias)
{
time_t tNow;
time(&tNow);
localtime_r(&tNow, &startTime);
doRollover(daily, initialName);
}
RollingFileLogMsgHandler::~RollingFileLogMsgHandler()
{
closeAndDeleteEmpty(filename,handle);
}
char const * RollingFileLogMsgHandler::disable()
{
crit.enter();
fclose(handle);
return filename;
}
void RollingFileLogMsgHandler::enable()
{
recursiveCreateDirectoryForFile(filename);
handle = fopen(filename, "a");
if(!handle) {
handle = getNullHandle();
assertex(!"RollingFileLogMsgHandler::enable : could not open file for output");
}
crit.leave();
}
void RollingFileLogMsgHandler::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
handlerTree->setProp("@type", "rollingfile");
handlerTree->setProp("@filebase", filebase.get());
handlerTree->setProp("@fileextn", fileextn.get());
handlerTree->setPropInt("@fields", messageFields);
if(append) handlerTree->setProp("@append", "true");
if(flushes) handlerTree->setProp("@flushes", "true");
tree->addPropTree("handler", handlerTree);
}
#define ROLLOVER_PERIOD 86400
void RollingFileLogMsgHandler::checkRollover() const
{
time_t tNow;
time(&tNow);
struct tm ltNow;
localtime_r(&tNow, <Now);
if(ltNow.tm_year != startTime.tm_year || ltNow.tm_yday != startTime.tm_yday)
{
localtime_r(&tNow, &startTime); // reset the start time for next rollover check
doRollover(true);
}
}
void RollingFileLogMsgHandler::doRollover(bool daily, const char *forceName) const
{
CriticalBlock block(crit);
closeAndDeleteEmpty(filename,handle);
handle = 0;
filename.clear();
if (forceName)
filename.append(forceName);
else
{
filename.clear().append(filebase.get());
addFileTimestamp(filename, daily);
filename.append(fileextn.get());
}
recursiveCreateDirectoryForFile(filename.str());
handle = fopen(filename.str(), append ? "a" : "w");
if (handle && alias && alias.length())
{
fclose(handle);
handle = 0;
remove(alias);
try
{
createHardLink(alias, filename.str());
}
catch (IException *E)
{
recursiveCreateDirectoryForFile(filename.str());
handle = fopen(filename.str(), append ? "a" : "w");
EXCLOG(E); // Log the fact that we could not create the alias - probably it is locked (tail a bit unfortunate on windows).
E->Release();
}
if (!handle)
{
recursiveCreateDirectoryForFile(filename.str());
handle = fopen(filename.str(), append ? "a" : "w");
}
}
if(!handle)
{
handle = getNullHandle();
DBGLOG("RollingFileLogMsgHandler::doRollover : could not open log file %s for output", filename.str());
// actually this is pretty fatal
}
}
// BinLogMsgHandler
BinLogMsgHandler::BinLogMsgHandler(const char * _filename, bool _append) : filename(_filename), append(_append)
{
file.setown(createIFile(filename.get()));
if(!file) assertex(!"BinLogMsgHandler::BinLogMsgHanlder : Could not create IFile");
if(append)
fio.setown(file->open(IFOwrite));
else
fio.setown(file->open(IFOcreate));
if(!fio) assertex(!"BinLogMsgHandler::BinLogMsgHanlder : Could not create IFileIO");
fstr.setown(createIOStream(fio));
if(!fstr) assertex(!"BinLogMsgHandler::BinLogMsgHanlder : Could not create IFileIOStream");
if(append)
fstr->seek(0, IFSend);
}
BinLogMsgHandler::~BinLogMsgHandler()
{
fstr.clear();
fio.clear();
file.clear();
}
void BinLogMsgHandler::handleMessage(const LogMsg & msg) const
{
CriticalBlock block(crit);
mbuff.clear();
msg.serialize(mbuff);
msglen = mbuff.length();
fstr->write(sizeof(msglen), &msglen);
fstr->write(msglen, mbuff.toByteArray());
}
void BinLogMsgHandler::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
handlerTree->setProp("@type", "binary");
handlerTree->setProp("@filename", filename.get());
if(append) handlerTree->setProp("@append", "true");
tree->addPropTree("handler", handlerTree);
}
char const * BinLogMsgHandler::disable()
{
crit.enter();
fstr.clear();
fio.clear();
return filename.get();
}
void BinLogMsgHandler::enable()
{
fio.setown(file->open(IFOwrite));
if(!fio) assertex(!"BinLogMsgHandler::enable : Could not create IFileIO");
fstr.setown(createIOStream(fio));
if(!fstr) assertex(!"BinLogMsgHandler::enable : Could not create IFileIOStream");
fstr->seek(0, IFSend);
crit.leave();
}
// LogMsgComponentReporter
void LogMsgComponentReporter::report(const LogMsgCategory & cat, const char * format, ...)
{
va_list args;
va_start(args, format);
queryLogMsgManager()->report_va(component, cat, unknownJob, format, args);
va_end(args);
}
void LogMsgComponentReporter::report_va(const LogMsgCategory & cat, const char * format, va_list args)
{
queryLogMsgManager()->report_va(component, cat, unknownJob, format, args);
}
void LogMsgComponentReporter::report(const LogMsgCategory & cat, LogMsgCode code, const char * format, ...)
{
va_list args;
va_start(args, format);
queryLogMsgManager()->report_va(component, cat, unknownJob, code, format, args);
va_end(args);
}
void LogMsgComponentReporter::report_va(const LogMsgCategory & cat, LogMsgCode code, const char * format, va_list args)
{
queryLogMsgManager()->report_va(component, cat, unknownJob, code, format, args);
}
void LogMsgComponentReporter::report(const LogMsgCategory & cat, const IException * exception, const char * prefix)
{
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
queryLogMsgManager()->report(component, cat, unknownJob, exception->errorCode(), "%s", buff.str());
}
void LogMsgComponentReporter::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, ...)
{
va_list args;
va_start(args, format);
queryLogMsgManager()->report_va(component, cat, job, format, args);
va_end(args);
}
void LogMsgComponentReporter::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, va_list args)
{
queryLogMsgManager()->report_va(component, cat, job, format, args);
}
void LogMsgComponentReporter::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, ...)
{
va_list args;
va_start(args, format);
queryLogMsgManager()->report_va(component, cat, job, code, format, args);
va_end(args);
}
void LogMsgComponentReporter::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, va_list args)
{
queryLogMsgManager()->report_va(component, cat, job, code, format, args);
}
void LogMsgComponentReporter::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const IException * exception, const char * prefix)
{
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
queryLogMsgManager()->report(component, cat, job, exception->errorCode(), "%s", buff.str());
}
void LogMsgComponentReporter::report(const LogMsg & msg)
{
queryLogMsgManager()->report(msg);
}
// LogMsgPrepender
LogMsgPrepender * LogMsgPrepender::setContext(LogMsgComponentReporter * r, char const * f, unsigned l)
{
crit.enter();
reporter = r;
file = f;
line = l;
return this;
}
void LogMsgPrepender::report(const LogMsgCategory & cat, const char * format, ...)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
va_list args;
va_start(args, format);
if(reporter)
reporter->report_va(cat, unknownJob, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, unknownJob, buff.str(), args);
va_end(args);
crit.leave();
}
void LogMsgPrepender::report_va(const LogMsgCategory & cat, const char * format, va_list args)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
if(reporter)
reporter->report_va(cat, unknownJob, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, unknownJob, buff.str(), args);
crit.leave();
}
void LogMsgPrepender::report(const LogMsgCategory & cat, LogMsgCode code, const char * format, ...)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
va_list args;
va_start(args, format);
if(reporter)
reporter->report_va(cat, unknownJob, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, unknownJob, buff.str(), args);
va_end(args);
crit.leave();
}
void LogMsgPrepender::report_va(const LogMsgCategory & cat, LogMsgCode code, const char * format, va_list args)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
if(reporter)
reporter->report_va(cat, unknownJob, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, unknownJob, buff.str(), args);
crit.leave();
}
void LogMsgPrepender::report(const LogMsgCategory & cat, const IException * exception, const char * prefix)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ");
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
if(reporter)
reporter->report(cat, unknownJob, exception->errorCode(), "%s", buff.str());
else
queryLogMsgManager()->report(cat, unknownJob, exception->errorCode(), "%s", buff.str());
crit.leave();
}
void LogMsgPrepender::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, ...)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
va_list args;
va_start(args, format);
if(reporter)
reporter->report_va(cat, job, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, job, buff.str(), args);
va_end(args);
crit.leave();
}
void LogMsgPrepender::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, va_list args)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
if(reporter)
reporter->report_va(cat, job, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, job, buff.str(), args);
crit.leave();
}
void LogMsgPrepender::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, ...)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
va_list args;
va_start(args, format);
if(reporter)
reporter->report_va(cat, job, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, job, buff.str(), args);
va_end(args);
crit.leave();
}
void LogMsgPrepender::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, va_list args)
{
StringBuffer buff;
buff.append(file).append("(").append(line).append(") : ").append(format);
if(reporter)
reporter->report_va(cat, job, buff.str(), args);
else
queryLogMsgManager()->report_va(cat, job, buff.str(), args);
crit.leave();
}
void LogMsgPrepender::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const IException * exception, const char * prefix)
{
StringBuffer txt;
if (prefix)
txt.append(prefix).append(" : ");
exception->errorMessage(txt);
if (reporter)
reporter->report(cat, job, exception->errorCode(), "%s(%d) : %s", file, line, txt.str());
else
queryLogMsgManager()->report(cat, job, exception->errorCode(), "%s(%d) : %s", file, line, txt.str());
crit.leave();
}
IException * LogMsgPrepender::report(IException * e, const char * prefix, LogMsgClass cls)
{
report(MCexception(e, cls), unknownJob, e, prefix);
return e;
}
// LogMsgMonitor
void LogMsgMonitor::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * monitorTree = createPTree(ipt_caseInsensitive);
handler->addToPTree(monitorTree);
filter->addToPTree(monitorTree);
tree->addPropTree("monitor", monitorTree);
}
// CLogMsgManager
void CLogMsgManager::MsgProcessor::push(LogMsg * msg)
{
//assertex(more); an assertex will just recurse here
if (!more) // we are effective stopped so don't bother even dropping (and leak parameter) as drop will involve
// interaction with the base class which is stopped and could easily crash (as this condition
// is expected not to occur - typically occurs if the user has incorrectly called exit on one thread
// while still in the process of logging on another)
// cf Bug #53695 for more discussion of the issue
return;
else if(droppingLimit && (q.ordinality() >= droppingLimit))
drop();
q.enqueue(msg);
}
int CLogMsgManager::MsgProcessor::run()
{
Owned msg;
while(more)
{
msg.setown(q.dequeueAndNotify(this)); // notify locks mutex on non-null return
if(!msg)
break;
owner->doReport(*msg);
pullCycleMutex.unlock();
}
while(true)
{
msg.setown(q.dequeueNowAndNotify(this)); // notify locks mutex on non-null return
if(!msg)
break;
owner->doReport(*msg);
pullCycleMutex.unlock();
}
return 0;
}
void CLogMsgManager::MsgProcessor::notify(LogMsg *)
{
pullCycleMutex.lock();
}
void CLogMsgManager::MsgProcessor::setBlockingLimit(unsigned lim)
{
q.setLimit(lim);
droppingLimit = 0;
}
void CLogMsgManager::MsgProcessor::setDroppingLimit(unsigned lim, unsigned num)
{
numToDrop = num;
droppingLimit = lim;
q.setLimit(0);
}
void CLogMsgManager::MsgProcessor::resetLimit()
{
droppingLimit = 0;
q.setLimit(0);
}
void CLogMsgManager::MsgProcessor::stop()
{
more = false;
q.stop();
}
void CLogMsgManager::MsgProcessor::drop()
{
Owned msg, lastMsg;
unsigned count;
unsigned prev = 0;
for(count = 0; count < numToDrop; count++)
{
msg.setown(q.dequeueTail(0));
if(!msg) break;
DropLogMsg * dmsg = dynamic_cast(msg.get());
if(dmsg) prev += dmsg->queryCount()-1;
lastMsg.setown(msg.getClear());
}
if(lastMsg)
q.enqueue(new DropLogMsg(owner, lastMsg->querySysInfo().queryMsgID(), count+prev));
}
bool CLogMsgManager::MsgProcessor::flush(unsigned timeout)
{
unsigned start = msTick();
if(!q.waitMaxOrdinality(0, timeout))
return false;
unsigned now = msTick();
if(now >= (start+timeout))
return false;
try
{
synchronized block(pullCycleMutex, timeout+start-now);
}
catch(IException * e)
{
e->Release();
return false;
}
return true;
}
CLogMsgManager::~CLogMsgManager()
{
CriticalBlock crit(modeLock);
if(processor)
{
processor->stop();
processor->join();
}
}
void CLogMsgManager::enterQueueingMode()
{
CriticalBlock crit(modeLock);
if(processor) return;
processor.setown(new MsgProcessor(this));
processor->setBlockingLimit(defaultMsgQueueLimit);
processor->start();
}
void CLogMsgManager::setQueueBlockingLimit(unsigned lim)
{
CriticalBlock crit(modeLock);
if(processor)
processor->setBlockingLimit(lim);
}
void CLogMsgManager::setQueueDroppingLimit(unsigned lim, unsigned numToDrop)
{
CriticalBlock crit(modeLock);
if(processor)
processor->setDroppingLimit(lim, numToDrop);
}
void CLogMsgManager::resetQueueLimit()
{
CriticalBlock crit(modeLock);
if(processor)
processor->resetLimit();
}
void CLogMsgManager::report(const LogMsgCategory & cat, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, NoLogMsgCode, format, args, 0, port, session));
va_end(args);
}
void CLogMsgManager::report_va(const LogMsgCategory & cat, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), unknownJob, NoLogMsgCode, format, args, 0, port, session));
}
void CLogMsgManager::report(const LogMsgCategory & cat, LogMsgCode code, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, code, format, args, 0, port, session));
va_end(args);
}
void CLogMsgManager::report_va(const LogMsgCategory & cat, LogMsgCode code, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), unknownJob, code, format, args, 0, port, session));
}
void CLogMsgManager::report(const LogMsgCategory & cat, const IException * exception, const char * prefix)
{
if(rejectsCategory(cat)) return;
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, exception->errorCode(), buff.str(), 0, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, NoLogMsgCode, format, args, compo, port, session));
va_end(args);
}
void CLogMsgManager::report_va(unsigned compo, const LogMsgCategory & cat, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), unknownJob, NoLogMsgCode, format, args, compo, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, LogMsgCode code, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, code, format, args, compo, port, session));
va_end(args);
}
void CLogMsgManager::report_va(unsigned compo, const LogMsgCategory & cat, LogMsgCode code, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), unknownJob, code, format, args, compo, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, const IException * exception, const char * prefix)
{
if(rejectsCategory(cat)) return;
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
pushMsg(new LogMsg(cat, getNextID(), unknownJob, exception->errorCode(), buff.str(), compo, port, session));
}
void CLogMsgManager::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), job, NoLogMsgCode, format, args, 0, port, session));
va_end(args);
}
void CLogMsgManager::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), job, NoLogMsgCode, format, args, 0, port, session));
}
void CLogMsgManager::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), job, code, format, args, 0, port, session));
va_end(args);
}
void CLogMsgManager::report_va(const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), job, code, format, args, 0, port, session));
}
void CLogMsgManager::report(const LogMsgCategory & cat, const LogMsgJobInfo & job, const IException * exception, const char * prefix)
{
if(rejectsCategory(cat)) return;
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
pushMsg(new LogMsg(cat, getNextID(), job, exception->errorCode(), buff.str(), 0, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), job, NoLogMsgCode, format, args, compo, port, session));
va_end(args);
}
void CLogMsgManager::report_va(unsigned compo, const LogMsgCategory & cat, const LogMsgJobInfo & job, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), job, NoLogMsgCode, format, args, compo, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, ...)
{
if(rejectsCategory(cat)) return;
va_list args;
va_start(args, format);
pushMsg(new LogMsg(cat, getNextID(), job, code, format, args, compo, port, session));
va_end(args);
}
void CLogMsgManager::report_va(unsigned compo, const LogMsgCategory & cat, const LogMsgJobInfo & job, LogMsgCode code, const char * format, va_list args)
{
if(rejectsCategory(cat)) return;
pushMsg(new LogMsg(cat, getNextID(), job, code, format, args, compo, port, session));
}
void CLogMsgManager::report(unsigned compo, const LogMsgCategory & cat, const LogMsgJobInfo & job, const IException * exception, const char * prefix)
{
if(rejectsCategory(cat)) return;
StringBuffer buff;
if(prefix) buff.append(prefix).append(" : ");
exception->errorMessage(buff);
pushMsg(new LogMsg(cat, getNextID(), job, exception->errorCode(), buff.str(), compo, port, session));
}
void CLogMsgManager::pushMsg(LogMsg * _msg)
{
Owned msg(_msg);
if(processor)
processor->push(msg.getLink());
else
doReport(*msg);
}
void CLogMsgManager::doReport(const LogMsg & msg) const
{
try
{
ReadLockBlock block(monitorLock);
ForEachItemIn(i, monitors)
monitors.item(i).processMessage(msg);
}
catch(IException * e)
{
StringBuffer err("exception reporting log message: ");
err.append(e->errorCode());
e->errorMessage(err);
panic(err.str());
e->Release();
}
catch(...)
{
panic("unknown exception reporting log message");
}
}
void CLogMsgManager::panic(char const * reason) const
{
fprintf(stderr, "%s", reason); // not sure there's anything more useful we can do here
}
aindex_t CLogMsgManager::find(const ILogMsgHandler * handler) const
{
// N.B. Should be used inside critical block
ForEachItemIn(i, monitors)
if(monitors.item(i).queryHandler()==handler) return i;
return NotFound;
}
bool CLogMsgManager::addMonitor(ILogMsgHandler * handler, ILogMsgFilter * filter)
{
flushQueue(10*1000);
WriteLockBlock block(monitorLock);
if(find(handler) != NotFound) return false;
monitors.append(*(new LogMsgMonitor(filter, handler)));
prefilter.orWithFilter(filter);
sendFilterToChildren(true);
return true;
}
bool CLogMsgManager::addMonitorOwn(ILogMsgHandler * handler, ILogMsgFilter * filter)
{
bool ret = addMonitor(handler, filter);
filter->Release();
handler->Release();
return ret;
}
void CLogMsgManager::buildPrefilter()
{
// N.B. Should be used inside critical block
prefilter.reset();
ForEachItemIn(i, monitors)
prefilter.orWithFilter(monitors.item(i).queryFilter());
}
bool CLogMsgManager::removeMonitor(ILogMsgHandler * handler)
{
Linked todelete;
{
WriteLockBlock block(monitorLock);
aindex_t pos = find(handler);
if(pos == NotFound) return false;
todelete.set(&monitors.item(pos));
monitors.remove(pos);
buildPrefilter();
sendFilterToChildren(true);
return true;
}
}
unsigned CLogMsgManager::removeMonitorsMatching(HandlerTest & test)
{
CIArrayOf todelete; // delete outside monitorLock
unsigned count = 0;
{
WriteLockBlock block(monitorLock);
ForEachItemInRev(i, monitors)
if(test(monitors.item(i).queryHandler()))
{
LogMsgMonitor &it = monitors.item(i);
it.Link();
todelete.append(it);
monitors.remove(i);
++count;
}
buildPrefilter();
sendFilterToChildren(true);
}
return count;
}
void CLogMsgManager::removeAllMonitors()
{
CIArrayOf todelete; // delete outside monitorLock
{
WriteLockBlock block(monitorLock);
ForEachItemInRev(i, monitors) {
LogMsgMonitor &it = monitors.item(i);
it.Link();
todelete.append(it);
monitors.remove(i);
}
prefilter.reset();
sendFilterToChildren(true);
}
}
void CLogMsgManager::resetMonitors()
{
suspendChildren();
removeAllMonitors();
Owned defaultFilter = getDefaultLogMsgFilter();
addMonitor(theStderrHandler, defaultFilter);
unsuspendChildren();
}
ILogMsgFilter * CLogMsgManager::queryMonitorFilter(const ILogMsgHandler * handler) const
{
ReadLockBlock block(monitorLock);
aindex_t pos = find(handler);
if(pos == NotFound) return 0;
return monitors.item(pos).queryFilter();
}
bool CLogMsgManager::changeMonitorFilter(const ILogMsgHandler * handler, ILogMsgFilter * newFilter)
{
WriteLockBlock block(monitorLock);
aindex_t pos = find(handler);
if(pos == NotFound) return 0;
monitors.item(pos).setFilter(newFilter);
buildPrefilter();
sendFilterToChildren(true);
return true;
}
void CLogMsgManager::prepAllHandlers() const
{
ReadLockBlock block(monitorLock);
ForEachItemIn(i, monitors)
if(monitors.item(i).queryHandler()->needsPrep()) monitors.item(i).queryHandler()->prep();
}
aindex_t CLogMsgManager::findChild(ILogMsgLinkToChild * child) const
{
ForEachItemIn(i, children)
if(&(children.item(i)) == child ) return i;
return NotFound;
}
ILogMsgFilter * CLogMsgManager::getCompoundFilter(bool locked) const
{
if(!locked) monitorLock.lockRead();
Owned categoryFilter = new CategoryLogMsgFilter(0, 0, 0, false);
Owned otherFilters;
ILogMsgFilter * ifilter;
bool hadCat = false;
ForEachItemIn(i, monitors)
{
ifilter = monitors.item(i).queryFilter();
if(ifilter->queryLocalFlag()) continue;
if(ifilter->isCategoryFilter())
{
categoryFilter->orWithFilter(ifilter);
hadCat = true;
}
else
{
if(otherFilters)
otherFilters.setown(getOrLogMsgFilter(otherFilters, ifilter));
else
otherFilters.set(ifilter);
}
}
if(hadCat)
{
if(otherFilters)
otherFilters.setown(getOrLogMsgFilter(otherFilters, categoryFilter));
else
otherFilters.set(categoryFilter);
}
if(!locked) monitorLock.unlock();
if(!otherFilters)
return getPassNoneLogMsgFilter();
return otherFilters.getLink();
}
void CLogMsgManager::sendFilterToChildren(bool locked) const
{
if(suspendedChildren) return;
ReadLockBlock block(childLock);
if(children.length()==0) return;
ILogMsgFilter * filter = getCompoundFilter(locked);
ForEachItemIn(i, children)
children.item(i).sendFilter(filter);
filter->Release();
}
bool CLogMsgManager::addMonitorToPTree(const ILogMsgHandler * handler, IPropertyTree * tree) const
{
ReadLockBlock block(monitorLock);
aindex_t pos = find(handler);
if(pos == NotFound) return false;
monitors.item(pos).addToPTree(tree);
return true;
}
void CLogMsgManager::addAllMonitorsToPTree(IPropertyTree * tree) const
{
ReadLockBlock block(monitorLock);
ForEachItemIn(i, monitors)
monitors.item(i).addToPTree(tree);
}
bool CLogMsgManager::rejectsCategory(const LogMsgCategory & cat) const
{
if (!prefilter.includeCategory(cat))
return true;
ReadLockBlock block(monitorLock);
ForEachItemIn(i, monitors)
{
if (monitors.item(i).queryFilter()->mayIncludeCategory(cat))
return false;
}
return true;
}
// Helper functions
ILogMsgFilter * getDeserializedLogMsgFilter(MemoryBuffer & in)
{
unsigned type;
in.read(type);
switch(type)
{
case MSGFILTER_passall : return LINK(thePassAllFilter);
case MSGFILTER_passlocal : return LINK(thePassLocalFilter);
case MSGFILTER_passnone : return LINK(thePassNoneFilter);
case MSGFILTER_category : return new CategoryLogMsgFilter(in);
case MSGFILTER_pid : return new PIDLogMsgFilter(in);
case MSGFILTER_tid : return new TIDLogMsgFilter(in);
case MSGFILTER_node : return new NodeLogMsgFilter(in);
case MSGFILTER_ip : return new IpLogMsgFilter(in);
case MSGFILTER_job : return new JobLogMsgFilter(in);
case MSGFILTER_user : return new UserLogMsgFilter(in);
case MSGFILTER_session : return new SessionLogMsgFilter(in);
case MSGFILTER_component : return new ComponentLogMsgFilter(in);
case MSGFILTER_regex : return new RegexLogMsgFilter(in);
case MSGFILTER_not : return new NotLogMsgFilter(in);
case MSGFILTER_and : return new AndLogMsgFilter(in);
case MSGFILTER_or : return new OrLogMsgFilter(in);
case MSGFILTER_switch : return new SwitchLogMsgFilter(in);
default: assertex(!"getDeserializedLogMsgFilter: unrecognized LogMsgFilterType");
}
return 0;
}
ILogMsgFilter * getLogMsgFilterFromPTree(IPropertyTree * xml)
{
/* Note that several of these constructors use GetPropInt and GetPropInt64 to get unsigneds. I think this is OK? (all int64 internally)*/
StringBuffer type;
xml->getProp("@type", type);
if(strcmp(type.str(), "all")==0) return LINK(thePassAllFilter);
else if(strcmp(type.str(), "local")==0) return LINK(thePassLocalFilter);
else if(strcmp(type.str(), "none")==0) return LINK(thePassNoneFilter);
else if(strcmp(type.str(), "category")==0) return new CategoryLogMsgFilter(xml);
else if(strcmp(type.str(), "pid")==0) return new PIDLogMsgFilter(xml);
else if(strcmp(type.str(), "tid")==0) return new TIDLogMsgFilter(xml);
else if(strcmp(type.str(), "node")==0) return new NodeLogMsgFilter(xml);
else if(strcmp(type.str(), "ip")==0) return new IpLogMsgFilter(xml);
else if(strcmp(type.str(), "job")==0) return new JobLogMsgFilter(xml);
else if(strcmp(type.str(), "user")==0) return new UserLogMsgFilter(xml);
else if(strcmp(type.str(), "session")==0) return new SessionLogMsgFilter(xml);
else if(strcmp(type.str(), "component")==0) return new ComponentLogMsgFilter(xml);
else if(strcmp(type.str(), "regex")==0) return new RegexLogMsgFilter(xml);
else if(strcmp(type.str(), "not")==0) return new NotLogMsgFilter(xml);
else if(strcmp(type.str(), "and")==0) return new AndLogMsgFilter(xml);
else if(strcmp(type.str(), "or")==0) return new OrLogMsgFilter(xml);
else if(strcmp(type.str(), "filter")==0) return new SwitchLogMsgFilter(xml);
else assertex(!"getLogMsgFilterFromPTree : unrecognized LogMsgFilter type");
return getPassAllLogMsgFilter();
}
ILogMsgFilter * getDefaultLogMsgFilter()
{
return new CategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, DefaultDetail, true);
}
ILogMsgFilter * getPassAllLogMsgFilter()
{
return LINK(thePassAllFilter);
}
ILogMsgFilter * getLocalLogMsgFilter()
{
return LINK(thePassLocalFilter);
}
ILogMsgFilter * getPassNoneLogMsgFilter()
{
return LINK(thePassNoneFilter);
}
ILogMsgFilter * queryPassAllLogMsgFilter()
{
return thePassAllFilter;
}
ILogMsgFilter * queryLocalLogMsgFilter()
{
return thePassLocalFilter;
}
ILogMsgFilter * queryPassNoneLogMsgFilter()
{
return thePassNoneFilter;
}
ILogMsgFilter * getCategoryLogMsgFilter(unsigned audiences, unsigned classes, LogMsgDetail maxDetail, bool local)
{
if((audiences==MSGAUD_all) && (classes==MSGCLS_all) && (maxDetail==TopDetail))
if(local)
return LINK(thePassLocalFilter);
else
return LINK(thePassAllFilter);
return new CategoryLogMsgFilter(audiences, classes, maxDetail, local);
}
ILogMsgFilter * getPIDLogMsgFilter(unsigned pid, bool local)
{
return new PIDLogMsgFilter(pid, local);
}
ILogMsgFilter * getTIDLogMsgFilter(unsigned tid, bool local)
{
return new TIDLogMsgFilter(tid, local);
}
ILogMsgFilter * getNodeLogMsgFilter(const char * name, unsigned port, bool local)
{
return new NodeLogMsgFilter(name, port, local);
}
ILogMsgFilter * getNodeLogMsgFilter(const IpAddress & ip, unsigned port, bool local)
{
return new NodeLogMsgFilter(ip, port, local);
}
ILogMsgFilter * getNodeLogMsgFilter(unsigned port, bool local)
{
return new NodeLogMsgFilter(port, local);
}
ILogMsgFilter * getIpLogMsgFilter(const char * name, bool local)
{
return new IpLogMsgFilter(name, local);
}
ILogMsgFilter * getIpLogMsgFilter(const IpAddress & ip, bool local)
{
return new IpLogMsgFilter(ip, local);
}
ILogMsgFilter * getIpLogMsgFilter(bool local)
{
return new IpLogMsgFilter(local);
}
ILogMsgFilter * getJobLogMsgFilter(LogMsgJobId job, bool local)
{
return new JobLogMsgFilter(job, local);
}
ILogMsgFilter * getUserLogMsgFilter(LogMsgUserId user, bool local)
{
return new UserLogMsgFilter(user, local);
}
ILogMsgFilter * getSessionLogMsgFilter(LogMsgSessionId session, bool local)
{
return new SessionLogMsgFilter(session, local);
}
ILogMsgFilter * getComponentLogMsgFilter(unsigned component, bool local)
{
return new ComponentLogMsgFilter(component, local);
}
ILogMsgFilter * getRegexLogMsgFilter(const char *regex, bool local)
{
return new RegexLogMsgFilter(regex, local);
}
ILogMsgFilter * getNotLogMsgFilter(ILogMsgFilter * arg)
{
return new NotLogMsgFilter(arg);
}
ILogMsgFilter * getNotLogMsgFilterOwn(ILogMsgFilter * arg)
{
ILogMsgFilter * ret = new NotLogMsgFilter(arg);
arg->Release();
return ret;
}
ILogMsgFilter * getAndLogMsgFilter(ILogMsgFilter * arg1, ILogMsgFilter * arg2)
{
return new AndLogMsgFilter(arg1, arg2);
}
ILogMsgFilter * getAndLogMsgFilterOwn(ILogMsgFilter * arg1, ILogMsgFilter * arg2)
{
ILogMsgFilter * ret = new AndLogMsgFilter(arg1, arg2);
arg1->Release();
arg2->Release();
return ret;
}
ILogMsgFilter * getOrLogMsgFilter(ILogMsgFilter * arg1, ILogMsgFilter * arg2)
{
return new OrLogMsgFilter(arg1, arg2);
}
ILogMsgFilter * getOrLogMsgFilterOwn(ILogMsgFilter * arg1, ILogMsgFilter * arg2)
{
ILogMsgFilter * ret = new OrLogMsgFilter(arg1, arg2);
arg1->Release();
arg2->Release();
return ret;
}
ILogMsgFilter * getSwitchLogMsgFilterOwn(ILogMsgFilter * switchFilter, ILogMsgFilter * yesFilter, ILogMsgFilter * noFilter)
{
ILogMsgFilter * ret = new SwitchLogMsgFilter(switchFilter, yesFilter, noFilter);
switchFilter->Release();
yesFilter->Release();
noFilter->Release();
return ret;
}
ILogMsgHandler * getHandleLogMsgHandler(FILE * handle, unsigned fields, bool writeXML)
{
if(writeXML)
return new HandleLogMsgHandlerXML(handle, fields);
return new HandleLogMsgHandlerTable(handle, fields);
}
ILogMsgHandler * getFileLogMsgHandler(const char * filename, const char * headertext, unsigned fields, bool writeXML, bool append, bool flushes)
{
if(writeXML)
return new FileLogMsgHandlerXML(filename, headertext, fields, append, flushes);
return new FileLogMsgHandlerTable(filename, headertext, fields, append, flushes);
}
ILogMsgHandler * getRollingFileLogMsgHandler(const char * filebase, const char * fileextn, unsigned fields, bool append, bool flushes, const char *initialName, const char *alias, bool daily)
{
return new RollingFileLogMsgHandler(filebase, fileextn, fields, append, flushes, initialName, alias, daily);
}
ILogMsgHandler * getBinLogMsgHandler(const char * filename, bool append)
{
return new BinLogMsgHandler(filename, append);
}
void installLogMsgFilterSwitch(ILogMsgHandler * handler, ILogMsgFilter * switchFilter, ILogMsgFilter * newFilter)
{
queryLogMsgManager()->changeMonitorFilterOwn(handler, getSwitchLogMsgFilterOwn(switchFilter, newFilter, queryLogMsgManager()->getMonitorFilter(handler)));
}
ILogMsgHandler * getLogMsgHandlerFromPTree(IPropertyTree * tree)
{
StringBuffer type;
tree->getProp("@type", type);
unsigned fields = MSGFIELD_all;
char const * fstr = tree->queryProp("@fields");
if(fstr)
if(isdigit(fstr[0]))
fields = atoi(fstr);
else
fields = LogMsgFieldsFromAbbrevs(fstr);
if(strcmp(type.str(), "stderr")==0)
return getHandleLogMsgHandler(stderr, fields, tree->hasProp("@writeXML"));
else if(strcmp(type.str(), "file")==0)
{
StringBuffer filename;
tree->getProp("@filename", filename);
if(tree->hasProp("@headertext"))
{
StringBuffer headertext;
tree->getProp("@headertext", headertext);
return getFileLogMsgHandler(filename.str(), headertext.str(), fields, !(tree->hasProp("@writeTable")), tree->hasProp("@append"), tree->hasProp("@flushes"));
}
else
return getFileLogMsgHandler(filename.str(), 0, fields, !(tree->hasProp("@writeTable")), tree->hasProp("@append"), tree->hasProp("@flushes"));
}
else if(strcmp(type.str(), "binary")==0)
{
StringBuffer filename;
tree->getProp("@filename", filename);
return getBinLogMsgHandler(filename.str(), tree->hasProp("@append"));
}
else assertex(!"getLogMsgFilterFromPTree : unrecognized LogMsgHandler type");
return LINK(theStderrHandler);
}
ILogMsgHandler * attachStandardFileLogMsgMonitor(const char * filename, const char * headertext, unsigned fields, unsigned audiences, unsigned classes, LogMsgDetail detail, bool writeXML, bool append, bool flushes, bool local)
{
#ifdef FILE_LOG_ENABLES_QUEUEUING
queryLogMsgManager()->enterQueueingMode();
#endif
ILogMsgFilter * filter = getCategoryLogMsgFilter(audiences, classes, detail, local);
ILogMsgHandler * handler = getFileLogMsgHandler(filename, headertext, fields, writeXML, append, flushes);
queryLogMsgManager()->addMonitorOwn(handler, filter);
return handler;
}
ILogMsgHandler * attachStandardBinLogMsgMonitor(const char * filename, unsigned audiences, unsigned classes, LogMsgDetail detail, bool append, bool local)
{
#ifdef FILE_LOG_ENABLES_QUEUEUING
queryLogMsgManager()->enterQueueingMode();
#endif
ILogMsgFilter * filter = getCategoryLogMsgFilter(audiences, classes, detail, local);
ILogMsgHandler * handler = getBinLogMsgHandler(filename, append);
queryLogMsgManager()->addMonitorOwn(handler, filter);
return handler;
}
ILogMsgHandler * attachStandardHandleLogMsgMonitor(FILE * handle, unsigned fields, unsigned audiences, unsigned classes, LogMsgDetail detail, bool writeXML, bool local)
{
ILogMsgFilter * filter = getCategoryLogMsgFilter(audiences, classes, detail, local);
ILogMsgHandler * handler = getHandleLogMsgHandler(handle, fields, writeXML);
queryLogMsgManager()->addMonitorOwn(handler, filter);
return handler;
}
ILogMsgHandler * attachLogMsgMonitorFromPTree(IPropertyTree * tree)
{
Owned handlertree = tree->getPropTree("handler");
Owned filtertree = tree->getPropTree("filter");
ILogMsgHandler * handler = getLogMsgHandlerFromPTree(handlertree);
ILogMsgFilter * filter = getLogMsgFilterFromPTree(filtertree);
queryLogMsgManager()->addMonitorOwn(handler, filter);
return handler;
}
void attachManyLogMsgMonitorsFromPTree(IPropertyTree * tree)
{
Owned iter = tree->getElements("monitor");
ForEach(*iter)
attachLogMsgMonitorFromPTree(&(iter->query()));
}
// Standard categories and unknown jobInfo
const LogMsgCategory MCdisaster(MSGAUD_all, MSGCLS_disaster);
const LogMsgCategory MCuserError(MSGAUD_user, MSGCLS_error);
const LogMsgCategory MCoperatorError(MSGAUD_operator, MSGCLS_error);
const LogMsgCategory MCinternalError((LogMsgAudience)(MSGAUD_internal & MSGAUD_programmer), MSGCLS_error, 1);
const LogMsgCategory MCuserWarning(MSGAUD_user, MSGCLS_warning);
const LogMsgCategory MCoperatorWarning(MSGAUD_operator, MSGCLS_warning);
const LogMsgCategory MCinternalWarning((LogMsgAudience)(MSGAUD_internal & MSGAUD_programmer), MSGCLS_warning, 1);
const LogMsgCategory MCuserProgress(MSGAUD_user, MSGCLS_progress);
const LogMsgCategory MCoperatorProgress(MSGAUD_operator, MSGCLS_progress);
const LogMsgCategory MCdebugProgress(MSGAUD_programmer, MSGCLS_progress);
const LogMsgCategory MCdebugInfo(MSGAUD_programmer, MSGCLS_information);
const LogMsgCategory MCstats(MSGAUD_performance, MSGCLS_information);
const LogMsgCategory MCevent(MSGAUD_monitor, MSGCLS_event);
const LogMsgCategory MClegacy(MSGAUD_legacy, MSGCLS_legacy, DefaultDetail);
const LogMsgJobInfo unknownJob(UnknownJob, UnknownUser);
// Calls to make, remove, and return the manager, standard handler, pass all/none filters, reporter array
PassAllLogMsgFilter * thePassAllFilter;
PassLocalLogMsgFilter * thePassLocalFilter;
PassNoneLogMsgFilter * thePassNoneFilter;
HandleLogMsgHandlerTable * theStderrHandler;
CLogMsgManager * theManager;
CSysLogEventLogger * theSysLogEventLogger;
LogMsgComponentReporter * theReporters[MSGCOMP_NUMBER];
LogMsgPrepender * thePrepender;
MODULE_INIT(INIT_PRIORITY_JLOG)
{
thePassAllFilter = new PassAllLogMsgFilter();
thePassLocalFilter = new PassLocalLogMsgFilter();
thePassNoneFilter = new PassNoneLogMsgFilter();
theStderrHandler = new HandleLogMsgHandlerTable(stderr, MSGFIELD_STANDARD);
theSysLogEventLogger = new CSysLogEventLogger;
theManager = new CLogMsgManager();
theManager->resetMonitors();
for(unsigned compo = 0; compo
#include "jelog.h"
struct AuditTypeWin32Data
{
public:
unsigned eventtype;
unsigned categoryid;
unsigned eventid;
};
#define CATEGORY_AUDIT_FUNCTION_REQUIRED
#define AUDIT_TYPES_BEGIN AuditTypeWin32Data auditTypeDataMap[NUM_AUDIT_TYPES+1] = {
#define MAKE_AUDIT_TYPE(name, type, categoryid, eventid, level) {type, categoryid, eventid},
#define AUDIT_TYPES_END {0, 0, 0} };
#include "jelogtype.hpp"
#undef CATEGORY_AUDIT_FUNCTION_REQUIRED
#undef AUDIT_TYPES_BEGIN
#undef MAKE_AUDIT_TYPE
#undef AUDIT_TYPES_END
CSysLogEventLogger::CSysLogEventLogger() : hEventLog(0)
{
}
bool CSysLogEventLogger::log(AuditType auditType, char const * msg, size32_t datasize, void const * data)
{
assertex(auditType < NUM_AUDIT_TYPES);
AuditTypeWin32Data const & typeData = auditTypeDataMap[auditType];
return win32Report(typeData.eventtype, typeData.categoryid, typeData.eventid, msg, datasize, data);
}
bool CSysLogEventLogger::win32Report(unsigned eventtype, unsigned category, unsigned eventid, const char * msg, size32_t datasize, const void * data)
{
if (hEventLog==0) {
// MORE - this doesn't work on Vista/Win7 as can't copy to system32...
// Perhaps we should just kill this code
char path[_MAX_PATH+1];
GetEnvironmentVariable("SystemRoot",path,sizeof(path));
strcat(path,"\\System32\\JELOG.dll");
Owned file = createIFile(path);
try {
if (!file->exists()) {
char src[_MAX_PATH+1];
LPTSTR tail;
DWORD res = SearchPath(NULL,"JELOG.DLL",NULL,sizeof(src),src,&tail);
if (res>0)
copyFile(path,src);
else
throw MakeOsException(GetLastError());
}
}
catch (IException *e)
{
EXCLOG(e, "reportEventLog: Could not install JELOG.DLL");
hEventLog=(HANDLE)-1;
e->Release();
return false;
}
HKEY hk;
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\Seisint",
NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, NULL)==0) {
DWORD sizedata = 0;
DWORD type = REG_EXPAND_SZ;
if ((RegQueryValueEx(hk,"EventMessageFile",NULL, &type, NULL, &sizedata)!=0)||!sizedata) {
StringAttr str("%SystemRoot%\\System32\\JELOG.dll");
RegSetValueEx(hk,"EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) str.get(), str.length() + 1);
RegSetValueEx(hk,"CategoryMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) str.get(), str.length() + 1);
DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD));
dwData = 16;
RegSetValueEx(hk, "CategoryCount", 0, REG_DWORD, (LPBYTE) &dwData, sizeof(DWORD));
}
RegCloseKey(hk);
}
hEventLog = RegisterEventSource(NULL,"Seisint");
if (!hEventLog) {
ERRLOG("reportEventLog: Could not register Seisint event source");
hEventLog=(HANDLE)-1;
return false;
}
}
if (hEventLog==(HANDLE)-1)
return false;
assertex((unsigned)eventtype<=16);
if (!data)
datasize = 0;
else if (!datasize)
data = NULL;
#if 1 //useful for debugging...
ReportEvent(hEventLog, eventtype, category, eventid, NULL, 1, datasize, &msg, (LPVOID)data);
#else
if(datasize)
{
char * buff = (char *)malloc(datasize*3+1);
unsigned char const * cdata = (unsigned char *)data;
unsigned i;
for(i=0; i
#define CATEGORY_AUDIT_FUNCTION_REQUIRED
#define AUDIT_TYPES_BEGIN int auditTypeDataMap[NUM_AUDIT_TYPES+1] = {
#define MAKE_AUDIT_TYPE(name, type, categoryid, eventid, level) level,
#define AUDIT_TYPES_END 0 };
#include "jelogtype.hpp"
#undef CATEGORY_AUDIT_FUNCTION_REQUIRED
#undef AUDIT_TYPES_BEGIN
#undef MAKE_AUDIT_TYPE
#undef AUDIT_TYPES_END
CSysLogEventLogger::CSysLogEventLogger() : dataLogUsed(false), dataLogName(0), dataLogFile(-1)
{
const char * processName = queryCurrentProcessName();
if (!processName||!*processName)
processName = "hpcc";
openlog(processName, LOG_PID, LOG_USER);
}
CSysLogEventLogger::~CSysLogEventLogger()
{
if(dataLogFile != -1)
close(dataLogFile);
if(dataLogName)
delete [] dataLogName;
closelog();
}
bool CSysLogEventLogger::log(AuditType auditType, const char *msg, size32_t datasize, const void * data)
{
assertex(auditType < NUM_AUDIT_TYPES);
int level = auditTypeDataMap[auditType];
return linuxReport(level, msg, datasize, data);
}
bool CSysLogEventLogger::linuxReport(int level, const char * msg, size32_t datasize, const void * data)
{
if (!data)
datasize = 0;
else if (!datasize)
data = NULL;
bool ret = true;
#if 1 //useful for debugging...
if(data)
{
if(!dataLogUsed)
openDataLog();
if(dataLogFile != -1)
{
int fpos = writeDataLog(datasize, (byte const *)data);
if(fpos != -1)
syslog(level, "%s [0x%X bytes of data at %s byte 0x%X]", msg, datasize, dataLogName, fpos);
else
syslog(level, "%s [could not write 0x%X bytes of data to %s]", msg, datasize, dataLogName);
}
else
{
ret = false;
syslog(level, "%s [could not open file of form %s to write data]", msg, AUDIT_DATA_LOG_TEMPLATE);
}
}
else
{
syslog(level, "%s", msg);
}
#else
if(datasize)
{
char * buff = (char *)malloc(datasize*3+1);
unsigned char const * cdata = (unsigned char *)data;
unsigned i;
for(i=0; i 0)
{
size32_t written = write(dataLogFile, data, datasize);
if(written == -1)
return -1;
data += written;
datasize -= written;
}
#ifdef __linux__
fdatasync(dataLogFile);
#endif
return fpos;
}
#endif
void SysLogMsgHandler::handleMessage(const LogMsg & msg) const
{
AuditType type = categoryToAuditType(msg.queryCategory());
StringBuffer text;
msg.toStringPlain(text, fields);
logger->log(type, text.str());
}
void SysLogMsgHandler::addToPTree(IPropertyTree * tree) const
{
IPropertyTree * handlerTree = createPTree(ipt_caseInsensitive);
handlerTree->setProp("@type", "audit");
tree->addPropTree("handler", handlerTree);
}
class DummyLogCtx : implements IContextLogger
{
public:
// It's a static object - we don't want to actually link-count it...
virtual void Link() const {}
virtual bool Release() const { return false; }
virtual void CTXLOG(const char *format, ...) const
{
va_list args;
va_start(args, format);
CTXLOGva(format, args);
va_end(args);
}
virtual void CTXLOGva(const char *format, va_list args) const
{
StringBuffer ss;
ss.valist_appendf(format, args);
DBGLOG("%s", ss.str());
}
virtual void CTXLOGa(unsigned activityId, const char *text) const
{
DBGLOG("[%d] %s", activityId, text);
}
virtual StringBuffer &getLogPrefix(StringBuffer &ret) const
{
return ret;
}
virtual void logOperatorException(IException *E, const char *file, unsigned line, const char *format, ...) const
{
va_list args;
va_start(args, format);
logOperatorExceptionVA(E, file, line, format, args);
va_end(args);
}
virtual void logOperatorExceptionVA(IException *E, const char *file, unsigned line, const char *format, va_list args) const
{
StringBuffer ss;
ss.append("ERROR");
if (E)
ss.append(": ").append(E->errorCode());
if (file)
ss.appendf(": %s(%d) ", file, line);
if (E)
E->errorMessage(ss.append(": "));
if (format)
ss.append(": ").valist_appendf(format, args);
LOG(MCoperatorProgress, unknownJob, "%s", ss.str());
}
virtual bool isIntercepted() const
{
return false;
}
virtual void noteStatistic(unsigned statCode, unsigned __int64 value, unsigned count) const
{
}
virtual unsigned queryTraceLevel() const
{
return 0;
}
} dummyContextLogger;
extern jlib_decl const IContextLogger &queryDummyContextLogger()
{
return dummyContextLogger;
}
extern jlib_decl void UseSysLogForOperatorMessages(bool use)
{
static ILogMsgHandler *msgHandler=NULL;
if (use==(msgHandler!=NULL))
return;
if (use) {
msgHandler = getSysLogMsgHandler();
ILogMsgFilter * operatorFilter = getCategoryLogMsgFilter(MSGAUD_operator|MSGAUD_monitor, MSGCLS_all, DefaultDetail, true);
queryLogMsgManager()->addMonitorOwn(msgHandler, operatorFilter);
}
else {
queryLogMsgManager()->removeMonitor(msgHandler);
msgHandler = NULL;
}
}
extern jlib_decl void AuditSystemAccess(const char *userid, bool success, char const * msg,...)
{
va_list args;
va_start(args, msg);
VStringBuffer s("User %s: ", userid);
SYSLOG((success) ? AUDIT_TYPE_ACCESS_SUCCESS : AUDIT_TYPE_ACCESS_FAILURE, s.valist_appendf(msg, args).str());
va_end(args);
}
//--------------------------------------------------------------
class jlib_decl CComponentLogFileCreator : implements IComponentLogFileCreator, public CInterface
{
private:
StringBuffer component;
//filename parts
StringBuffer prefix;
StringBuffer name;
StringBuffer postfix;
StringBuffer extension;
StringBuffer fullFileSpec;
bool createAlias;
StringBuffer aliasName;
StringBuffer logDirSubdir;
bool rolling;
//ILogMsgHandler fields
bool append;
bool flushes;
unsigned msgFields;
//ILogMsgFilter fields
unsigned msgAudiences;
unsigned msgClasses;
LogMsgDetail maxDetail;
bool local;
//available after logging started
StringBuffer logDir; //access via queryLogDir()
StringBuffer aliasFileSpec; //access via queryLogDir()
StringBuffer expandedLogSpec;//access via queryLogFileSpec()
private:
void setDefaults()
{
rolling = true;
append = true;
flushes = true;
msgFields = MSGFIELD_STANDARD;
msgAudiences = MSGAUD_all;
msgClasses = MSGCLS_all;
maxDetail = DefaultDetail;
name.set(component);//logfile defaults to component name. Change via setName(), setPrefix() and setPostfix()
extension.set(".log");
local = false;
createAlias = true;
}
public:
IMPLEMENT_IINTERFACE;
CComponentLogFileCreator(IPropertyTree * _properties, const char *_component) : component(_component)
{
setDefaults();
if (!getConfigurationDirectory(_properties->queryPropTree("Directories"), "log", _component, _properties->queryProp("@name"), logDir))
_properties->getProp("@logDir", logDir);
}
CComponentLogFileCreator(const char *_logDir, const char *_component) : logDir(_logDir), component(_component)
{
setDefaults();
}
CComponentLogFileCreator(const char *_component) : component(_component)
{
setDefaults();
if (!getConfigurationDirectory(NULL, "log", _component, _component, logDir))
{
appendCurrentDirectory(logDir,false);
}
}
//set methods
void setExtension(const char * _ext) { extension.set(_ext); }
void setPrefix(const char * _prefix) { prefix.set(_prefix); }
void setName(const char * _name) { name.set(_name); }
void setCompleteFilespec(const char * _fs){ fullFileSpec.set(_fs); }
void setPostfix(const char * _postfix) { postfix.set(_postfix); }
void setCreateAliasFile(bool _create) { createAlias = _create; }
void setAliasName(const char * _aliasName) { aliasName.set(_aliasName); }
void setLogDirSubdir(const char * _subdir) { logDirSubdir.set(_subdir); }
void setRolling(const bool _rolls) { rolling = _rolls; }
//ILogMsgHandler fields
void setAppend(const bool _append) { append = _append; }
void setFlushes(const bool _flushes) { flushes = _flushes; }
void setMsgFields(const unsigned _fields){ msgFields = _fields; }
//ILogMsgFilter fields
void setMsgAudiences(const unsigned _audiences){ msgAudiences = _audiences; }
void setMsgClasses(const unsigned _classes) { msgClasses = _classes; }
void setMaxDetail(const LogMsgDetail _maxDetail) { maxDetail = _maxDetail; }
void setLocal(const bool _local) { local = _local; }
//query methods (not valid until logging started)
const char * queryLogDir() { return logDir.str(); }
const char * queryLogFileSpec() { return expandedLogSpec.str(); }
const char * queryAliasFileSpec() { return aliasFileSpec.str(); }
ILogMsgHandler * beginLogging()
{
//build directory path
StringBuffer logFileSpec;
if (!fullFileSpec.length())//user specify complete logfile specification?
{
if (!logDir.length())
{
appendCurrentDirectory(logDir,false).append(PATHSEPSTR).append("logs");
WARNLOG("No logfile directory specified - logs will be written locally to %s", logDir.str());
}
makeAbsolutePath(logDir);
//build log file name (without date string or extension)
StringBuffer logFileName;
if (prefix.length())
logFileName.append(prefix).append(".");
logFileName.append(name);
if (postfix.length())
logFileName.append(".").append(postfix);
//build log file spec
if (logDirSubdir.length())
logDir.append(PATHSEPCHAR).append(logDirSubdir);//user specified subfolder
logFileSpec.append(logDir).append(PATHSEPCHAR).append(logFileName);
//build alias file spec
if (createAlias)
{
if (aliasName.length()==0)
aliasName.set(logFileName);
aliasFileSpec.append(logDir).append(PATHSEPCHAR).append(aliasName).append(extension);
}
}
else
makeAbsolutePath(fullFileSpec);
ILogMsgHandler * lmh;
if (rolling)
lmh = getRollingFileLogMsgHandler(logFileSpec.str(), extension, msgFields, append, flushes, NULL, aliasFileSpec.str(), true);
else
{
StringBuffer lfs;
if (fullFileSpec.length())
lfs.set(fullFileSpec);
else
lfs.set(logFileSpec.append(extension).str());
lmh = getFileLogMsgHandler(lfs.str(), NULL, msgFields, false);
}
lmh->getLogName(expandedLogSpec);
queryLogMsgManager()->addMonitorOwn( lmh, getCategoryLogMsgFilter(msgAudiences, msgClasses, maxDetail, local));
return lmh;
}
};
IComponentLogFileCreator * createComponentLogFileCreator(IPropertyTree * _properties, const char *_component)
{
return new CComponentLogFileCreator(_properties, _component);
}
IComponentLogFileCreator * createComponentLogFileCreator(const char *_logDir, const char *_component)
{
return new CComponentLogFileCreator(_logDir, _component);
}
IComponentLogFileCreator * createComponentLogFileCreator(const char *_component)
{
return new CComponentLogFileCreator(_component);
}