2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/server/log-writer.h"
23 #include "hphp/util/logger.h"
24 #include "hphp/runtime/base/init-fini-node.h"
28 ///////////////////////////////////////////////////////////////////////////////
29 std::string
FieldGenerator::escapeData(const char* s
, int len
) {
30 static const char digits
[] = "0123456789abcdef";
31 std::ostringstream out
;
32 for (int i
= 0; i
< len
; i
++) {
33 unsigned char uc
= *s
++;
35 case '"': out
<< "\\\""; break;
36 case '\\': out
<< "\\\\"; break;
37 case '\b': out
<< "\\b"; break;
38 case '\f': out
<< "\\f"; break;
39 case '\n': out
<< "\\n"; break;
40 case '\r': out
<< "\\r"; break;
41 case '\t': out
<< "\\t"; break;
43 if (uc
>= ' ' && (uc
& 127) == uc
) {
46 out
<< "\\x" << digits
[(uc
>> 4) & 15] << digits
[(uc
>> 0) & 15];
54 ClassicWriter::~ClassicWriter() {
55 if (m_channel
== LogChannel::REGULAR
) {
56 if (m_logdata
.file
[0] == '|') {
64 void ClassicWriter::init(const std::string
& username
,
65 AccessLog::GetThreadDataFunc fn
) {
67 if (m_channel
== LogChannel::CRONOLOG
) {
68 assertx(!m_logdata
.file
.empty());
69 m_cronolog
= std::make_unique
<Cronolog
>();
70 if (m_logdata
.file
.find('%') != std::string::npos
) {
71 m_cronolog
->m_template
= m_logdata
.file
;
72 m_cronolog
->setPeriodicity();
73 if (m_logdata
.periodMultiplier
) {
74 m_cronolog
->m_periodMultiple
= m_logdata
.periodMultiplier
;
77 m_cronolog
->m_linkName
= m_logdata
.symLink
;
78 Cronolog::changeOwner(username
, m_logdata
.symLink
);
80 m_cronolog
->m_file
= fopen(m_logdata
.file
.c_str(), "a");
82 } else if (m_channel
== LogChannel::REGULAR
) {
83 assertx(!m_logdata
.file
.empty());
84 if (m_logdata
.file
[0] == '|') {
85 std::string plog
= m_logdata
.file
.substr(1);
86 m_filelog
= popen(plog
.c_str(), "w");
88 m_filelog
= fopen(m_logdata
.file
.c_str(), "a");
91 Logger::Error("Couldn't open access log file %s", m_logdata
.file
.c_str());
96 void ClassicWriter::write(Transport
* transport
, const VirtualHost
* vhost
) {
97 auto outfile
= getOutputFile();
100 std::ostringstream out
;
101 const auto* format
= m_logdata
.format
.c_str();
102 FieldGenerator fieldGen
{
105 (m_threadDataFn
? m_threadDataFn() : nullptr)
107 while ((c
= *format
++)) {
112 if (parseConditions(format
, transport
->getResponseCode())) {
114 std::string arg
= parseArgument(format
);
115 while (!std::isalpha(*format
)) format
++;
116 if (fieldGen
.gen
<std::string
>(*format
++, arg
, field
)) {
128 auto str
= out
.str();
129 int nbytes
= fwrite(str
.data(), 1, str
.size(), outfile
);
132 if (m_channel
!= LogChannel::REGULAR
|| m_logdata
.file
[0] != '|') {
133 recordWriteAndMaybeDropCaches(outfile
, nbytes
);
137 const std::string
ClassicWriter::handle
= "__classic";
139 bool ClassicWriter::parseConditions(const char*& format
, int code
) {
140 bool wantMatch
= true;
141 if (*format
== '!') {
144 } else if (!isdigit(*format
)) {
151 bool matched
= false;
152 while (isdigit(*format
)) {
156 if (folly::to
<int>(buf
) == code
) {
162 while (!(*format
== '{' || isalpha(*format
))) {
165 return wantMatch
== matched
;
168 std::string
ClassicWriter::parseArgument(const char*& format
) {
169 if (*format
!= '{') return std::string();
171 const char *start
= format
;
172 while (*format
!= '}') { format
++; }
173 std::string
res(start
, format
- start
);
178 void ClassicWriter::skipField(const char*& format
) {
180 if (*format
== '{') {
181 while (*format
!= '}') { format
++; }
184 // Find control letter
185 while (!isalpha(*format
)) { format
++; }
190 static InitFiniNode
registerClassicWriter(
191 []() { AccessLogFileData::registerWriter(
192 ClassicWriter::handle
,
193 [](const AccessLogFileData
& alfd
, LogChannel chan
) {
194 return std::make_unique
<ClassicWriter
>(alfd
, chan
);
197 InitFiniNode::When::ServerPreInit
199 ///////////////////////////////////////////////////////////////////////////////