2 * Copyright 2004-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #include <folly/experimental/logging/FileHandlerFactory.h>
17 #include <folly/experimental/logging/StreamHandlerFactory.h>
19 #include <folly/Exception.h>
20 #include <folly/experimental/TestUtil.h>
21 #include <folly/experimental/logging/AsyncFileWriter.h>
22 #include <folly/experimental/logging/GlogStyleFormatter.h>
23 #include <folly/experimental/logging/ImmediateFileWriter.h>
24 #include <folly/experimental/logging/StandardLogHandler.h>
25 #include <folly/portability/GTest.h>
26 #include <folly/test/TestUtils.h>
28 using namespace folly;
29 using folly::test::TemporaryFile;
32 void checkAsyncWriter(
33 const LogWriter* writer,
34 const char* expectedPath,
35 size_t expectedMaxBufferSize) {
36 auto asyncWriter = dynamic_cast<const AsyncFileWriter*>(writer);
37 ASSERT_TRUE(asyncWriter)
38 << "handler factory should have created an AsyncFileWriter";
39 EXPECT_EQ(expectedMaxBufferSize, asyncWriter->getMaxBufferSize());
41 // Make sure this refers to the expected output file
42 struct stat expectedStatInfo;
43 checkUnixError(stat(expectedPath, &expectedStatInfo), "stat failed");
44 struct stat actualStatInfo;
46 fstat(asyncWriter->getFile().fd(), &actualStatInfo), "fstat failed");
47 EXPECT_EQ(expectedStatInfo.st_dev, actualStatInfo.st_dev);
48 EXPECT_EQ(expectedStatInfo.st_ino, actualStatInfo.st_ino);
51 void checkAsyncWriter(
52 const LogWriter* writer,
54 size_t expectedMaxBufferSize) {
55 auto asyncWriter = dynamic_cast<const AsyncFileWriter*>(writer);
56 ASSERT_TRUE(asyncWriter)
57 << "handler factory should have created an AsyncFileWriter";
58 EXPECT_EQ(expectedMaxBufferSize, asyncWriter->getMaxBufferSize());
59 EXPECT_EQ(expectedFD, asyncWriter->getFile().fd());
62 TEST(FileHandlerFactory, pathOnly) {
63 FileHandlerFactory factory;
65 TemporaryFile tmpFile{"logging_test"};
66 auto options = LogHandlerFactory::Options{
67 make_pair("path", tmpFile.path().string()),
69 auto handler = factory.createHandler(options);
71 auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
72 ASSERT_TRUE(stdHandler);
75 std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
76 EXPECT_TRUE(formatter)
77 << "handler factory should have created a GlogStyleFormatter";
80 stdHandler->getWriter().get(),
81 tmpFile.path().string().c_str(),
82 AsyncFileWriter::kDefaultMaxBufferSize);
85 TEST(StreamHandlerFactory, stderrStream) {
86 StreamHandlerFactory factory;
88 TemporaryFile tmpFile{"logging_test"};
89 auto options = StreamHandlerFactory::Options{
90 make_pair("stream", "stderr"),
92 auto handler = factory.createHandler(options);
94 auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
95 ASSERT_TRUE(stdHandler);
98 std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
99 EXPECT_TRUE(formatter)
100 << "handler factory should have created a GlogStyleFormatter";
103 stdHandler->getWriter().get(),
105 AsyncFileWriter::kDefaultMaxBufferSize);
108 TEST(StreamHandlerFactory, stdoutWithMaxBuffer) {
109 StreamHandlerFactory factory;
111 TemporaryFile tmpFile{"logging_test"};
112 auto options = StreamHandlerFactory::Options{
113 make_pair("stream", "stdout"),
114 make_pair("max_buffer_size", "4096"),
116 auto handler = factory.createHandler(options);
118 auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
119 ASSERT_TRUE(stdHandler);
122 std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
123 EXPECT_TRUE(formatter)
124 << "handler factory should have created a GlogStyleFormatter";
126 checkAsyncWriter(stdHandler->getWriter().get(), STDOUT_FILENO, 4096);
129 TEST(FileHandlerFactory, pathWithMaxBufferSize) {
130 FileHandlerFactory factory;
132 TemporaryFile tmpFile{"logging_test"};
133 auto options = LogHandlerFactory::Options{
134 make_pair("path", tmpFile.path().string()),
135 make_pair("max_buffer_size", "4096000"),
137 auto handler = factory.createHandler(options);
139 auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
140 ASSERT_TRUE(stdHandler);
143 std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
144 EXPECT_TRUE(formatter)
145 << "handler factory should have created a GlogStyleFormatter";
148 stdHandler->getWriter().get(), tmpFile.path().string().c_str(), 4096000);
151 TEST(StreamHandlerFactory, nonAsyncStderr) {
152 StreamHandlerFactory factory;
154 TemporaryFile tmpFile{"logging_test"};
155 auto options = LogHandlerFactory::Options{
156 make_pair("stream", "stderr"),
157 make_pair("async", "no"),
159 auto handler = factory.createHandler(options);
161 auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
162 ASSERT_TRUE(stdHandler);
165 std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
166 EXPECT_TRUE(formatter)
167 << "handler factory should have created a GlogStyleFormatter";
170 std::dynamic_pointer_cast<ImmediateFileWriter>(stdHandler->getWriter());
172 EXPECT_EQ(STDERR_FILENO, writer->getFile().fd());
175 TEST(FileHandlerFactory, errors) {
176 FileHandlerFactory factory;
177 TemporaryFile tmpFile{"logging_test"};
178 using Options = LogHandlerFactory::Options;
181 auto options = Options{};
183 factory.createHandler(options),
184 std::invalid_argument,
185 "no path specified for file handler");
189 auto options = Options{
190 {"path", tmpFile.path().string()},
191 {"stream", "stderr"},
194 factory.createHandler(options),
195 std::invalid_argument,
196 "unknown option \"stream\"");
200 auto options = Options{
201 {"stream", "nonstdout"},
204 factory.createHandler(options),
205 std::invalid_argument,
206 "unknown option \"stream\"");
210 auto options = Options{
211 {"path", tmpFile.path().string()},
215 factory.createHandler(options),
216 std::invalid_argument,
217 "^error processing option \"async\": Invalid value for bool: \"xyz\"$");
221 auto options = Options{
222 {"path", tmpFile.path().string()},
224 {"max_buffer_size", "1234"},
227 factory.createHandler(options),
228 std::invalid_argument,
229 "the \"max_buffer_size\" option is only valid for async file handlers");
233 auto options = Options{
234 {"path", tmpFile.path().string()},
235 {"max_buffer_size", "hello"},
238 factory.createHandler(options),
239 std::invalid_argument,
240 "^error processing option \"max_buffer_size\": "
241 "Non-digit character found: \"hello\"$");
245 auto options = Options{
246 {"path", tmpFile.path().string()},
247 {"max_buffer_size", "0"},
250 factory.createHandler(options),
251 std::invalid_argument,
252 "^error processing option \"max_buffer_size\": "
253 "must be a positive integer$");
257 auto options = Options{
258 {"path", tmpFile.path().string()},
262 factory.createHandler(options),
263 std::invalid_argument,
264 "^unknown option \"foo\"$");
268 TEST(StreamHandlerFactory, errors) {
269 StreamHandlerFactory factory;
270 using Options = LogHandlerFactory::Options;
273 auto options = Options{};
275 factory.createHandler(options),
276 std::invalid_argument,
277 "no stream name specified for stream handler");
281 auto options = Options{
282 {"path", "/tmp/log.txt"},
283 {"stream", "stderr"},
286 factory.createHandler(options),
287 std::invalid_argument,
288 "unknown option \"path\"");
292 auto options = Options{
293 {"stream", "nonstdout"},
296 factory.createHandler(options),
297 std::invalid_argument,
298 "unknown stream \"nonstdout\": expected one of stdout or stderr");
302 auto options = Options{
303 {"stream", "stderr"},
307 factory.createHandler(options),
308 std::invalid_argument,
309 "^error processing option \"async\": Invalid value for bool: \"xyz\"$");
313 auto options = Options{
314 {"stream", "stderr"},
316 {"max_buffer_size", "1234"},
319 factory.createHandler(options),
320 std::invalid_argument,
321 "^the \"max_buffer_size\" option is only valid for "
322 "async file handlers$");
326 auto options = Options{
327 {"stream", "stderr"},
328 {"max_buffer_size", "hello"},
331 factory.createHandler(options),
332 std::invalid_argument,
333 "^error processing option \"max_buffer_size\": "
334 "Non-digit character found: \"hello\"$");
338 auto options = Options{
339 {"stream", "stderr"},
340 {"max_buffer_size", "0"},
343 factory.createHandler(options),
344 std::invalid_argument,
345 "^error processing option \"max_buffer_size\": "
346 "must be a positive integer$");
350 auto options = Options{
351 make_pair("stream", "stderr"),
352 make_pair("foo", "bar"),
355 factory.createHandler(options),
356 std::invalid_argument,
357 "unknown option \"foo\"");