setting.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. // Copyright 2014 Unknwon
  2. // Copyright 2014 Torkel Ödegaard
  3. package setting
  4. import (
  5. "bytes"
  6. "fmt"
  7. "net/url"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "regexp"
  12. "runtime"
  13. "strings"
  14. "gopkg.in/ini.v1"
  15. "github.com/go-macaron/session"
  16. "github.com/grafana/grafana/pkg/log"
  17. "github.com/grafana/grafana/pkg/util"
  18. )
  19. type Scheme string
  20. const (
  21. HTTP Scheme = "http"
  22. HTTPS Scheme = "https"
  23. SOCKET Scheme = "socket"
  24. DEFAULT_HTTP_ADDR string = "0.0.0.0"
  25. )
  26. const (
  27. DEV string = "development"
  28. PROD string = "production"
  29. TEST string = "test"
  30. )
  31. var (
  32. // App settings.
  33. Env string = DEV
  34. AppUrl string
  35. AppSubUrl string
  36. InstanceName string
  37. // build
  38. BuildVersion string
  39. BuildCommit string
  40. BuildStamp int64
  41. // Paths
  42. LogsPath string
  43. HomePath string
  44. DataPath string
  45. PluginsPath string
  46. ProvisioningPath string
  47. CustomInitPath = "conf/custom.ini"
  48. // Log settings.
  49. LogModes []string
  50. LogConfigs []util.DynMap
  51. // Http server options
  52. Protocol Scheme
  53. Domain string
  54. HttpAddr, HttpPort string
  55. SshPort int
  56. CertFile, KeyFile string
  57. SocketPath string
  58. RouterLogging bool
  59. DataProxyLogging bool
  60. StaticRootPath string
  61. EnableGzip bool
  62. EnforceDomain bool
  63. // Security settings.
  64. SecretKey string
  65. LogInRememberDays int
  66. CookieUserName string
  67. CookieRememberName string
  68. DisableGravatar bool
  69. EmailCodeValidMinutes int
  70. DataProxyWhiteList map[string]bool
  71. // Snapshots
  72. ExternalSnapshotUrl string
  73. ExternalSnapshotName string
  74. ExternalEnabled bool
  75. SnapShotTTLDays int
  76. SnapShotRemoveExpired bool
  77. // Dashboard history
  78. DashboardVersionsToKeep int
  79. // User settings
  80. AllowUserSignUp bool
  81. AllowUserOrgCreate bool
  82. AutoAssignOrg bool
  83. AutoAssignOrgRole string
  84. VerifyEmailEnabled bool
  85. LoginHint string
  86. DefaultTheme string
  87. DisableLoginForm bool
  88. DisableSignoutMenu bool
  89. ExternalUserMngLinkUrl string
  90. ExternalUserMngLinkName string
  91. ExternalUserMngInfo string
  92. // Http auth
  93. AdminUser string
  94. AdminPassword string
  95. AnonymousEnabled bool
  96. AnonymousOrgName string
  97. AnonymousOrgRole string
  98. // Auth proxy settings
  99. AuthProxyEnabled bool
  100. AuthProxyHeaderName string
  101. AuthProxyHeaderProperty string
  102. AuthProxyAutoSignUp bool
  103. AuthProxyLdapSyncTtl int
  104. AuthProxyWhitelist string
  105. // Basic Auth
  106. BasicAuthEnabled bool
  107. // Plugin settings
  108. PluginAppsSkipVerifyTLS bool
  109. // Session settings.
  110. SessionOptions session.Options
  111. // Global setting objects.
  112. Cfg *ini.File
  113. ConfRootPath string
  114. IsWindows bool
  115. // PhantomJs Rendering
  116. ImagesDir string
  117. PhantomDir string
  118. // for logging purposes
  119. configFiles []string
  120. appliedCommandLineProperties []string
  121. appliedEnvOverrides []string
  122. ReportingEnabled bool
  123. CheckForUpdates bool
  124. GoogleAnalyticsId string
  125. GoogleTagManagerId string
  126. // LDAP
  127. LdapEnabled bool
  128. LdapConfigFile string
  129. LdapAllowSignup bool = true
  130. // SMTP email settings
  131. Smtp SmtpSettings
  132. // QUOTA
  133. Quota QuotaSettings
  134. // Alerting
  135. AlertingEnabled bool
  136. ExecuteAlerts bool
  137. // logger
  138. logger log.Logger
  139. // Grafana.NET URL
  140. GrafanaComUrl string
  141. // S3 temp image store
  142. S3TempImageStoreBucketUrl string
  143. S3TempImageStoreAccessKey string
  144. S3TempImageStoreSecretKey string
  145. ImageUploadProvider string
  146. )
  147. type CommandLineArgs struct {
  148. Config string
  149. HomePath string
  150. Args []string
  151. }
  152. func init() {
  153. IsWindows = runtime.GOOS == "windows"
  154. logger = log.New("settings")
  155. }
  156. func parseAppUrlAndSubUrl(section *ini.Section) (string, string) {
  157. appUrl := section.Key("root_url").MustString("http://localhost:3000/")
  158. if appUrl[len(appUrl)-1] != '/' {
  159. appUrl += "/"
  160. }
  161. // Check if has app suburl.
  162. url, err := url.Parse(appUrl)
  163. if err != nil {
  164. log.Fatal(4, "Invalid root_url(%s): %s", appUrl, err)
  165. }
  166. appSubUrl := strings.TrimSuffix(url.Path, "/")
  167. return appUrl, appSubUrl
  168. }
  169. func ToAbsUrl(relativeUrl string) string {
  170. return AppUrl + relativeUrl
  171. }
  172. func shouldRedactKey(s string) bool {
  173. uppercased := strings.ToUpper(s)
  174. return strings.Contains(uppercased, "PASSWORD") || strings.Contains(uppercased, "SECRET") || strings.Contains(uppercased, "PROVIDER_CONFIG")
  175. }
  176. func shouldRedactURLKey(s string) bool {
  177. uppercased := strings.ToUpper(s)
  178. return strings.Contains(uppercased, "DATABASE_URL")
  179. }
  180. func applyEnvVariableOverrides() {
  181. appliedEnvOverrides = make([]string, 0)
  182. for _, section := range Cfg.Sections() {
  183. for _, key := range section.Keys() {
  184. sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
  185. keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
  186. envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName)
  187. envValue := os.Getenv(envKey)
  188. if len(envValue) > 0 {
  189. key.SetValue(envValue)
  190. if shouldRedactKey(envKey) {
  191. envValue = "*********"
  192. }
  193. if shouldRedactURLKey(envKey) {
  194. u, _ := url.Parse(envValue)
  195. ui := u.User
  196. if ui != nil {
  197. _, exists := ui.Password()
  198. if exists {
  199. u.User = url.UserPassword(ui.Username(), "-redacted-")
  200. envValue = u.String()
  201. }
  202. }
  203. }
  204. appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
  205. }
  206. }
  207. }
  208. }
  209. func applyCommandLineDefaultProperties(props map[string]string) {
  210. appliedCommandLineProperties = make([]string, 0)
  211. for _, section := range Cfg.Sections() {
  212. for _, key := range section.Keys() {
  213. keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
  214. value, exists := props[keyString]
  215. if exists {
  216. key.SetValue(value)
  217. if shouldRedactKey(keyString) {
  218. value = "*********"
  219. }
  220. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  221. }
  222. }
  223. }
  224. }
  225. func applyCommandLineProperties(props map[string]string) {
  226. for _, section := range Cfg.Sections() {
  227. sectionName := section.Name() + "."
  228. if section.Name() == ini.DEFAULT_SECTION {
  229. sectionName = ""
  230. }
  231. for _, key := range section.Keys() {
  232. keyString := sectionName + key.Name()
  233. value, exists := props[keyString]
  234. if exists {
  235. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  236. key.SetValue(value)
  237. }
  238. }
  239. }
  240. }
  241. func getCommandLineProperties(args []string) map[string]string {
  242. props := make(map[string]string)
  243. for _, arg := range args {
  244. if !strings.HasPrefix(arg, "cfg:") {
  245. continue
  246. }
  247. trimmed := strings.TrimPrefix(arg, "cfg:")
  248. parts := strings.Split(trimmed, "=")
  249. if len(parts) != 2 {
  250. log.Fatal(3, "Invalid command line argument", arg)
  251. return nil
  252. }
  253. props[parts[0]] = parts[1]
  254. }
  255. return props
  256. }
  257. func makeAbsolute(path string, root string) string {
  258. if filepath.IsAbs(path) {
  259. return path
  260. }
  261. return filepath.Join(root, path)
  262. }
  263. func evalEnvVarExpression(value string) string {
  264. regex := regexp.MustCompile(`\${(\w+)}`)
  265. return regex.ReplaceAllStringFunc(value, func(envVar string) string {
  266. envVar = strings.TrimPrefix(envVar, "${")
  267. envVar = strings.TrimSuffix(envVar, "}")
  268. envValue := os.Getenv(envVar)
  269. // if env variable is hostname and it is empty use os.Hostname as default
  270. if envVar == "HOSTNAME" && envValue == "" {
  271. envValue, _ = os.Hostname()
  272. }
  273. return envValue
  274. })
  275. }
  276. func evalConfigValues() {
  277. for _, section := range Cfg.Sections() {
  278. for _, key := range section.Keys() {
  279. key.SetValue(evalEnvVarExpression(key.Value()))
  280. }
  281. }
  282. }
  283. func loadSpecifedConfigFile(configFile string) error {
  284. if configFile == "" {
  285. configFile = filepath.Join(HomePath, CustomInitPath)
  286. // return without error if custom file does not exist
  287. if !pathExists(configFile) {
  288. return nil
  289. }
  290. }
  291. userConfig, err := ini.Load(configFile)
  292. if err != nil {
  293. return fmt.Errorf("Failed to parse %v, %v", configFile, err)
  294. }
  295. userConfig.BlockMode = false
  296. for _, section := range userConfig.Sections() {
  297. for _, key := range section.Keys() {
  298. if key.Value() == "" {
  299. continue
  300. }
  301. defaultSec, err := Cfg.GetSection(section.Name())
  302. if err != nil {
  303. defaultSec, _ = Cfg.NewSection(section.Name())
  304. }
  305. defaultKey, err := defaultSec.GetKey(key.Name())
  306. if err != nil {
  307. defaultKey, _ = defaultSec.NewKey(key.Name(), key.Value())
  308. }
  309. defaultKey.SetValue(key.Value())
  310. }
  311. }
  312. configFiles = append(configFiles, configFile)
  313. return nil
  314. }
  315. func loadConfiguration(args *CommandLineArgs) {
  316. var err error
  317. // load config defaults
  318. defaultConfigFile := path.Join(HomePath, "conf/defaults.ini")
  319. configFiles = append(configFiles, defaultConfigFile)
  320. // check if config file exists
  321. if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
  322. fmt.Println("Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath")
  323. os.Exit(1)
  324. }
  325. // load defaults
  326. Cfg, err = ini.Load(defaultConfigFile)
  327. if err != nil {
  328. fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err))
  329. os.Exit(1)
  330. return
  331. }
  332. Cfg.BlockMode = false
  333. // command line props
  334. commandLineProps := getCommandLineProperties(args.Args)
  335. // load default overrides
  336. applyCommandLineDefaultProperties(commandLineProps)
  337. // load specified config file
  338. err = loadSpecifedConfigFile(args.Config)
  339. if err != nil {
  340. initLogging()
  341. log.Fatal(3, err.Error())
  342. }
  343. // apply environment overrides
  344. applyEnvVariableOverrides()
  345. // apply command line overrides
  346. applyCommandLineProperties(commandLineProps)
  347. // evaluate config values containing environment variables
  348. evalConfigValues()
  349. // update data path and logging config
  350. DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath)
  351. initLogging()
  352. }
  353. func pathExists(path string) bool {
  354. _, err := os.Stat(path)
  355. if err == nil {
  356. return true
  357. }
  358. if os.IsNotExist(err) {
  359. return false
  360. }
  361. return false
  362. }
  363. func setHomePath(args *CommandLineArgs) {
  364. if args.HomePath != "" {
  365. HomePath = args.HomePath
  366. return
  367. }
  368. HomePath, _ = filepath.Abs(".")
  369. // check if homepath is correct
  370. if pathExists(filepath.Join(HomePath, "conf/defaults.ini")) {
  371. return
  372. }
  373. // try down one path
  374. if pathExists(filepath.Join(HomePath, "../conf/defaults.ini")) {
  375. HomePath = filepath.Join(HomePath, "../")
  376. }
  377. }
  378. var skipStaticRootValidation bool = false
  379. func validateStaticRootPath() error {
  380. if skipStaticRootValidation {
  381. return nil
  382. }
  383. if _, err := os.Stat(path.Join(StaticRootPath, "build")); err != nil {
  384. logger.Error("Failed to detect generated javascript files in public/build")
  385. }
  386. return nil
  387. }
  388. func NewConfigContext(args *CommandLineArgs) error {
  389. setHomePath(args)
  390. loadConfiguration(args)
  391. Env = Cfg.Section("").Key("app_mode").MustString("development")
  392. InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
  393. PluginsPath = makeAbsolute(Cfg.Section("paths").Key("plugins").String(), HomePath)
  394. ProvisioningPath = makeAbsolute(Cfg.Section("paths").Key("provisioning").String(), HomePath)
  395. server := Cfg.Section("server")
  396. AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
  397. Protocol = HTTP
  398. if server.Key("protocol").MustString("http") == "https" {
  399. Protocol = HTTPS
  400. CertFile = server.Key("cert_file").String()
  401. KeyFile = server.Key("cert_key").String()
  402. }
  403. if server.Key("protocol").MustString("http") == "socket" {
  404. Protocol = SOCKET
  405. SocketPath = server.Key("socket").String()
  406. }
  407. Domain = server.Key("domain").MustString("localhost")
  408. HttpAddr = server.Key("http_addr").MustString(DEFAULT_HTTP_ADDR)
  409. HttpPort = server.Key("http_port").MustString("3000")
  410. RouterLogging = server.Key("router_logging").MustBool(false)
  411. EnableGzip = server.Key("enable_gzip").MustBool(false)
  412. EnforceDomain = server.Key("enforce_domain").MustBool(false)
  413. StaticRootPath = makeAbsolute(server.Key("static_root_path").String(), HomePath)
  414. if err := validateStaticRootPath(); err != nil {
  415. return err
  416. }
  417. // read data proxy settings
  418. dataproxy := Cfg.Section("dataproxy")
  419. DataProxyLogging = dataproxy.Key("logging").MustBool(false)
  420. // read security settings
  421. security := Cfg.Section("security")
  422. SecretKey = security.Key("secret_key").String()
  423. LogInRememberDays = security.Key("login_remember_days").MustInt()
  424. CookieUserName = security.Key("cookie_username").String()
  425. CookieRememberName = security.Key("cookie_remember_name").String()
  426. DisableGravatar = security.Key("disable_gravatar").MustBool(true)
  427. // read snapshots settings
  428. snapshots := Cfg.Section("snapshots")
  429. ExternalSnapshotUrl = snapshots.Key("external_snapshot_url").String()
  430. ExternalSnapshotName = snapshots.Key("external_snapshot_name").String()
  431. ExternalEnabled = snapshots.Key("external_enabled").MustBool(true)
  432. SnapShotRemoveExpired = snapshots.Key("snapshot_remove_expired").MustBool(true)
  433. SnapShotTTLDays = snapshots.Key("snapshot_TTL_days").MustInt(90)
  434. // read dashboard settings
  435. dashboards := Cfg.Section("dashboards")
  436. DashboardVersionsToKeep = dashboards.Key("versions_to_keep").MustInt(20)
  437. // read data source proxy white list
  438. DataProxyWhiteList = make(map[string]bool)
  439. for _, hostAndIp := range util.SplitString(security.Key("data_source_proxy_whitelist").String()) {
  440. DataProxyWhiteList[hostAndIp] = true
  441. }
  442. // admin
  443. AdminUser = security.Key("admin_user").String()
  444. AdminPassword = security.Key("admin_password").String()
  445. users := Cfg.Section("users")
  446. AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
  447. AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
  448. AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
  449. AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Read Only Editor", "Viewer"})
  450. VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
  451. LoginHint = users.Key("login_hint").String()
  452. DefaultTheme = users.Key("default_theme").String()
  453. ExternalUserMngLinkUrl = users.Key("external_manage_link_url").String()
  454. ExternalUserMngLinkName = users.Key("external_manage_link_name").String()
  455. ExternalUserMngInfo = users.Key("external_manage_info").String()
  456. // auth
  457. auth := Cfg.Section("auth")
  458. DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
  459. DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
  460. // anonymous access
  461. AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
  462. AnonymousOrgName = Cfg.Section("auth.anonymous").Key("org_name").String()
  463. AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String()
  464. // auth proxy
  465. authProxy := Cfg.Section("auth.proxy")
  466. AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
  467. AuthProxyHeaderName = authProxy.Key("header_name").String()
  468. AuthProxyHeaderProperty = authProxy.Key("header_property").String()
  469. AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
  470. AuthProxyLdapSyncTtl = authProxy.Key("ldap_sync_ttl").MustInt()
  471. AuthProxyWhitelist = authProxy.Key("whitelist").String()
  472. // basic auth
  473. authBasic := Cfg.Section("auth.basic")
  474. BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
  475. // global plugin settings
  476. PluginAppsSkipVerifyTLS = Cfg.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
  477. // PhantomJS rendering
  478. ImagesDir = filepath.Join(DataPath, "png")
  479. PhantomDir = filepath.Join(HomePath, "vendor/phantomjs")
  480. analytics := Cfg.Section("analytics")
  481. ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
  482. CheckForUpdates = analytics.Key("check_for_updates").MustBool(true)
  483. GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
  484. GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
  485. ldapSec := Cfg.Section("auth.ldap")
  486. LdapEnabled = ldapSec.Key("enabled").MustBool(false)
  487. LdapConfigFile = ldapSec.Key("config_file").String()
  488. LdapAllowSignup = ldapSec.Key("allow_sign_up").MustBool(true)
  489. alerting := Cfg.Section("alerting")
  490. AlertingEnabled = alerting.Key("enabled").MustBool(true)
  491. ExecuteAlerts = alerting.Key("execute_alerts").MustBool(true)
  492. readSessionConfig()
  493. readSmtpSettings()
  494. readQuotaSettings()
  495. if VerifyEmailEnabled && !Smtp.Enabled {
  496. log.Warn("require_email_validation is enabled but smpt is disabled")
  497. }
  498. // check old key name
  499. GrafanaComUrl = Cfg.Section("grafana_net").Key("url").MustString("")
  500. if GrafanaComUrl == "" {
  501. GrafanaComUrl = Cfg.Section("grafana_com").Key("url").MustString("https://grafana.com")
  502. }
  503. imageUploadingSection := Cfg.Section("external_image_storage")
  504. ImageUploadProvider = imageUploadingSection.Key("provider").MustString("internal")
  505. return nil
  506. }
  507. func readSessionConfig() {
  508. sec := Cfg.Section("session")
  509. SessionOptions = session.Options{}
  510. SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql", "postgres", "memcache"})
  511. SessionOptions.ProviderConfig = strings.Trim(sec.Key("provider_config").String(), "\" ")
  512. SessionOptions.CookieName = sec.Key("cookie_name").MustString("grafana_sess")
  513. SessionOptions.CookiePath = AppSubUrl
  514. SessionOptions.Secure = sec.Key("cookie_secure").MustBool()
  515. SessionOptions.Gclifetime = Cfg.Section("session").Key("gc_interval_time").MustInt64(86400)
  516. SessionOptions.Maxlifetime = Cfg.Section("session").Key("session_life_time").MustInt64(86400)
  517. SessionOptions.IDLength = 16
  518. if SessionOptions.Provider == "file" {
  519. SessionOptions.ProviderConfig = makeAbsolute(SessionOptions.ProviderConfig, DataPath)
  520. os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
  521. }
  522. if SessionOptions.CookiePath == "" {
  523. SessionOptions.CookiePath = "/"
  524. }
  525. }
  526. func initLogging() {
  527. // split on comma
  528. LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), ",")
  529. // also try space
  530. if len(LogModes) == 1 {
  531. LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), " ")
  532. }
  533. LogsPath = makeAbsolute(Cfg.Section("paths").Key("logs").String(), HomePath)
  534. log.ReadLoggingConfig(LogModes, LogsPath, Cfg)
  535. }
  536. func LogConfigurationInfo() {
  537. var text bytes.Buffer
  538. for _, file := range configFiles {
  539. logger.Info("Config loaded from", "file", file)
  540. }
  541. if len(appliedCommandLineProperties) > 0 {
  542. for _, prop := range appliedCommandLineProperties {
  543. logger.Info("Config overridden from command line", "arg", prop)
  544. }
  545. }
  546. if len(appliedEnvOverrides) > 0 {
  547. text.WriteString("\tEnvironment variables used:\n")
  548. for _, prop := range appliedEnvOverrides {
  549. logger.Info("Config overridden from Environment variable", "var", prop)
  550. }
  551. }
  552. logger.Info("Path Home", "path", HomePath)
  553. logger.Info("Path Data", "path", DataPath)
  554. logger.Info("Path Logs", "path", LogsPath)
  555. logger.Info("Path Plugins", "path", PluginsPath)
  556. logger.Info("Path Provisioning", "path", ProvisioningPath)
  557. logger.Info("App mode " + Env)
  558. }