The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/app.ts

232 lines
7.2 KiB

import 'symbol-observable';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import 'whatwg-fetch'; // fetch polyfill needed for PhantomJs rendering
import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'; // fetch polyfill needed for PhantomJs rendering
// @ts-ignore
import ttiPolyfill from 'tti-polyfill';
import 'file-saver';
import 'lodash';
import 'jquery';
import 'angular';
import 'angular-route';
import 'angular-sanitize';
import 'angular-bindonce';
import 'react';
import 'react-dom';
import 'vendor/bootstrap/bootstrap';
import 'vendor/angular-other/angular-strap';
import $ from 'jquery';
import angular from 'angular';
import config from 'app/core/config';
// @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move
import _ from 'lodash';
import {
AppEvents,
setLocale,
standardEditorsRegistry,
standardFieldConfigEditorRegistry,
standardTransformersRegistry,
DateTime: adding support to select preferred timezone for presentation of date and time values. (#23586) * added moment timezone package. * added a qnd way of selecting timezone. * added a first draft to display how it can be used. * fixed failing tests. * made moment.local to be in utc when running tests. * added tests to verify that the timeZone support works as expected. * Fixed so we use the formatter in the graph context menu. * changed so we will format d3 according to timeZone. * changed from class base to function based for easier consumption. * fixed so tests got green. * renamed to make it shorter. * fixed formatting in logRow. * removed unused value. * added time formatter to flot. * fixed failing tests. * changed so history will use the formatting with support for timezone. * added todo. * added so we append the correct abbrivation behind time. * added time zone abbrevation in timepicker. * adding timezone in rangeutil tool. * will use timezone when formatting range. * changed so we use new functions to format date so timezone is respected. * wip - dashboard settings. * changed so the time picker settings is in react. * added force update. * wip to get the react graph to work. * fixed formatting and parsing on the timepicker. * updated snap to be correct. * fixed so we format values properly in time picker. * make sure we pass timezone on all the proper places. * fixed so we use correct timeZone in explore. * fixed failing tests. * fixed so we always parse from local to selected timezone. * removed unused variable. * reverted back. * trying to fix issue with directive. * fixed issue. * fixed strict null errors. * fixed so we still can select default. * make sure we reads the time zone from getTimezone
5 years ago
setTimeZoneResolver,
} from '@grafana/data';
import appEvents from 'app/core/app_events';
import { checkBrowserCompatibility } from 'app/core/utils/browser';
import { importPluginModule } from 'app/features/plugins/plugin_loader';
import { angularModules, coreModule } from 'app/core/core_module';
import { registerAngularDirectives } from 'app/core/core';
import { setupAngularRoutes } from 'app/routes/routes';
import { registerEchoBackend, setEchoSrv } from '@grafana/runtime';
import { Echo } from './core/services/echo/Echo';
import { reportPerformance } from './core/services/echo/EchoSrv';
import { PerformanceBackend } from './core/services/echo/backends/PerformanceBackend';
import 'app/routes/GrafanaCtrl';
import 'app/features/all';
import { getStandardFieldConfigs, getStandardOptionEditors, getScrollbarWidth } from '@grafana/ui';
import { getDefaultVariableAdapters, variableAdapters } from './features/variables/adapters';
import { initDevFeatures } from './dev';
import { getStandardTransformers } from 'app/core/utils/standardTransformers';
import { SentryEchoBackend } from './core/services/echo/backends/sentry/SentryBackend';
import { monkeyPatchInjectorWithPreAssignedBindings } from './core/injectorMonkeyPatch';
// add move to lodash for backward compatabiltiy
// @ts-ignore
_.move = (array: [], fromIndex: number, toIndex: number) => {
array.splice(toIndex, 0, array.splice(fromIndex, 1)[0]);
return array;
};
// import symlinked extensions
const extensionsIndex = (require as any).context('.', true, /extensions\/index.ts/);
extensionsIndex.keys().forEach((key: any) => {
extensionsIndex(key);
});
if (process.env.NODE_ENV === 'development') {
initDevFeatures();
}
export class GrafanaApp {
registerFunctions: any;
ngModuleDependencies: any[];
preBootModules: any[] | null;
constructor() {
this.preBootModules = [];
this.registerFunctions = {};
this.ngModuleDependencies = [];
}
useModule(module: angular.IModule) {
if (this.preBootModules) {
this.preBootModules.push(module);
} else {
_.extend(module, this.registerFunctions);
}
this.ngModuleDependencies.push(module.name);
return module;
}
init() {
const app = angular.module('grafana', []);
addClassIfNoOverlayScrollbar();
setLocale(config.bootData.user.locale);
setTimeZoneResolver(() => config.bootData.user.timezone);
standardEditorsRegistry.setInit(getStandardOptionEditors);
standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
standardTransformersRegistry.setInit(getStandardTransformers);
variableAdapters.setInit(getDefaultVariableAdapters);
app.config(
(
$controllerProvider: angular.IControllerProvider,
$compileProvider: angular.ICompileProvider,
$filterProvider: angular.IFilterProvider,
$httpProvider: angular.IHttpProvider,
$provide: angular.auto.IProvideService
) => {
if (config.buildInfo.env !== 'development') {
$compileProvider.debugInfoEnabled(false);
}
$httpProvider.useApplyAsync(true);
this.registerFunctions.controller = $controllerProvider.register;
this.registerFunctions.directive = $compileProvider.directive;
this.registerFunctions.factory = $provide.factory;
this.registerFunctions.service = $provide.service;
this.registerFunctions.filter = $filterProvider.register;
$provide.decorator('$http', [
'$delegate',
'$templateCache',
($delegate: any, $templateCache: any) => {
const get = $delegate.get;
$delegate.get = (url: string, config: any) => {
if (url.match(/\.html$/)) {
// some template's already exist in the cache
if (!$templateCache.get(url)) {
url += '?v=' + new Date().getTime();
}
}
return get(url, config);
};
return $delegate;
},
]);
}
);
this.ngModuleDependencies = [
'grafana.core',
'ngRoute',
'ngSanitize',
'$strap.directives',
'grafana',
'pasvaz.bindonce',
'react',
];
// makes it possible to add dynamic stuff
_.each(angularModules, (m: angular.IModule) => {
this.useModule(m);
});
// register react angular wrappers
coreModule.config(setupAngularRoutes);
registerAngularDirectives();
// disable tool tip animation
$.fn.tooltip.defaults.animation = false;
// bootstrap the app
const injector: any = angular.bootstrap(document, this.ngModuleDependencies);
injector.invoke(() => {
_.each(this.preBootModules, (module: angular.IModule) => {
_.extend(module, this.registerFunctions);
});
this.preBootModules = null;
if (!checkBrowserCompatibility()) {
setTimeout(() => {
appEvents.emit(AppEvents.alertWarning, [
'Your browser is not fully supported',
'A newer browser version is recommended',
]);
}, 1000);
}
});
monkeyPatchInjectorWithPreAssignedBindings(injector);
// Preload selected app plugins
for (const modulePath of config.pluginsToPreload) {
importPluginModule(modulePath);
}
}
initEchoSrv() {
setEchoSrv(new Echo({ debug: process.env.NODE_ENV === 'development' }));
ttiPolyfill.getFirstConsistentlyInteractive().then((tti: any) => {
// Collecting paint metrics first
const paintMetrics = performance && performance.getEntriesByType ? performance.getEntriesByType('paint') : [];
for (const metric of paintMetrics) {
reportPerformance(metric.name, Math.round(metric.startTime + metric.duration));
}
reportPerformance('tti', tti);
});
registerEchoBackend(new PerformanceBackend({}));
if (config.sentry.enabled) {
registerEchoBackend(
new SentryEchoBackend({
...config.sentry,
user: config.bootData.user,
buildInfo: config.buildInfo,
})
);
}
window.addEventListener('DOMContentLoaded', () => {
reportPerformance('dcl', Math.round(performance.now()));
});
}
}
function addClassIfNoOverlayScrollbar() {
if (getScrollbarWidth() > 0) {
document.body.classList.add('no-overlay-scrollbar');
}
}
export default new GrafanaApp();