setting.go 18 KB

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