Commit 92dbb6a8 authored by Adam Simpkins's avatar Adam Simpkins Committed by Facebook Github Bot

logging: add a getBaseLoggingConfig() function

Summary:
Update folly::initLogging() to call getBaseLoggingConfig() to get a base
configuration string that is applied before the argument that it was called
with.  getBaseLoggingConfig() is defined as a weak symbol, and is intended to
be overridden on a per-executable basis if desired.

This allows individual programs to control their default logging settings, but
still let them be overridden via a command line flag.  The command line flag is
used to update this base configuration.  The command line flag can fully
override all of the base configuration settings if desired, but settings from
the base config that are not overridden will still be used.

For example, if the base configuration defines settings for the categories
"foo" and "bar", and the command line flag defines settings for the categories
"bar" and "wub", the base configuration settings for "foo" will still be used.

Reviewed By: yfeldblum

Differential Revision: D7164832

fbshipit-source-id: e172f746d7bd004948872adbbb87c597765e283c
parent f999d202
......@@ -23,12 +23,24 @@
namespace folly {
void initLogging(StringPiece configString) {
if (configString.empty()) {
auto* const baseConfigStr = getBaseLoggingConfig();
// Return early if we have nothing to do
if (!baseConfigStr && configString.empty()) {
return;
}
// Parse and apply the config string
auto config = parseLogConfig(configString);
// Parse the configuration string(s)
LogConfig config;
if (baseConfigStr) {
config = parseLogConfig(baseConfigStr);
if (!configString.empty()) {
config.update(parseLogConfig(configString));
}
} else {
config = parseLogConfig(configString);
}
// Apply the config settings
LoggerDB::get().updateConfig(config);
}
......
......@@ -39,4 +39,26 @@ namespace folly {
*/
void initLogging(folly::StringPiece configString = "");
/**
* folly::getBaseLoggingConfig() allows individual executables to easily
* customize their default logging configuration.
*
* You can define this function in your executable and folly::initLogging()
* will call it to get the base logging configuration. The settings returned
* by getBaseLoggingConfig() will then be modified by updating them with the
* configuration string parameter passed to initLogging().
*
* This allows the user-specified configuration passed to initLogging() to
* update the base configuration. The user-specified configuration can apply
* additional settings, and it may also override settings for categories and
* handlers defined in the base configuration.
*
* See folly/experimental/logging/example/main.cpp for an example that defines
* getBaseLoggingConfig().
*
* If this function returns a non-null pointer, it should point to a
* null-terminated string with static storage duration.
*/
const char* getBaseLoggingConfig();
} // namespace folly
/*
* 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/CPortability.h>
namespace folly {
// The default implementation for getBaseLoggingConfig().
// By default this returns null, and we will use only the default settings
// applied by initializeLoggerDB().
//
// This is defined in a separate module from initLogging() so that it can be
// placed into a separate library from the main folly logging code when linking
// as a shared library. This is required to help ensure that any
// getBaseLoggingConfig() provided by the main binary is preferred over this
// symbol.
FOLLY_ATTR_WEAK const char* getBaseLoggingConfig() {
return nullptr;
}
} // namespace folly
......@@ -316,6 +316,11 @@ class LoggerDB {
* generally a good idea to defer more complicated setup until after main()
* starts.
*
* In most situations it is normally better to override getBaseLoggingConfig()
* from logging/Init.h rather than overriding initializeLoggerDB(). You only
* need to override initializeLoggerDB() if you want to change the settings
* that are used for messages that get logged before initLogging() is called.
*
* The default implementation configures the root log category to write all
* warning and higher-level log messages to stderr, using a format similar to
* that used by GLOG.
......
......@@ -25,16 +25,26 @@ DEFINE_string(logging, "", "Logging category configuration string");
using namespace example;
using folly::LogLevel;
// Invoking code that uses XLOG() statements before main is safe,
// but will not log anywhere, since no handlers are configured yet.
// Invoking code that uses XLOG() statements before main() is safe.
// This will use default log settings defined by folly::initializeLoggerDB().
static ExampleObject staticInitialized("static");
namespace folly {
const char* getBaseLoggingConfig() {
// Configure folly to enable INFO+ messages, and everything else to
// enable WARNING+.
//
// Set the default log handler to log asynchronously by default.
return ".=WARNING,folly=INFO; default:async=true";
}
} // namespace folly
int main(int argc, char* argv[]) {
// Using log macros before configuring any log levels or log handlers is
// safe, but the messages will always be ignore since no handlers are defined.
XLOG(INFO, "no handlers configured yet, so this will go nowhere");
printf("main starting\n");
fflush(stdout);
// Using log macros before calling folly::initLogging() will use the default
// log settings defined by folly::initializeLoggerDB(). The default behavior
// is to log WARNING+ messages to stderr.
XLOG(INFO) << "log messages less than WARNING will be ignored";
XLOG(ERR) << "error messages before initLogging() will be logged to stderr";
// Call folly::init() and then initialize log levels and handlers
folly::init(&argc, &argv);
......@@ -42,15 +52,16 @@ int main(int argc, char* argv[]) {
// All XLOG() statements in this file will log to the category
// folly.experimental.logging.example.main
XLOG(INFO, "now log messages will be sent to stderr");
XLOG(INFO, "now the normal log settings have been applied");
XLOG(DBG1, "log arguments are concatenated: ", 12345, ", ", 92.0);
XLOGF(DBG1, "XLOGF supports {}-style formatting: {:.3f}", "python", 1.0 / 3);
XLOG(DBG2) << "streaming syntax is also supported: " << 1234;
XLOG(DBG2, "you can even", " mix function-style") << " and streaming "
<< "syntax";
XLOG(DBG2, "if you really want, ", "you can even")
<< " mix function-style and streaming syntax: " << 42;
XLOGF(DBG3, "and {} can mix {} style", "you", "format") << " and streaming";
ExampleObject("foo");
XLOG(INFO, "main returning");
XLOG(INFO) << "main returning";
return 0;
}
/*
* 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/Init.h>
#include <folly/experimental/logging/LogConfigParser.h>
#include <folly/experimental/logging/LoggerDB.h>
#include <folly/experimental/logging/test/ConfigHelpers.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
using folly::LoggerDB;
using folly::parseLogConfig;
namespace {
// A counter to help confirm that our getBaseLoggingConfigCalled() was invoked
// rather than the default implementation that folly exports as a weak symbol.
unsigned int getBaseLoggingConfigCalled;
} // namespace
namespace folly {
const char* getBaseLoggingConfig() {
++getBaseLoggingConfigCalled;
return "folly=INFO; default:stream=stdout";
}
} // namespace folly
TEST(Init, checkConfig) {
// Before we call initLogging(), the LoggerDB will have the default
// configuration provided by initializeLoggerDB().
auto initialConfig = folly::LoggerDB::get().getConfig();
EXPECT_EQ(0, getBaseLoggingConfigCalled);
EXPECT_EQ(
parseLogConfig(".:=WARN:default; "
"default=stream:stream=stderr,async=false"),
LoggerDB::get().getConfig());
// Call initLogging()
// Make sure it merges the supplied config argument with our custom
// base configuration.
folly::initLogging(".=ERROR,folly.logging=DBG7");
EXPECT_EQ(1, getBaseLoggingConfigCalled);
EXPECT_EQ(
parseLogConfig(".:=ERROR:default,folly=INFO:,folly.logging=DBG7:; "
"default=stream:stream=stdout,async=false"),
LoggerDB::get().getConfig());
}
// We use our custom main() to ensure that folly::initLogging() has
// not been called yet when we start running the tests.
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, /* remove_flags = */ true);
return RUN_ALL_TESTS();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment