setting.go 18 KB

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