Arquivos
hhvm/hphp/util/logger.cpp
T
Tim Starling 998951619f update copyright date
We did not intend to imply our copyrights last forever

Closes #759
2013-06-03 12:43:56 -07:00

306 linhas
9.4 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/util/logger.h"
#include "hphp/util/base.h"
#include "hphp/util/stack_trace.h"
#include "hphp/util/process.h"
#include "hphp/util/exception.h"
#include "util.h"
#include "hphp/util/text_color.h"
#include <syslog.h>
#define IMPLEMENT_LOGLEVEL(LOGLEVEL) \
void Logger::LOGLEVEL(const char *fmt, ...) { \
if (LogLevel < Log ## LOGLEVEL) return; \
va_list ap; va_start(ap, fmt); \
Log(Log ## LOGLEVEL, fmt, ap); \
va_end(ap); \
} \
void Logger::LOGLEVEL(const std::string &msg) { \
if (LogLevel < Log ## LOGLEVEL) return; \
Log(Log ## LOGLEVEL, msg, nullptr); \
}
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
IMPLEMENT_LOGLEVEL(Error);
IMPLEMENT_LOGLEVEL(Warning);
IMPLEMENT_LOGLEVEL(Info);
IMPLEMENT_LOGLEVEL(Verbose);
///////////////////////////////////////////////////////////////////////////////
bool Logger::UseSyslog = false;
bool Logger::UseLogFile = true;
bool Logger::UseCronolog = true;
bool Logger::IsPipeOutput = false;
int Logger::DropCacheChunkSize = (1 << 20);
FILE *Logger::Output = nullptr;
Cronolog Logger::cronOutput;
Logger::LogLevelType Logger::LogLevel = LogInfo;
std::atomic<int> Logger::bytesWritten(0);
int Logger::prevBytesWritten = 0;
bool Logger::LogHeader = false;
bool Logger::LogNativeStackTrace = true;
std::string Logger::ExtraHeader;
int Logger::MaxMessagesPerRequest = -1;
bool Logger::Escape = true;
IMPLEMENT_THREAD_LOCAL(Logger::ThreadData, Logger::s_threadData);
Logger *Logger::s_logger = new Logger();
void Logger::Log(LogLevelType level, const char *fmt, va_list ap) {
if (!IsEnabled()) return;
string msg;
Util::string_vsnprintf(msg, fmt, ap);
Log(level, msg, nullptr);
}
void Logger::LogEscapeMore(LogLevelType level, const char *fmt, va_list ap) {
if (!IsEnabled()) return;
string msg;
Util::string_vsnprintf(msg, fmt, ap);
Log(level, msg, nullptr, true, true);
}
void Logger::Log(LogLevelType level, const char *type, const Exception &e,
const char *file /* = NULL */, int line /* = 0 */) {
s_logger->log(level, type, e, file, line);
}
void Logger::log(LogLevelType level, const char *type, const Exception &e,
const char *file /* = NULL */, int line /* = 0 */) {
if (!IsEnabled()) return;
std::string msg = type;
msg += e.getMessage();
if (file && file[0]) {
std::ostringstream os;
os << " in " << file << " on line " << line;
msg += os.str();
}
Log(level, msg, nullptr);
}
void Logger::OnNewRequest() {
ThreadData *threadData = s_threadData.get();
++threadData->request;
threadData->message = 0;
}
void Logger::ResetRequestCount() {
ThreadData *threadData = s_threadData.get();
threadData->request = 0;
threadData->message = 0;
}
void Logger::Log(LogLevelType level, const std::string &msg,
const StackTrace *stackTrace,
bool escape /* = false */, bool escapeMore /* = false */) {
s_logger->log(level, msg, stackTrace, escape, escapeMore);
}
FILE *Logger::GetStandardOut(LogLevelType level) {
return level <= LogWarning ? stderr : stdout;
}
int Logger::GetSyslogLevel(LogLevelType level) {
switch (level) {
case LogError: return LOG_ERR;
case LogWarning: return LOG_WARNING;
case LogInfo: return LOG_INFO;
case LogVerbose: return LOG_DEBUG;
default: return LOG_NOTICE;
}
}
void Logger::log(LogLevelType level, const std::string &msg,
const StackTrace *stackTrace,
bool escape /* = false */, bool escapeMore /* = false */) {
if (Logger::Escape) {
escape = true;
}
assert(!escapeMore || escape);
ThreadData *threadData = s_threadData.get();
if (++threadData->message > MaxMessagesPerRequest &&
MaxMessagesPerRequest >= 0) {
return;
}
boost::shared_ptr<StackTrace> deleter;
if (LogNativeStackTrace && stackTrace == nullptr) {
deleter = boost::shared_ptr<StackTrace>(new StackTrace());
stackTrace = deleter.get();
}
if (UseSyslog) {
syslog(GetSyslogLevel(level), "%s", msg.c_str());
}
FILE *stdf = GetStandardOut(level);
if (UseLogFile) {
FILE *f;
if (UseCronolog) {
f = cronOutput.getOutputFile();
if (!f) f = stdf;
} else {
f = Output ? Output : stdf;
}
string header, sheader;
if (LogHeader) {
header = GetHeader();
if (LogNativeStackTrace) {
sheader = header + "[" + stackTrace->hexEncode(5) + "] ";
} else {
sheader = header;
}
}
const char *escaped = escape ? EscapeString(msg) : msg.c_str();
const char *ending = escapeMore ? "\\n" : "\n";
int bytes;
if (f == stdf && s_stderr_color) {
bytes =
fprintf(f, "%s%s%s%s%s",
s_stderr_color, sheader.c_str(), msg.c_str(), ending,
ANSI_COLOR_END);
} else {
bytes = fprintf(f, "%s%s%s", sheader.c_str(), escaped, ending);
}
bytesWritten.fetch_add(bytes, std::memory_order_relaxed);
FILE *tf = threadData->log;
if (tf) {
threadData->bytesWritten +=
fprintf(tf, "%s%s%s", header.c_str(), escaped, ending);
fflush(tf);
threadData->prevBytesWritten =
checkDropCache(threadData->bytesWritten,
threadData->prevBytesWritten,
tf);
}
if (threadData->hook) {
threadData->hook(header.c_str(), msg.c_str(), ending,
threadData->hookData);
}
if (escape) {
free((void*)escaped);
}
fflush(f);
if (UseCronolog || (Output && !Logger::IsPipeOutput)) {
prevBytesWritten =
checkDropCache(bytesWritten.load(std::memory_order_relaxed),
prevBytesWritten, f);
}
}
}
std::string Logger::GetHeader() {
static std::string host = Process::GetHostName();
static pid_t pid = Process::GetProcessId();
time_t now = time(nullptr);
char snow[64];
ctime_r(&now, snow);
// Eliminate trailing newilne from ctime_r.
snow[24] = '\0';
char header[128];
ThreadData *threadData = s_threadData.get();
snprintf(header, sizeof(header), "[%s] [hphp] [%lld:%llx:%d:%06d%s] ",
snow,
(unsigned long long)pid,
(unsigned long long)Process::GetThreadId(),
threadData->request, threadData->message,
ExtraHeader.c_str());
return header;
}
char *Logger::EscapeString(const std::string &msg) {
char *new_str = (char *)malloc((msg.size() << 2) + 1);
const char *source;
const char *end;
char *target;
for (source = msg.c_str(), end = source + msg.size(), target = new_str;
source < end && *source; source++) {
char c = *source;
if ((unsigned char) c < 32 || (unsigned char) c > 126) {
*target++ = '\\';
switch (c) {
case '\n': *target++ = 'n'; break;
case '\t': *target++ = 't'; break;
case '\r': *target++ = 'r'; break;
case '\v': *target++ = 'v'; break;
case '\b': *target++ = 'b'; break;
default: target += sprintf(target, "x%02x", (unsigned char)c);
}
} else if (c == '\\') {
*target++ = c;
*target++ = c;
} else {
*target++ = c;
}
}
*target = 0;
return new_str;
}
int Logger::checkDropCache(int bytesWritten, int prevBytesWritten,
FILE *f) {
if (bytesWritten - prevBytesWritten > Logger::DropCacheChunkSize) {
Util::drop_cache(f);
return bytesWritten;
}
return prevBytesWritten;
}
bool Logger::SetThreadLog(const char *file) {
return (s_threadData->log = fopen(file, "a")) != nullptr;
}
void Logger::ClearThreadLog() {
ThreadData *threadData = s_threadData.get();
if (threadData->log) {
fclose(threadData->log);
}
threadData->log = nullptr;
}
void Logger::SetThreadHook(PFUNC_LOG func, void *data) {
ThreadData *threadData = s_threadData.get();
threadData->hook = func;
threadData->hookData = data;
}
void Logger::SetNewOutput(FILE *output) {
Logger::UseCronolog = false;
ThreadData *threadData = s_threadData.get();
if (threadData->log) {
fclose(threadData->log);
threadData->log = output;
} else {
if (Output) fclose(Output);
Output = output;
}
}
///////////////////////////////////////////////////////////////////////////////
}