app.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import 'symbol-observable';
  2. import 'core-js';
  3. import 'regenerator-runtime/runtime';
  4. import 'whatwg-fetch'; // fetch polyfill needed for PhantomJs rendering
  5. import './polyfills/old-mediaquerylist'; // Safari < 14 does not have mql.addEventListener()
  6. import 'file-saver';
  7. import 'jquery';
  8. import 'app/features/all';
  9. import _ from 'lodash'; // eslint-disable-line lodash/import-scope
  10. import React from 'react';
  11. import ReactDOM from 'react-dom';
  12. import {
  13. locationUtil,
  14. monacoLanguageRegistry,
  15. setLocale,
  16. setTimeZoneResolver,
  17. setWeekStart,
  18. standardEditorsRegistry,
  19. standardFieldConfigEditorRegistry,
  20. standardTransformersRegistry,
  21. } from '@grafana/data';
  22. import {
  23. locationService,
  24. registerEchoBackend,
  25. setBackendSrv,
  26. setDataSourceSrv,
  27. setEchoSrv,
  28. setLocationSrv,
  29. setQueryRunnerFactory,
  30. } from '@grafana/runtime';
  31. import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
  32. import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer';
  33. import { getScrollbarWidth } from '@grafana/ui';
  34. import config from 'app/core/config';
  35. import { arrayMove } from 'app/core/utils/arrayMove';
  36. import { getStandardTransformers } from 'app/features/transformers/standardTransformers';
  37. import getDefaultMonacoLanguages from '../lib/monaco-languages';
  38. import { AppWrapper } from './AppWrapper';
  39. import { getAllOptionEditors, getAllStandardFieldConfigs } from './core/components/editors/registry';
  40. import { interceptLinkClicks } from './core/navigation/patch/interceptLinkClicks';
  41. import { ModalManager } from './core/services/ModalManager';
  42. import { backendSrv } from './core/services/backend_srv';
  43. import { contextSrv } from './core/services/context_srv';
  44. import { Echo } from './core/services/echo/Echo';
  45. import { reportPerformance } from './core/services/echo/EchoSrv';
  46. import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend';
  47. import { ApplicationInsightsBackend } from './core/services/echo/backends/analytics/ApplicationInsightsBackend';
  48. import { GAEchoBackend } from './core/services/echo/backends/analytics/GABackend';
  49. import { RudderstackBackend } from './core/services/echo/backends/analytics/RudderstackBackend';
  50. import { SentryEchoBackend } from './core/services/echo/backends/sentry/SentryBackend';
  51. import { initDevFeatures } from './dev';
  52. import { getTimeSrv } from './features/dashboard/services/TimeSrv';
  53. import { PanelDataErrorView } from './features/panel/components/PanelDataErrorView';
  54. import { PanelRenderer } from './features/panel/components/PanelRenderer';
  55. import { DatasourceSrv } from './features/plugins/datasource_srv';
  56. import { preloadPlugins } from './features/plugins/pluginPreloader';
  57. import { QueryRunner } from './features/query/state/QueryRunner';
  58. import { initWindowRuntime } from './features/runtime/init';
  59. import { variableAdapters } from './features/variables/adapters';
  60. import { createAdHocVariableAdapter } from './features/variables/adhoc/adapter';
  61. import { createConstantVariableAdapter } from './features/variables/constant/adapter';
  62. import { createCustomVariableAdapter } from './features/variables/custom/adapter';
  63. import { createDataSourceVariableAdapter } from './features/variables/datasource/adapter';
  64. import { getVariablesUrlParams } from './features/variables/getAllVariableValuesForUrl';
  65. import { createIntervalVariableAdapter } from './features/variables/interval/adapter';
  66. import { setVariableQueryRunner, VariableQueryRunner } from './features/variables/query/VariableQueryRunner';
  67. import { createQueryVariableAdapter } from './features/variables/query/adapter';
  68. import { createSystemVariableAdapter } from './features/variables/system/adapter';
  69. import { createTextBoxVariableAdapter } from './features/variables/textbox/adapter';
  70. import { configureStore } from './store/configureStore';
  71. // add move to lodash for backward compatabilty with plugins
  72. // @ts-ignore
  73. _.move = arrayMove;
  74. // import symlinked extensions
  75. const extensionsIndex = (require as any).context('.', true, /extensions\/index.ts/);
  76. const extensionsExports = extensionsIndex.keys().map((key: any) => {
  77. return extensionsIndex(key);
  78. });
  79. if (process.env.NODE_ENV === 'development') {
  80. initDevFeatures();
  81. }
  82. export class GrafanaApp {
  83. async init() {
  84. try {
  85. setBackendSrv(backendSrv);
  86. initEchoSrv();
  87. addClassIfNoOverlayScrollbar();
  88. setLocale(config.bootData.user.locale);
  89. setWeekStart(config.bootData.user.weekStart);
  90. setPanelRenderer(PanelRenderer);
  91. setPanelDataErrorView(PanelDataErrorView);
  92. setLocationSrv(locationService);
  93. setTimeZoneResolver(() => config.bootData.user.timezone);
  94. // Important that extension reducers are initialized before store
  95. addExtensionReducers();
  96. configureStore();
  97. initExtensions();
  98. standardEditorsRegistry.setInit(getAllOptionEditors);
  99. standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);
  100. standardTransformersRegistry.setInit(getStandardTransformers);
  101. variableAdapters.setInit(() => [
  102. createQueryVariableAdapter(),
  103. createCustomVariableAdapter(),
  104. createTextBoxVariableAdapter(),
  105. createConstantVariableAdapter(),
  106. createDataSourceVariableAdapter(),
  107. createIntervalVariableAdapter(),
  108. createAdHocVariableAdapter(),
  109. createSystemVariableAdapter(),
  110. ]);
  111. monacoLanguageRegistry.setInit(getDefaultMonacoLanguages);
  112. setQueryRunnerFactory(() => new QueryRunner());
  113. setVariableQueryRunner(new VariableQueryRunner());
  114. locationUtil.initialize({
  115. config,
  116. getTimeRangeForUrl: getTimeSrv().timeRangeForUrl,
  117. getVariablesUrlParams: getVariablesUrlParams,
  118. });
  119. // intercept anchor clicks and forward it to custom history instead of relying on browser's history
  120. document.addEventListener('click', interceptLinkClicks);
  121. // Init DataSourceSrv
  122. const dataSourceSrv = new DatasourceSrv();
  123. dataSourceSrv.init(config.datasources, config.defaultDatasource);
  124. setDataSourceSrv(dataSourceSrv);
  125. initWindowRuntime();
  126. // init modal manager
  127. const modalManager = new ModalManager();
  128. modalManager.init();
  129. // Preload selected app plugins
  130. await preloadPlugins(config.pluginsToPreload);
  131. ReactDOM.render(
  132. React.createElement(AppWrapper, {
  133. app: this,
  134. }),
  135. document.getElementById('reactRoot')
  136. );
  137. } catch (error: any) {
  138. console.error('Failed to start Grafana', error);
  139. window.__grafana_load_failed();
  140. }
  141. }
  142. }
  143. function addExtensionReducers() {
  144. if (extensionsExports.length > 0) {
  145. extensionsExports[0].addExtensionReducers();
  146. }
  147. }
  148. function initExtensions() {
  149. if (extensionsExports.length > 0) {
  150. extensionsExports[0].init();
  151. }
  152. }
  153. function initEchoSrv() {
  154. setEchoSrv(new Echo({ debug: process.env.NODE_ENV === 'development' }));
  155. window.addEventListener('load', (e) => {
  156. const loadMetricName = 'frontend_boot_load_time_seconds';
  157. // Metrics below are marked in public/views/index-template.html
  158. const jsLoadMetricName = 'frontend_boot_js_done_time_seconds';
  159. const cssLoadMetricName = 'frontend_boot_css_time_seconds';
  160. if (performance) {
  161. performance.mark(loadMetricName);
  162. reportMetricPerformanceMark('first-paint', 'frontend_boot_', '_time_seconds');
  163. reportMetricPerformanceMark('first-contentful-paint', 'frontend_boot_', '_time_seconds');
  164. reportMetricPerformanceMark(loadMetricName);
  165. reportMetricPerformanceMark(jsLoadMetricName);
  166. reportMetricPerformanceMark(cssLoadMetricName);
  167. }
  168. });
  169. if (contextSrv.user.orgRole !== '') {
  170. registerEchoBackend(new PerformanceBackend({}));
  171. }
  172. if (config.sentry.enabled) {
  173. registerEchoBackend(
  174. new SentryEchoBackend({
  175. ...config.sentry,
  176. user: config.bootData.user,
  177. buildInfo: config.buildInfo,
  178. })
  179. );
  180. }
  181. if ((config as any).googleAnalyticsId) {
  182. registerEchoBackend(
  183. new GAEchoBackend({
  184. googleAnalyticsId: (config as any).googleAnalyticsId,
  185. })
  186. );
  187. }
  188. if ((config as any).rudderstackWriteKey && (config as any).rudderstackDataPlaneUrl) {
  189. registerEchoBackend(
  190. new RudderstackBackend({
  191. writeKey: (config as any).rudderstackWriteKey,
  192. dataPlaneUrl: (config as any).rudderstackDataPlaneUrl,
  193. user: config.bootData.user,
  194. sdkUrl: (config as any).rudderstackSdkUrl,
  195. configUrl: (config as any).rudderstackConfigUrl,
  196. })
  197. );
  198. }
  199. if (config.applicationInsightsConnectionString) {
  200. registerEchoBackend(
  201. new ApplicationInsightsBackend({
  202. connectionString: config.applicationInsightsConnectionString,
  203. endpointUrl: config.applicationInsightsEndpointUrl,
  204. })
  205. );
  206. }
  207. }
  208. function addClassIfNoOverlayScrollbar() {
  209. if (getScrollbarWidth() > 0) {
  210. document.body.classList.add('no-overlay-scrollbar');
  211. }
  212. }
  213. /**
  214. * Report when a metric of a given name was marked during the document lifecycle. Works for markers with no duration,
  215. * like PerformanceMark or PerformancePaintTiming (e.g. created with performance.mark, or first-contentful-paint)
  216. */
  217. function reportMetricPerformanceMark(metricName: string, prefix = '', suffix = ''): void {
  218. const metric = _.first(performance.getEntriesByName(metricName));
  219. if (metric) {
  220. const metricName = metric.name.replace(/-/g, '_');
  221. reportPerformance(`${prefix}${metricName}${suffix}`, Math.round(metric.startTime) / 1000);
  222. }
  223. }
  224. export default new GrafanaApp();