(ns metabase.core (:require [clojure.string :as str] [clojure.tools.trace :as trace] [environ.core :as env] [java-time.api :as t] [metabase.analytics.prometheus :as prometheus] [metabase.config :as config] [metabase.core.config-from-file :as config-from-file] [metabase.core.initialization-status :as init-status] [metabase.db :as mdb] [metabase.driver.h2] [metabase.driver.mysql] [metabase.driver.postgres] [metabase.embed.settings :as embed.settings] [metabase.events :as events] [metabase.logger :as logger] [metabase.models.cloud-migration :as cloud-migration] [metabase.models.database :as database] [metabase.models.setting :as settings] [metabase.notification.core :as notification] [metabase.plugins :as plugins] [metabase.plugins.classloader :as classloader] [metabase.public-settings :as public-settings] [metabase.public-settings.premium-features :as premium-features :refer [defenterprise]] [metabase.sample-data :as sample-data] [metabase.server.core :as server] [metabase.setup :as setup] [metabase.task :as task] [metabase.troubleshooting :as troubleshooting] [metabase.util :as u] [metabase.util.log :as log]) (:import (java.lang.management ManagementFactory))) | |
(set! *warn-on-reflection* true) | |
(comment ;; Load up the drivers shipped as part of the main codebase, so they will show up in the list of available DB types metabase.driver.h2/keep-me metabase.driver.mysql/keep-me metabase.driver.postgres/keep-me ;; Make sure the custom Metabase logger code gets loaded up so we use our custom logger for performance reasons. logger/keep-me) | |
don't i18n this, it's legalese | (log/info (format "\nMetabase %s" config/mb-version-string) (format "\n\nCopyright © %d Metabase, Inc." (.getYear (java.time.LocalDate/now))) (str "\n\n" (if config/ee-available? (str "Metabase Enterprise Edition extensions are PRESENT." "\n\n" "Usage of Metabase Enterprise Edition features are subject to the Metabase Commercial License." "See https://www.metabase.com/license/commercial/ for details.") "Metabase Enterprise Edition extensions are NOT PRESENT."))) |
--------------------------------------------------- Lifecycle ---------------------------------------------------- | |
Print the setup url during instance initialization. | (defn- print-setup-url [] (let [hostname (or (config/config-str :mb-jetty-host) "localhost") port (config/config-int :mb-jetty-port) site-url (or (public-settings/site-url) (str "http://" hostname (when-not (= 80 port) (str ":" port)))) setup-url (str site-url "/setup/")] (log/info (u/format-color 'green (str "Please use the following URL to setup your Metabase installation:" "\n\n" setup-url "\n\n"))))) |
Create and set a new setup token and log it. | (defn- create-setup-token-and-log-setup-url! [] (setup/create-token!) ; we need this here to create the initial token (print-setup-url)) |
General application shutdown function which should be called once at application shutdown. | (defn- destroy! [] (log/info "Metabase Shutting Down ...") (task/stop-scheduler!) (server/stop-web-server!) (prometheus/shutdown!) ;; This timeout was chosen based on a 30s default termination grace period in Kubernetes. (let [timeout-seconds 20] (mdb/release-migration-locks! timeout-seconds)) (log/info "Metabase Shutdown COMPLETE")) |
OSS implementation of | (defenterprise ensure-audit-db-installed! metabase-enterprise.audit-app.audit [] ::noop) |
General application initialization function which should be run once at application startup. | (defn- init!* [] (log/infof "Starting Metabase version %s ..." config/mb-version-string) (log/infof "System info:\n %s" (u/pprint-to-str (troubleshooting/system-info))) (init-status/set-progress! 0.1) ;; First of all, lets register a shutdown hook that will tidy things up for us on app exit (.addShutdownHook (Runtime/getRuntime) (Thread. ^Runnable destroy!)) (init-status/set-progress! 0.2) ;; load any plugins as needed (plugins/load-plugins!) (init-status/set-progress! 0.3) (settings/validate-settings-formatting!) ;; startup database. validates connection & runs any necessary migrations (log/info "Setting up and migrating Metabase DB. Please sit tight, this may take a minute...") ;; Cal 2024-04-03: ;; we have to skip creating sample content if we're running tests, because it causes some tests to timeout ;; and the test suite can take 2x longer. this is really unfortunate because it could lead to some false ;; negatives, but for now there's not much we can do (mdb/setup-db! :create-sample-content? (not config/is-test?)) ;; Disable read-only mode if its on during startup. ;; This can happen if a cloud migration process dies during h2 dump. (when (cloud-migration/read-only-mode) (cloud-migration/read-only-mode! false)) (init-status/set-progress! 0.4) ;; Set up Prometheus (log/info "Setting up prometheus metrics") (prometheus/setup!) (init-status/set-progress! 0.5) (premium-features/airgap-check-user-count) (init-status/set-progress! 0.55) ;; run a very quick check to see if we are doing a first time installation ;; the test we are using is if there is at least 1 User in the database (let [new-install? (not (setup/has-user-setup))] ;; initialize Metabase from an `config.yml` file if present (Enterprise Editionâ„¢ only) (config-from-file/init-from-file-if-code-available!) (init-status/set-progress! 0.6) (when new-install? (log/info "Looks like this is a new installation ... preparing setup wizard") ;; create setup token (create-setup-token-and-log-setup-url!) ;; publish install event (events/publish-event! :event/install {})) (init-status/set-progress! 0.7) ;; deal with our sample database as needed (when (config/load-sample-content?) (if new-install? ;; add the sample database DB for fresh installs (sample-data/extract-and-sync-sample-database!) ;; otherwise update if appropriate (sample-data/update-sample-database-if-needed!))) (init-status/set-progress! 0.8)) (ensure-audit-db-installed!) (notification/seed-notification!) (init-status/set-progress! 0.9) (embed.settings/check-and-sync-settings-on-startup! env/env) (init-status/set-progress! 0.95) (settings/migrate-encrypted-settings!) ;; start scheduler at end of init! (task/start-scheduler!) ;; In case we could not do this earlier (e.g. for DBs added via config file), because the scheduler was not up yet: (database/check-and-schedule-tasks!) (init-status/set-complete!) (let [start-time (.getStartTime (ManagementFactory/getRuntimeMXBean)) duration (- (System/currentTimeMillis) start-time)] (log/infof "Metabase Initialization COMPLETE in %s" (u/format-milliseconds duration)))) |
General application initialization function which should be run once at application startup. Calls `[[init!*]] and records the duration of startup. | (defn init! [] (let [start-time (t/zoned-date-time)] (init!*) (public-settings/startup-time-millis! (.toMillis (t/duration start-time (t/zoned-date-time)))))) |
-------------------------------------------------- Normal Start -------------------------------------------------- | |
(defn- start-normally [] (log/info "Starting Metabase in STANDALONE mode") (try ;; launch embedded webserver async (server/start-web-server! (server/handler)) ;; run our initialization process (init!) ;; Ok, now block forever while Jetty does its thing (when (config/config-bool :mb-jetty-join) (.join (server/instance))) (catch Throwable e (log/error e "Metabase Initialization FAILED") (System/exit 1)))) | |
(defn- run-cmd [cmd init-fn args] (classloader/require 'metabase.cmd) ((resolve 'metabase.cmd/run-cmd) cmd init-fn args)) | |
-------------------------------------------------- Tracing ------------------------------------------------------- | |
(defn- maybe-enable-tracing [] (let [mb-trace-str (config/config-str :mb-ns-trace)] (when (not-empty mb-trace-str) (log/warn "WARNING: You have enabled namespace tracing, which could log sensitive information like db passwords.") (doseq [namespace (map symbol (str/split mb-trace-str #",\s*"))] (try (require namespace) (catch Throwable _ (throw (ex-info "A namespace you specified with MB_NS_TRACE could not be required" {:namespace namespace})))) (trace/trace-ns namespace))))) | |
------------------------------------------------ App Entry Point ------------------------------------------------- | |
Launch Metabase in standalone mode. (Main application entrypoint is [[metabase.bootstrap/-main]].) | (defn entrypoint [& [cmd & args]] (maybe-enable-tracing) (if cmd ;; run a command like `java --add-opens java.base/java.nio=ALL-UNNAMED -jar metabase.jar migrate release-locks` or ;; `clojure -M:run migrate release-locks` (run-cmd cmd init! args) ;; with no command line args just start Metabase normally (start-normally))) |