setting.go 18 KB

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