blob: 5bcab699d26b5f8ba3cdd233fc42f97dc7380d0b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if (defined(WIN32) || defined(_WIN32)) && !defined(_WIN32_WCE)
#include <apr_strings.h>
#include <log4cxx/nt/nteventlogappender.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/level.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/pool.h>
using namespace log4cxx;
using namespace log4cxx::spi;
using namespace log4cxx::helpers;
using namespace log4cxx::nt;
class CCtUserSIDHelper
{
public:
static bool FreeSid(SID* pSid)
{
return ::HeapFree(GetProcessHeap(), 0, (LPVOID)pSid) != 0;
}
static bool CopySid(SID * * ppDstSid, SID* pSrcSid)
{
bool bSuccess = false;
DWORD dwLength = ::GetLengthSid(pSrcSid);
*ppDstSid = (SID*) ::HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, dwLength);
if (::CopySid(dwLength, *ppDstSid, pSrcSid))
{
bSuccess = true;
}
else
{
FreeSid(*ppDstSid);
}
return bSuccess;
}
static bool GetCurrentUserSID(SID * * ppSid)
{
bool bSuccess = false;
// Pseudohandle so don't need to close it
HANDLE hProcess = ::GetCurrentProcess();
HANDLE hToken = NULL;
if (::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
// Get the required size
DWORD tusize = 0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &tusize);
TOKEN_USER* ptu = (TOKEN_USER*)new BYTE[tusize];
if (GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, tusize, &tusize))
{
bSuccess = CopySid(ppSid, (SID*)ptu->User.Sid);
}
CloseHandle(hToken);
delete [] ptu;
}
return bSuccess;
}
};
IMPLEMENT_LOG4CXX_OBJECT(NTEventLogAppender)
NTEventLogAppender::NTEventLogAppender() : hEventLog(NULL), pCurrentUserSID(NULL)
{
}
NTEventLogAppender::NTEventLogAppender(const LogString& server, const LogString& log, const LogString& source, const LayoutPtr& layout)
: server(server), log(log), source(source), hEventLog(NULL), pCurrentUserSID(NULL)
{
this->layout = layout;
Pool pool;
activateOptions(pool);
}
NTEventLogAppender::~NTEventLogAppender()
{
finalize();
}
void NTEventLogAppender::close()
{
if (hEventLog != NULL)
{
::DeregisterEventSource(hEventLog);
hEventLog = NULL;
}
if (pCurrentUserSID != NULL)
{
CCtUserSIDHelper::FreeSid((::SID*) pCurrentUserSID);
pCurrentUserSID = NULL;
}
}
void NTEventLogAppender::setOption(const LogString& option, const LogString& value)
{
if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SERVER"), LOG4CXX_STR("server")))
{
server = value;
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOG"), LOG4CXX_STR("log")))
{
log = value;
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SOURCE"), LOG4CXX_STR("source")))
{
source = value;
}
else
{
AppenderSkeleton::setOption(option, value);
}
}
void NTEventLogAppender::activateOptions(Pool&)
{
if (source.empty())
{
LogLog::warn(
((LogString) LOG4CXX_STR("Source option not set for appender ["))
+ name + LOG4CXX_STR("]."));
return;
}
if (log.empty())
{
log = LOG4CXX_STR("Application");
}
close();
// current user security identifier
CCtUserSIDHelper::GetCurrentUserSID((::SID**) &pCurrentUserSID);
addRegistryInfo();
LOG4CXX_ENCODE_WCHAR(wsource, source);
LOG4CXX_ENCODE_WCHAR(wserver, server);
hEventLog = ::RegisterEventSourceW(
wserver.empty() ? NULL : wserver.c_str(),
wsource.c_str());
if (hEventLog == NULL)
{
LogString msg(LOG4CXX_STR("Cannot register NT EventLog -- server: '"));
msg.append(server);
msg.append(LOG4CXX_STR("' source: '"));
msg.append(source);
LogLog::error(msg);
LogLog::error(getErrorString(LOG4CXX_STR("RegisterEventSource")));
}
}
void NTEventLogAppender::append(const LoggingEventPtr& event, Pool& p)
{
if (hEventLog == NULL)
{
LogLog::warn(LOG4CXX_STR("NT EventLog not opened."));
return;
}
LogString oss;
layout->format(oss, event, p);
wchar_t* msgs = Transcoder::wencode(oss, p);
BOOL bSuccess = ::ReportEventW(
hEventLog,
getEventType(event),
getEventCategory(event),
0x1000,
pCurrentUserSID,
1,
0,
(LPCWSTR*) &msgs,
NULL);
if (!bSuccess)
{
LogLog::error(getErrorString(LOG4CXX_STR("ReportEvent")));
}
}
/*
* Add this source with appropriate configuration keys to the registry.
*/
void NTEventLogAppender::addRegistryInfo()
{
DWORD disposition = 0;
::HKEY hkey = 0;
LogString subkey(LOG4CXX_STR("SYSTEM\\CurrentControlSet\\Services\\EventLog\\"));
subkey.append(log);
subkey.append(1, (logchar) 0x5C /* '\\' */);
subkey.append(source);
LOG4CXX_ENCODE_WCHAR(wsubkey, subkey);
long stat = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wsubkey.c_str(), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
&hkey, &disposition);
if (stat == ERROR_SUCCESS && disposition == REG_CREATED_NEW_KEY)
{
HMODULE hmodule = GetModuleHandleW(L"log4cxx");
if (hmodule == NULL)
{
hmodule = GetModuleHandleW(0);
}
wchar_t modpath[_MAX_PATH];
DWORD modlen = GetModuleFileNameW(hmodule, modpath, _MAX_PATH - 1);
if (modlen > 0)
{
modpath[modlen] = 0;
RegSetValueExW(hkey, L"EventMessageFile", 0, REG_SZ,
(LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
RegSetValueExW(hkey, L"CategoryMessageFile", 0, REG_SZ,
(LPBYTE) modpath, wcslen(modpath) * sizeof(wchar_t));
DWORD typesSupported = 7;
DWORD categoryCount = 6;
RegSetValueExW(hkey, L"TypesSupported", 0, REG_DWORD,
(LPBYTE)&typesSupported, sizeof(DWORD));
RegSetValueExW(hkey, L"CategoryCount", 0, REG_DWORD,
(LPBYTE)&categoryCount, sizeof(DWORD));
}
}
RegCloseKey(hkey);
return;
}
WORD NTEventLogAppender::getEventType(const LoggingEventPtr& event)
{
int priority = event->getLevel()->toInt();
WORD type = EVENTLOG_SUCCESS;
if (priority >= Level::INFO_INT)
{
type = EVENTLOG_INFORMATION_TYPE;
if (priority >= Level::WARN_INT)
{
type = EVENTLOG_WARNING_TYPE;
if (priority >= Level::ERROR_INT)
{
type = EVENTLOG_ERROR_TYPE;
}
}
}
return type;
}
WORD NTEventLogAppender::getEventCategory(const LoggingEventPtr& event)
{
int priority = event->getLevel()->toInt();
WORD category = 1;
if (priority >= Level::DEBUG_INT)
{
category = 2;
if (priority >= Level::INFO_INT)
{
category = 3;
if (priority >= Level::WARN_INT)
{
category = 4;
if (priority >= Level::ERROR_INT)
{
category = 5;
if (priority >= Level::FATAL_INT)
{
category = 6;
}
}
}
}
}
return category;
}
LogString NTEventLogAppender::getErrorString(const LogString& function)
{
Pool p;
enum { MSGSIZE = 5000 };
wchar_t* lpMsgBuf = (wchar_t*) p.palloc(MSGSIZE * sizeof(wchar_t));
DWORD dw = GetLastError();
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
lpMsgBuf,
MSGSIZE, NULL );
LogString msg(function);
msg.append(LOG4CXX_STR(" failed with error "));
StringHelper::toString((size_t) dw, p, msg);
msg.append(LOG4CXX_STR(": "));
Transcoder::decode(lpMsgBuf, msg);
return msg;
}
#endif // WIN32