experimental/logging/printf.h \
experimental/logging/RateLimiter.h \
experimental/logging/StandardLogHandler.h \
+ experimental/logging/StandardLogHandlerFactory.h \
experimental/logging/xlog.h \
experimental/NestedCommandLineApp.h \
experimental/observer/detail/Core.h \
*/
#include <folly/experimental/logging/FileHandlerFactory.h>
-#include <set>
-
#include <folly/Conv.h>
-#include <folly/MapUtil.h>
#include <folly/experimental/logging/AsyncFileWriter.h>
-#include <folly/experimental/logging/GlogStyleFormatter.h>
#include <folly/experimental/logging/ImmediateFileWriter.h>
#include <folly/experimental/logging/StandardLogHandler.h>
+#include <folly/experimental/logging/StandardLogHandlerFactory.h>
using std::make_shared;
using std::shared_ptr;
namespace folly {
-std::shared_ptr<LogHandler> FileHandlerFactory::createHandler(
- const Options& options) {
- // Raise an error if we receive unexpected options
- auto knownOptions =
- std::set<std::string>{"path", "stream", "async", "max_buffer_size"};
- for (const auto& entry : options) {
- if (knownOptions.find(entry.first) == knownOptions.end()) {
- throw std::invalid_argument(to<string>(
- "unknown parameter \"", entry.first, "\" for FileHandlerFactory"));
+class FileHandlerFactory::WriterFactory
+ : public StandardLogHandlerFactory::WriterFactory {
+ public:
+ bool processOption(StringPiece name, StringPiece value) override {
+ if (name == "path") {
+ if (!stream_.empty()) {
+ throw std::invalid_argument(
+ "cannot specify both \"path\" and \"stream\" "
+ "parameters for FileHandlerFactory");
+ }
+ path_ = value.str();
+ return true;
+ } else if (name == "stream") {
+ if (!path_.empty()) {
+ throw std::invalid_argument(
+ "cannot specify both \"path\" and \"stream\" "
+ "parameters for FileHandlerFactory");
+ }
+ stream_ = value.str();
+ return true;
+ } else if (name == "async") {
+ async_ = to<bool>(value);
+ return true;
+ } else if (name == "max_buffer_size") {
+ auto size = to<size_t>(value);
+ if (size == 0) {
+ throw std::invalid_argument(to<string>("must be a postive number"));
+ }
+ maxBufferSize_ = size;
+ return true;
+ } else {
+ return false;
}
}
- // Construct the formatter
- // TODO: We should eventually support parameters to control the formatter
- // behavior.
- auto formatter = make_shared<GlogStyleFormatter>();
-
- // Get the output file to use
- File outputFile;
- auto* path = get_ptr(options, "path");
- auto* stream = get_ptr(options, "stream");
- if (path && stream) {
- throw std::invalid_argument(
- "cannot specify both \"path\" and \"stream\" "
- "parameters for FileHandlerFactory");
- } else if (path) {
- outputFile = File{*path, O_WRONLY | O_APPEND | O_CREAT};
- } else if (stream) {
- if (*stream == "stderr") {
+ std::shared_ptr<LogWriter> createWriter() override {
+ // Get the output file to use
+ File outputFile;
+ if (!path_.empty()) {
+ outputFile = File{path_, O_WRONLY | O_APPEND | O_CREAT};
+ } else if (stream_ == "stderr") {
outputFile = File{STDERR_FILENO, /* ownsFd */ false};
- } else if (*stream == "stdout") {
+ } else if (stream_ == "stdout") {
outputFile = File{STDOUT_FILENO, /* ownsFd */ false};
} else {
throw std::invalid_argument(to<string>(
"unknown stream for FileHandlerFactory: \"",
- *stream,
+ stream_,
"\" expected one of stdout or stderr"));
}
- } else {
- throw std::invalid_argument(
- "must specify a \"path\" or \"stream\" "
- "parameter for FileHandlerFactory");
- }
- // Determine whether we should use ImmediateFileWriter or AsyncFileWriter
- shared_ptr<LogWriter> writer;
- bool async = true;
- auto* asyncOption = get_ptr(options, "async");
- if (asyncOption) {
- try {
- async = to<bool>(*asyncOption);
- } catch (const std::exception& ex) {
- throw std::invalid_argument(to<string>(
- "expected a boolean value for FileHandlerFactory \"async\" "
- "parameter: ",
- *asyncOption));
- }
- }
- auto* maxBufferOption = get_ptr(options, "max_buffer_size");
- if (async) {
- auto asyncWriter = make_shared<AsyncFileWriter>(std::move(outputFile));
- if (maxBufferOption) {
- size_t maxBufferSize;
- try {
- maxBufferSize = to<size_t>(*maxBufferOption);
- } catch (const std::exception& ex) {
- throw std::invalid_argument(to<string>(
- "expected an integer value for FileHandlerFactory "
- "\"max_buffer_size\": ",
- *maxBufferOption));
+ // Determine whether we should use ImmediateFileWriter or AsyncFileWriter
+ if (async_) {
+ auto asyncWriter = make_shared<AsyncFileWriter>(std::move(outputFile));
+ if (maxBufferSize_.hasValue()) {
+ asyncWriter->setMaxBufferSize(maxBufferSize_.value());
}
- if (maxBufferSize == 0) {
+ return asyncWriter;
+ } else {
+ if (maxBufferSize_.hasValue()) {
throw std::invalid_argument(to<string>(
- "expected a positive value for FileHandlerFactory "
- "\"max_buffer_size\": ",
- *maxBufferOption));
+ "the \"max_buffer_size\" option is only valid for async file "
+ "handlers"));
}
- asyncWriter->setMaxBufferSize(maxBufferSize);
+ return make_shared<ImmediateFileWriter>(std::move(outputFile));
}
- writer = std::move(asyncWriter);
- } else {
- if (maxBufferOption) {
- throw std::invalid_argument(to<string>(
- "the \"max_buffer_size\" option is only valid for async file "
- "handlers"));
- }
- writer = make_shared<ImmediateFileWriter>(std::move(outputFile));
}
- return make_shared<StandardLogHandler>(
- LogHandlerConfig{getType(), options}, formatter, writer);
+ std::string path_;
+ std::string stream_;
+ bool async_{true};
+ Optional<size_t> maxBufferSize_;
+};
+
+std::shared_ptr<LogHandler> FileHandlerFactory::createHandler(
+ const Options& options) {
+ WriterFactory writerFactory;
+ return StandardLogHandlerFactory::createHandler(
+ getType(), &writerFactory, options);
}
} // namespace folly
}
std::shared_ptr<LogHandler> createHandler(const Options& options) override;
+
+ private:
+ class WriterFactory;
};
} // namespace folly
// Create the new log handler
const auto& factory = factoryIter->second;
std::shared_ptr<LogHandler> handler;
- if (oldHandler) {
- handler = factory->updateHandler(oldHandler, entry.second.options);
- if (handler != oldHandler) {
- oldToNewHandlerMap->emplace(oldHandler, handler);
+ try {
+ if (oldHandler) {
+ handler = factory->updateHandler(oldHandler, entry.second.options);
+ if (handler != oldHandler) {
+ oldToNewHandlerMap->emplace(oldHandler, handler);
+ }
+ } else {
+ handler = factory->createHandler(entry.second.options);
}
- } else {
- handler = factory->createHandler(entry.second.options);
+ } catch (const std::exception& ex) {
+ // Errors creating or updating the the log handler are generally due to
+ // bad configuration options. It is useful to update the exception
+ // message to include the name of the log handler we were trying to
+ // update or create.
+ throw std::invalid_argument(to<string>(
+ "error ",
+ oldHandler ? "updating" : "creating",
+ " log handler \"",
+ entry.first,
+ "\": ",
+ exceptionStr(ex)));
}
handlerInfo->handlers[entry.first] = handler;
(*handlers)[entry.first] = handler;
printf.cpp \
RateLimiter.cpp \
StandardLogHandler.cpp \
+ StandardLogHandlerFactory.cpp \
xlog.cpp
libfollylogging_la_LIBADD = $(top_builddir)/libfolly.la
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed 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.
+ */
+#include <folly/experimental/logging/StandardLogHandlerFactory.h>
+
+#include <folly/MapUtil.h>
+#include <folly/String.h>
+#include <folly/experimental/logging/GlogStyleFormatter.h>
+#include <folly/experimental/logging/StandardLogHandler.h>
+
+using std::string;
+
+namespace folly {
+
+class GlogFormatterFactory
+ : public StandardLogHandlerFactory::FormatterFactory {
+ public:
+ bool processOption(StringPiece /* name */, StringPiece /* value */) override {
+ return false;
+ }
+ std::shared_ptr<LogFormatter> createFormatter() override {
+ return std::make_shared<GlogStyleFormatter>();
+ }
+};
+
+std::shared_ptr<StandardLogHandler> StandardLogHandlerFactory::createHandler(
+ StringPiece type,
+ WriterFactory* writerFactory,
+ const Options& options) {
+ std::unique_ptr<FormatterFactory> formatterFactory;
+
+ // Get the log formatter type
+ auto* formatterType = get_ptr(options, "formatter");
+ if (!formatterType || *formatterType == "glog") {
+ formatterFactory = std::make_unique<GlogFormatterFactory>();
+ } else {
+ throw std::invalid_argument(
+ to<string>("unknown log formatter type \"", *formatterType, "\""));
+ }
+
+ // Process the log formatter and log handler options
+ std::vector<string> errors;
+ for (const auto& entry : options) {
+ bool handled = false;
+ try {
+ // Allow both the formatterFactory and writerFactory to consume an
+ // option. In general they probably should have mutually exclusive
+ // option names, but we don't give precedence to one over the other here.
+ handled |= formatterFactory->processOption(entry.first, entry.second);
+ handled |= writerFactory->processOption(entry.first, entry.second);
+ } catch (const std::exception& ex) {
+ errors.push_back(to<string>(
+ "error processing option \"", entry.first, "\": ", ex.what()));
+ continue;
+ }
+
+ // We explicitly processed the "formatter" option above.
+ handled |= handled || (entry.first == "formatter");
+
+ // Complain about unknown options.
+ if (!handled) {
+ errors.push_back(to<string>("unknown option \"", entry.first, "\""));
+ }
+ }
+
+ if (!errors.empty()) {
+ throw std::invalid_argument(join(", ", errors));
+ }
+
+ // Construct the formatter and writer
+ auto formatter = formatterFactory->createFormatter();
+ auto writer = writerFactory->createWriter();
+
+ return std::make_shared<StandardLogHandler>(
+ LogHandlerConfig{type, options}, formatter, writer);
+}
+
+} // namespace folly
--- /dev/null
+/*
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed 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.
+ */
+#pragma once
+
+#include <string>
+#include <unordered_map>
+
+#include <folly/Range.h>
+
+namespace folly {
+
+class LogWriter;
+class LogFormatter;
+class StandardLogHandler;
+
+/**
+ * StandardLogHandlerFactory contains helper methods for LogHandlerFactory
+ * implementations that create StandardLogHandler objects.
+ *
+ * StandardLogHandlerFactory does not derive from LogHandlerFactory itself.
+ */
+class StandardLogHandlerFactory {
+ public:
+ using Options = std::unordered_map<std::string, std::string>;
+
+ class OptionProcessor {
+ public:
+ virtual ~OptionProcessor() {}
+
+ /**
+ * Process an option.
+ *
+ * This should return true if the option was processed successfully,
+ * or false if this is an unknown option name. It should throw an
+ * exception if the option name is known but there is a problem with the
+ * value.
+ */
+ virtual bool processOption(StringPiece name, StringPiece value) = 0;
+ };
+
+ class FormatterFactory : public OptionProcessor {
+ public:
+ virtual std::shared_ptr<LogFormatter> createFormatter() = 0;
+ };
+
+ class WriterFactory : public OptionProcessor {
+ public:
+ virtual std::shared_ptr<LogWriter> createWriter() = 0;
+ };
+
+ static std::shared_ptr<StandardLogHandler> createHandler(
+ StringPiece type,
+ WriterFactory* writerFactory,
+ const Options& options);
+};
+
+} // namespace folly