core/config/config.go (197 lines of code) (raw):
// Copyright 1999-2020 Alibaba Group Holding Ltd.
//
// 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.
package config
import (
"io/ioutil"
"os"
"path/filepath"
"strconv"
"sync"
"github.com/alibaba/sentinel-golang/logging"
"github.com/alibaba/sentinel-golang/util"
"github.com/pkg/errors"
"gopkg.in/yaml.v2"
)
var (
globalCfg = NewDefaultConfig()
initLogOnce sync.Once
)
func ResetGlobalConfig(config *Entity) {
globalCfg = config
}
// InitConfigWithYaml loads general configuration from the YAML file under provided path.
func InitConfigWithYaml(filePath string) (err error) {
// Initialize general config and logging module.
if err = applyYamlConfigFile(filePath); err != nil {
return err
}
return OverrideConfigFromEnvAndInitLog()
}
// applyYamlConfigFile loads general configuration from the given YAML file.
func applyYamlConfigFile(configPath string) error {
// Priority: system environment > YAML file > default config
if util.IsBlank(configPath) {
// If the config file path is absent, Sentinel will try to resolve it from the system env.
configPath = os.Getenv(ConfFilePathEnvKey)
}
if util.IsBlank(configPath) {
configPath = DefaultConfigFilename
}
// First Sentinel will try to load config from the given file.
// If the path is empty (not set), Sentinel will use the default config.
return loadGlobalConfigFromYamlFile(configPath)
}
func OverrideConfigFromEnvAndInitLog() error {
// Then Sentinel will try to get fundamental config items from system environment.
// If present, the value in system env will override the value in config file.
err := overrideItemsFromSystemEnv()
if err != nil {
return err
}
defer logging.Info("[Config] Print effective global config", "globalConfig", *globalCfg)
// Configured Logger is the highest priority
if configLogger := Logger(); configLogger != nil {
err = logging.ResetGlobalLogger(configLogger)
if err != nil {
return err
}
return nil
}
logDir := LogBaseDir()
if len(logDir) == 0 {
logDir = GetDefaultLogDir()
}
if err := initializeLogConfig(logDir, LogUsePid()); err != nil {
return err
}
logging.Info("[Config] App name resolved", "appName", AppName())
return nil
}
func loadGlobalConfigFromYamlFile(filePath string) error {
if filePath == DefaultConfigFilename {
if _, err := os.Stat(DefaultConfigFilename); err != nil {
//use default globalCfg.
return nil
}
}
_, err := os.Stat(filePath)
if err != nil && !os.IsExist(err) {
return err
}
content, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
err = yaml.Unmarshal(content, globalCfg)
if err != nil {
return err
}
logging.Info("[Config] Resolving Sentinel config from file", "file", filePath)
return checkConfValid(&(globalCfg.Sentinel))
}
func overrideItemsFromSystemEnv() error {
if appName := os.Getenv(AppNameEnvKey); !util.IsBlank(appName) {
globalCfg.Sentinel.App.Name = appName
}
if appTypeStr := os.Getenv(AppTypeEnvKey); !util.IsBlank(appTypeStr) {
appType, err := strconv.ParseInt(appTypeStr, 10, 32)
if err != nil {
return err
} else {
globalCfg.Sentinel.App.Type = int32(appType)
}
}
if addPidStr := os.Getenv(LogNamePidEnvKey); !util.IsBlank(addPidStr) {
addPid, err := strconv.ParseBool(addPidStr)
if err != nil {
return err
} else {
globalCfg.Sentinel.Log.UsePid = addPid
}
}
if logDir := os.Getenv(LogDirEnvKey); !util.IsBlank(logDir) {
globalCfg.Sentinel.Log.Dir = logDir
}
return checkConfValid(&(globalCfg.Sentinel))
}
func initializeLogConfig(logDir string, usePid bool) (err error) {
if logDir == "" {
return errors.New("invalid empty log path")
}
initLogOnce.Do(func() {
if err = util.CreateDirIfNotExists(logDir); err != nil {
return
}
err = reconfigureRecordLogger(logDir, usePid)
})
return err
}
func reconfigureRecordLogger(logBaseDir string, withPid bool) error {
filePath := filepath.Join(logBaseDir, logging.RecordLogFileName)
if withPid {
filePath = filePath + ".pid" + strconv.Itoa(os.Getpid())
}
fileLogger, err := logging.NewSimpleFileLogger(filePath)
if err != nil {
return err
}
// Note: not thread-safe!
if err := logging.ResetGlobalLogger(fileLogger); err != nil {
return err
}
logging.Info("[Config] Log base directory", "baseDir", logBaseDir)
return nil
}
func GetDefaultLogDir() string {
home, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(home, logging.DefaultDirName)
}
func AppName() string {
return globalCfg.AppName()
}
func AppType() int32 {
return globalCfg.AppType()
}
func Logger() logging.Logger {
return globalCfg.Logger()
}
func LogBaseDir() string {
return globalCfg.LogBaseDir()
}
// LogUsePid returns whether the log file name contains the PID suffix.
func LogUsePid() bool {
return globalCfg.LogUsePid()
}
func MetricExportHTTPAddr() string {
return globalCfg.MetricExportHTTPAddr()
}
func MetricExportHTTPPath() string {
return globalCfg.MetricExportHTTPPath()
}
func MetricLogFlushIntervalSec() uint32 {
return globalCfg.MetricLogFlushIntervalSec()
}
func MetricLogSingleFileMaxSize() uint64 {
return globalCfg.MetricLogSingleFileMaxSize()
}
func MetricLogMaxFileAmount() uint32 {
return globalCfg.MetricLogMaxFileAmount()
}
func SystemStatCollectIntervalMs() uint32 {
return globalCfg.SystemStatCollectIntervalMs()
}
func LoadStatCollectIntervalMs() uint32 {
return globalCfg.LoadStatCollectIntervalMs()
}
func CpuStatCollectIntervalMs() uint32 {
return globalCfg.CpuStatCollectIntervalMs()
}
func MemoryStatCollectIntervalMs() uint32 {
return globalCfg.MemoryStatCollectIntervalMs()
}
func UseCacheTime() bool {
return globalCfg.UseCacheTime()
}
func GlobalStatisticIntervalMsTotal() uint32 {
return globalCfg.GlobalStatisticIntervalMsTotal()
}
func GlobalStatisticSampleCountTotal() uint32 {
return globalCfg.GlobalStatisticSampleCountTotal()
}
func GlobalStatisticBucketLengthInMs() uint32 {
return globalCfg.GlobalStatisticIntervalMsTotal() / GlobalStatisticSampleCountTotal()
}
func MetricStatisticIntervalMs() uint32 {
return globalCfg.MetricStatisticIntervalMs()
}
func MetricStatisticSampleCount() uint32 {
return globalCfg.MetricStatisticSampleCount()
}