void Logger::LoadConfig()

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);
    }
}