Fix copyright lines
[folly.git] / folly / experimental / logging / test / FileHandlerFactoryTest.cpp
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include <folly/experimental/logging/FileHandlerFactory.h>
17 #include <folly/experimental/logging/StreamHandlerFactory.h>
18
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>
27
28 using namespace folly;
29 using folly::test::TemporaryFile;
30 using std::make_pair;
31
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());
40
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;
45   checkUnixError(
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);
49 }
50
51 void checkAsyncWriter(
52     const LogWriter* writer,
53     int expectedFD,
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());
60 }
61
62 TEST(FileHandlerFactory, pathOnly) {
63   FileHandlerFactory factory;
64
65   TemporaryFile tmpFile{"logging_test"};
66   auto options = LogHandlerFactory::Options{
67       make_pair("path", tmpFile.path().string()),
68   };
69   auto handler = factory.createHandler(options);
70
71   auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
72   ASSERT_TRUE(stdHandler);
73
74   auto formatter =
75       std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
76   EXPECT_TRUE(formatter)
77       << "handler factory should have created a GlogStyleFormatter";
78
79   checkAsyncWriter(
80       stdHandler->getWriter().get(),
81       tmpFile.path().string().c_str(),
82       AsyncFileWriter::kDefaultMaxBufferSize);
83 }
84
85 TEST(StreamHandlerFactory, stderrStream) {
86   StreamHandlerFactory factory;
87
88   TemporaryFile tmpFile{"logging_test"};
89   auto options = StreamHandlerFactory::Options{
90       make_pair("stream", "stderr"),
91   };
92   auto handler = factory.createHandler(options);
93
94   auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
95   ASSERT_TRUE(stdHandler);
96
97   auto formatter =
98       std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
99   EXPECT_TRUE(formatter)
100       << "handler factory should have created a GlogStyleFormatter";
101
102   checkAsyncWriter(
103       stdHandler->getWriter().get(),
104       STDERR_FILENO,
105       AsyncFileWriter::kDefaultMaxBufferSize);
106 }
107
108 TEST(StreamHandlerFactory, stdoutWithMaxBuffer) {
109   StreamHandlerFactory factory;
110
111   TemporaryFile tmpFile{"logging_test"};
112   auto options = StreamHandlerFactory::Options{
113       make_pair("stream", "stdout"),
114       make_pair("max_buffer_size", "4096"),
115   };
116   auto handler = factory.createHandler(options);
117
118   auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
119   ASSERT_TRUE(stdHandler);
120
121   auto formatter =
122       std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
123   EXPECT_TRUE(formatter)
124       << "handler factory should have created a GlogStyleFormatter";
125
126   checkAsyncWriter(stdHandler->getWriter().get(), STDOUT_FILENO, 4096);
127 }
128
129 TEST(FileHandlerFactory, pathWithMaxBufferSize) {
130   FileHandlerFactory factory;
131
132   TemporaryFile tmpFile{"logging_test"};
133   auto options = LogHandlerFactory::Options{
134       make_pair("path", tmpFile.path().string()),
135       make_pair("max_buffer_size", "4096000"),
136   };
137   auto handler = factory.createHandler(options);
138
139   auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
140   ASSERT_TRUE(stdHandler);
141
142   auto formatter =
143       std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
144   EXPECT_TRUE(formatter)
145       << "handler factory should have created a GlogStyleFormatter";
146
147   checkAsyncWriter(
148       stdHandler->getWriter().get(), tmpFile.path().string().c_str(), 4096000);
149 }
150
151 TEST(StreamHandlerFactory, nonAsyncStderr) {
152   StreamHandlerFactory factory;
153
154   TemporaryFile tmpFile{"logging_test"};
155   auto options = LogHandlerFactory::Options{
156       make_pair("stream", "stderr"),
157       make_pair("async", "no"),
158   };
159   auto handler = factory.createHandler(options);
160
161   auto stdHandler = std::dynamic_pointer_cast<StandardLogHandler>(handler);
162   ASSERT_TRUE(stdHandler);
163
164   auto formatter =
165       std::dynamic_pointer_cast<GlogStyleFormatter>(stdHandler->getFormatter());
166   EXPECT_TRUE(formatter)
167       << "handler factory should have created a GlogStyleFormatter";
168
169   auto writer =
170       std::dynamic_pointer_cast<ImmediateFileWriter>(stdHandler->getWriter());
171   ASSERT_TRUE(writer);
172   EXPECT_EQ(STDERR_FILENO, writer->getFile().fd());
173 }
174
175 TEST(FileHandlerFactory, errors) {
176   FileHandlerFactory factory;
177   TemporaryFile tmpFile{"logging_test"};
178   using Options = LogHandlerFactory::Options;
179
180   {
181     auto options = Options{};
182     EXPECT_THROW_RE(
183         factory.createHandler(options),
184         std::invalid_argument,
185         "no path specified for file handler");
186   }
187
188   {
189     auto options = Options{
190         {"path", tmpFile.path().string()},
191         {"stream", "stderr"},
192     };
193     EXPECT_THROW_RE(
194         factory.createHandler(options),
195         std::invalid_argument,
196         "unknown option \"stream\"");
197   }
198
199   {
200     auto options = Options{
201         {"stream", "nonstdout"},
202     };
203     EXPECT_THROW_RE(
204         factory.createHandler(options),
205         std::invalid_argument,
206         "unknown option \"stream\"");
207   }
208
209   {
210     auto options = Options{
211         {"path", tmpFile.path().string()},
212         {"async", "xyz"},
213     };
214     EXPECT_THROW_RE(
215         factory.createHandler(options),
216         std::invalid_argument,
217         "^error processing option \"async\": Invalid value for bool: \"xyz\"$");
218   }
219
220   {
221     auto options = Options{
222         {"path", tmpFile.path().string()},
223         {"async", "false"},
224         {"max_buffer_size", "1234"},
225     };
226     EXPECT_THROW_RE(
227         factory.createHandler(options),
228         std::invalid_argument,
229         "the \"max_buffer_size\" option is only valid for async file handlers");
230   }
231
232   {
233     auto options = Options{
234         {"path", tmpFile.path().string()},
235         {"max_buffer_size", "hello"},
236     };
237     EXPECT_THROW_RE(
238         factory.createHandler(options),
239         std::invalid_argument,
240         "^error processing option \"max_buffer_size\": "
241         "Non-digit character found: \"hello\"$");
242   }
243
244   {
245     auto options = Options{
246         {"path", tmpFile.path().string()},
247         {"max_buffer_size", "0"},
248     };
249     EXPECT_THROW_RE(
250         factory.createHandler(options),
251         std::invalid_argument,
252         "^error processing option \"max_buffer_size\": "
253         "must be a positive integer$");
254   }
255
256   {
257     auto options = Options{
258         {"path", tmpFile.path().string()},
259         {"foo", "bar"},
260     };
261     EXPECT_THROW_RE(
262         factory.createHandler(options),
263         std::invalid_argument,
264         "^unknown option \"foo\"$");
265   }
266 }
267
268 TEST(StreamHandlerFactory, errors) {
269   StreamHandlerFactory factory;
270   using Options = LogHandlerFactory::Options;
271
272   {
273     auto options = Options{};
274     EXPECT_THROW_RE(
275         factory.createHandler(options),
276         std::invalid_argument,
277         "no stream name specified for stream handler");
278   }
279
280   {
281     auto options = Options{
282         {"path", "/tmp/log.txt"},
283         {"stream", "stderr"},
284     };
285     EXPECT_THROW_RE(
286         factory.createHandler(options),
287         std::invalid_argument,
288         "unknown option \"path\"");
289   }
290
291   {
292     auto options = Options{
293         {"stream", "nonstdout"},
294     };
295     EXPECT_THROW_RE(
296         factory.createHandler(options),
297         std::invalid_argument,
298         "unknown stream \"nonstdout\": expected one of stdout or stderr");
299   }
300
301   {
302     auto options = Options{
303         {"stream", "stderr"},
304         {"async", "xyz"},
305     };
306     EXPECT_THROW_RE(
307         factory.createHandler(options),
308         std::invalid_argument,
309         "^error processing option \"async\": Invalid value for bool: \"xyz\"$");
310   }
311
312   {
313     auto options = Options{
314         {"stream", "stderr"},
315         {"async", "false"},
316         {"max_buffer_size", "1234"},
317     };
318     EXPECT_THROW_RE(
319         factory.createHandler(options),
320         std::invalid_argument,
321         "^the \"max_buffer_size\" option is only valid for "
322         "async file handlers$");
323   }
324
325   {
326     auto options = Options{
327         {"stream", "stderr"},
328         {"max_buffer_size", "hello"},
329     };
330     EXPECT_THROW_RE(
331         factory.createHandler(options),
332         std::invalid_argument,
333         "^error processing option \"max_buffer_size\": "
334         "Non-digit character found: \"hello\"$");
335   }
336
337   {
338     auto options = Options{
339         {"stream", "stderr"},
340         {"max_buffer_size", "0"},
341     };
342     EXPECT_THROW_RE(
343         factory.createHandler(options),
344         std::invalid_argument,
345         "^error processing option \"max_buffer_size\": "
346         "must be a positive integer$");
347   }
348
349   {
350     auto options = Options{
351         make_pair("stream", "stderr"),
352         make_pair("foo", "bar"),
353     };
354     EXPECT_THROW_RE(
355         factory.createHandler(options),
356         std::invalid_argument,
357         "unknown option \"foo\"");
358   }
359 }