in core/logger/Logger.cpp [190:355]
void Logger::LoadConfig(const std::string& filePath) {
std::map<std::string, LoggerConfig> loggerConfigs;
std::map<std::string, SinkConfig> sinkConfigs;
std::string logConfigInfo;
// Load config file, check if it is valid or not.
do {
std::ifstream in(filePath);
if (!in)
break;
in.seekg(0, std::ios::end);
size_t len = in.tellg();
in.seekg(0, std::ios::beg);
std::vector<char> buffer(len + 1, '\0');
in.read(buffer.data(), len);
in.close();
Json::Value jsonRoot;
Json::CharReaderBuilder builder;
builder["collectComments"] = false;
std::unique_ptr<Json::CharReader> jsonReader(builder.newCharReader());
std::string jsonParseErrs;
if (!jsonReader->parse(buffer.data(), buffer.data() + buffer.size(), &jsonRoot, &jsonParseErrs)) {
break;
}
// Parse Sinks at first, so that we can find them when parsing Loggers.
if (!jsonRoot.isMember("Sinks") || !jsonRoot["Sinks"].isObject() || jsonRoot["Sinks"].empty()) {
break;
}
auto& jsonSinks = jsonRoot["Sinks"];
auto sinkNames = jsonSinks.getMemberNames();
for (auto& name : sinkNames) {
auto& jsonSink = jsonSinks[name];
if (!jsonSink.isObject()
|| !(jsonSink.isMember("Type") && jsonSink["Type"].isString() && !jsonSink["Type"].asString().empty())
|| !(jsonSink.isMember("MaxLogFileNum") && jsonSink["MaxLogFileNum"].isIntegral()
&& jsonSink["MaxLogFileNum"].asInt() > 0)
|| !(jsonSink.isMember("MaxLogFileSize") && jsonSink["MaxLogFileSize"].isIntegral()
&& jsonSink["MaxLogFileSize"].asInt64() > 0)
|| !(jsonSink.isMember("MaxDaysFromModify") && jsonSink["MaxDaysFromModify"].isIntegral()
&& jsonSink["MaxDaysFromModify"].asInt() > 0)
|| !(jsonSink.isMember("LogFilePath") && jsonSink["LogFilePath"].isString()
&& !jsonSink["LogFilePath"].asString().empty())) {
continue;
}
SinkConfig sinkCfg;
sinkCfg.type = jsonSink["Type"].asString();
sinkCfg.maxLogFileNum = jsonSink["MaxLogFileNum"].asInt();
sinkCfg.maxLogFileSize = jsonSink["MaxLogFileSize"].asInt64();
sinkCfg.maxDaysFromModify = jsonSink["MaxDaysFromModify"].asInt();
sinkCfg.logFilePath = jsonSink["LogFilePath"].asString();
if (jsonSink.isMember("Compress") && jsonSink["Compress"].isString()) {
sinkCfg.compress = jsonSink["Compress"].asString();
}
sinkConfigs[name] = std::move(sinkCfg);
}
if (sinkConfigs.empty())
break;
if (!jsonRoot.isMember("Loggers") || !jsonRoot["Loggers"].isObject() || jsonRoot["Loggers"].empty()) {
break;
}
auto& jsonLoggers = jsonRoot["Loggers"];
auto loggerNames = jsonLoggers.getMemberNames();
for (auto& name : loggerNames) {
auto& jsonLogger = jsonLoggers[name];
if (!jsonLogger.isObject() || jsonLogger.empty())
continue;
auto sinkName = jsonLogger.getMemberNames()[0];
if (sinkName.empty() || !jsonLogger[sinkName].isString())
continue;
if (sinkConfigs.find(sinkName) == sinkConfigs.end())
continue;
LoggerConfig logCfg;
if (!MapStringToLevel(jsonLogger[sinkName].asString(), logCfg.level))
continue;
logCfg.sinkName = std::move(sinkName);
loggerConfigs[name] = std::move(logCfg);
}
logConfigInfo = "Load log config from " + filePath;
} while (0);
// parse env log level
level::level_enum envLogLevel = level::info;
std::string aliyun_logtail_log_level;
const char* envLogLevelVal = std::getenv("LOGTAIL_LOG_LEVEL");
if (envLogLevelVal) {
aliyun_logtail_log_level = envLogLevelVal;
}
if (MapStringToLevel(ToUpperCaseString(aliyun_logtail_log_level), envLogLevel)) {
LogMsg(std::string("Load log level from the env success, level: ") + aliyun_logtail_log_level);
} else {
LogMsg(std::string("Load log level from the env error, level: ") + aliyun_logtail_log_level);
aliyun_logtail_log_level = "";
}
// Add or supply default config(s).
bool needSave = true;
if (loggerConfigs.empty()) {
logConfigInfo = "Load log config file failed, use default configs.";
LoadAllDefaultConfigs(loggerConfigs, sinkConfigs);
} else if (loggerConfigs.end() == loggerConfigs.find(DEFAULT_LOGGER_NAME)) {
logConfigInfo += ", and add default config.";
LoadDefaultConfig(loggerConfigs, sinkConfigs);
} else
needSave = false;
EnsureSnapshotDirExist(sinkConfigs);
LogMsg(logConfigInfo);
LogMsg(std::string("Logger size in config: ") + std::to_string(loggerConfigs.size()));
// Create loggers, record failed loggers.
std::set<std::string> failedLoggers;
for (auto& loggerIter : loggerConfigs) {
auto& name = loggerIter.first;
auto& loggerCfg = loggerIter.second;
auto& sinkCfg = sinkConfigs[loggerCfg.sinkName];
auto logger = CreateLogger(name,
sinkCfg.logFilePath,
sinkCfg.maxLogFileNum,
sinkCfg.maxLogFileSize,
sinkCfg.maxDaysFromModify,
sinkCfg.compress);
if (nullptr == logger) {
failedLoggers.insert(name);
LogMsg(std::string("[ERROR] logger named ") + name + " created failed.");
continue;
}
spdlog::register_logger(logger);
logger->set_pattern(DEFAULT_PATTERN);
if (name == GetAgentLoggersPrefix() && !aliyun_logtail_log_level.empty()) {
logger->set_level(envLogLevel);
logger->flush_on(envLogLevel);
} else {
logger->set_level(loggerCfg.level);
logger->flush_on(loggerCfg.level);
}
LogMsg(std::string("logger named ") + name + " created.");
}
if (failedLoggers.empty()) {
if (needSave)
SaveConfig(filePath, loggerConfigs, sinkConfigs);
return;
}
// If the default logger failed, downgrade to console logger.
// It is useless, but we need to offer at least one available logger.
if (failedLoggers.find(DEFAULT_LOGGER_NAME) != failedLoggers.end()) {
try {
spdlog::stdout_logger_mt(DEFAULT_LOGGER_NAME);
} catch (...) {
LogMsg("Create console logger for default logger failed, we have no idea now.");
throw ExceptionBase("Initialize logger failed......");
}
failedLoggers.erase(DEFAULT_LOGGER_NAME);
}
}