diff --git a/package.json b/package.json index f2e79c38c..23599ffe6 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "dependencies": { "@azure/storage-blob": "10.3.0", "@fluentui/react": "^7.117.2", + "@microsoft/applicationinsights-react-js": "^3.0.1", + "@microsoft/applicationinsights-web": "^2.5.6", "axios": "^0.19.0", "bootstrap": "^4.4.1", "chart.js": "^2.9.3", diff --git a/src/App.tsx b/src/App.tsx index 8bb9bf4a6..b8a53145c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,6 @@ import { connect } from "react-redux"; import { BrowserRouter } from "react-router-dom"; import { ToastContainer } from "react-toastify"; import { IAppError, IApplicationState, IProject, ErrorCode } from "./models/applicationState"; -import { strings } from "./common/strings"; import IAppErrorActions, * as appErrorActions from "./redux/actions/appErrorActions"; import { ErrorHandler } from "./react/components/common/errorHandler/errorHandler"; import { KeyboardManager } from "./react/components/common/keyboardManager/keyboardManager"; @@ -18,7 +17,8 @@ import { Sidebar } from "./react/components/shell/sidebar"; import { StatusBar } from "./react/components/shell/statusBar"; import { StatusBarMetrics } from "./react/components/shell/statusBarMetrics"; import { TitleBar } from "./react/components/shell/titleBar"; -import { SkipButton } from "./react/components/shell/skipButton"; +import { getAppInsights } from './services/telemetryService'; +import TelemetryProvider from "./providers/telemetry/telemetryProvider"; import "./App.scss"; import "react-toastify/dist/ReactToastify.css"; @@ -47,11 +47,11 @@ function mapDispatchToProps(dispatch) { */ @connect(mapStateToProps, mapDispatchToProps) export default class App extends React.Component { + appInsights: any = null; constructor(props, context) { super(props, context); - this.state = { - currentProject: this.props.currentProject, + currentProject: this.props.currentProject, }; } @@ -73,28 +73,29 @@ export default class App extends React.Component { onError={this.props.actions.showError} onClearError={this.props.actions.clearError} /> {/* Don't render app contents during a render error */} - {strings.common.skipToMainContent} {(!this.props.appError || this.props.appError.errorCode !== ErrorCode.GenericRenderError) && -
- -
- -
-
- -
-
-
- - + { this.appInsights = getAppInsights() }}> +
+ +
+ +
+
+ +
+
+
+ + +
+ + + +
- - - - -
+ } diff --git a/src/common/constants.ts b/src/common/constants.ts index 9dad46d27..c0e950fd1 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -42,4 +42,5 @@ export const constants = { pdfjsCMapUrl(version: string) { return `https://fotts.azureedge.net/npm/pdfjs-dist/${version}/cmaps/`; }, + insightsKey: "dd15609c-c451-4e00-b193-abdf3e1d36e8", }; diff --git a/src/providers/telemetry/telemetryProvider.jsx b/src/providers/telemetry/telemetryProvider.jsx new file mode 100644 index 000000000..f1752eba1 --- /dev/null +++ b/src/providers/telemetry/telemetryProvider.jsx @@ -0,0 +1,40 @@ +import React, {Component, Fragment} from 'react'; +import {withAITracking} from '@microsoft/applicationinsights-react-js'; +import {ai} from '../../services/telemetryService'; +import {withRouter} from 'react-router-dom'; +import { constants } from '../../common/constants'; + +/** + * This Component provides telemetry with Azure App Insights + * + * NOTE: the package '@microsoft/applicationinsights-react-js' has a HOC withAITracking that requires this to be a Class Component rather than a Functional Component + */ +class TelemetryProvider extends Component { + state = { + initialized: false + }; + + componentDidMount() { + const {history} = this.props; + const { initialized } = this.state; + const AppInsightsInstrumentationKey = constants.insightsKey; // <-Paste your AppInsightsInstrumentationKey here + + if (!Boolean(initialized) && Boolean(AppInsightsInstrumentationKey) && Boolean(history)) { + ai.initialize(AppInsightsInstrumentationKey, history); + this.setState({initialized: true}); + } + + this.props.after(); + } + + render() { + const {children} = this.props; + return ( + + {children} + + ); + } +} + +export default withRouter(withAITracking(ai.reactPlugin, TelemetryProvider)); diff --git a/src/react/components/pages/modelCompose/composeModelView.tsx b/src/react/components/pages/modelCompose/composeModelView.tsx index 5956f8b8b..71c6d948b 100644 --- a/src/react/components/pages/modelCompose/composeModelView.tsx +++ b/src/react/components/pages/modelCompose/composeModelView.tsx @@ -6,6 +6,7 @@ import { Customizer, IColumn, ICustomizations, Modal, DetailsList, SelectionMode import { getDarkGreyTheme, getPrimaryGreenTheme, getPrimaryGreyTheme } from "../../../../common/themes"; import { strings } from "../../../../common/strings"; import { IModel } from "./modelCompose"; +import { getAppInsights } from '../../../../services/telemetryService'; export interface IComposeModelViewProps { @@ -23,6 +24,7 @@ export default class ComposeModelView extends React.Component { recentProjects: IProject[]; diff --git a/src/react/components/pages/predict/predictPage.tsx b/src/react/components/pages/predict/predictPage.tsx index c1a051ab7..56ee13d85 100644 --- a/src/react/components/pages/predict/predictPage.tsx +++ b/src/react/components/pages/predict/predictPage.tsx @@ -39,6 +39,7 @@ import { getPrimaryGreenTheme, getPrimaryWhiteTheme, import axios from "axios"; import { IAnalyzeModelInfo } from './predictResult'; import RecentModelsView from "./recentModelsView"; +import { getAppInsights } from '../../../../services/telemetryService'; pdfjsLib.GlobalWorkerOptions.workerSrc = constants.pdfjsWorkerSrc(pdfjsLib.version); const cMapUrl = constants.pdfjsCMapUrl(pdfjsLib.version); @@ -109,6 +110,8 @@ function mapDispatchToProps(dispatch) { @connect(mapStateToProps, mapDispatchToProps) export default class PredictPage extends React.Component { + private appInsights: any = null; + public state: IPredictPageState = { couldNotGetRecentModel: false, selectionIndexTracker: -1, @@ -149,6 +152,7 @@ export default class PredictPage extends React.Component { connections: IConnection[]; @@ -74,6 +75,7 @@ function mapDispatchToProps(dispatch) { @connect(mapStateToProps, mapDispatchToProps) export default class TrainPage extends React.Component { + private appInsights: any = null; constructor(props) { super(props); @@ -102,6 +104,7 @@ export default class TrainPage extends React.Component { diff --git a/src/services/telemetryService.ts b/src/services/telemetryService.ts new file mode 100644 index 000000000..7afe45a44 --- /dev/null +++ b/src/services/telemetryService.ts @@ -0,0 +1,51 @@ +import {ApplicationInsights} from '@microsoft/applicationinsights-web'; +import {ReactPlugin} from '@microsoft/applicationinsights-react-js'; + +let reactPlugin = null; +let appInsights = null; + +/** + * Create the App Insights Telemetry Service + * @return {{reactPlugin: ReactPlugin, appInsights: Object, initialize: Function}} - Object + */ +const createTelemetryService = () => { + + /** + * Initialize the Application Insights class + * @param {string} instrumentationKey - Application Insights Instrumentation Key + * @param {Object} browserHistory - client's browser history, supplied by the withRouter HOC + * @return {void} + */ + const initialize = (instrumentationKey: string, browserHistory: any): void => { + if (!browserHistory) { + throw new Error('Could not initialize Telemetry Service'); + } + if (!instrumentationKey) { + throw new Error('Telemetry Service Instrumentation key not provided.') + } + + reactPlugin = new ReactPlugin(); + + appInsights = new ApplicationInsights({ + config: { + instrumentationKey, + maxBatchInterval: 0, + disableAjaxTracking: true, + enableUnhandledPromiseRejectionTracking: true, + extensions: [reactPlugin], + extensionConfig: { + [reactPlugin.identifier]: { + history: browserHistory + } + } + } + }); + + appInsights.loadAppInsights(); + }; + + return {reactPlugin, appInsights, initialize}; +}; + +export const ai = createTelemetryService(); +export const getAppInsights = () => appInsights; diff --git a/yarn.lock b/yarn.lock index bba89d370..9a213e346 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1421,6 +1421,94 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@microsoft/applicationinsights-analytics-js@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.5.6.tgz#0a2862aa896eb0d467a626b332e2c65ef821ec87" + integrity sha512-ExNn0Z1Q5mfYqlKgsiXLXWkiSf2XaK5LlVJ9TJQjL2gv6EPXVjKU/6QaFH5ekBmKvvt2bsBK/I/nUUt2n3eiQg== + dependencies: + "@microsoft/applicationinsights-common" "2.5.6" + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + "@microsoft/dynamicproto-js" "^0.5.2" + +"@microsoft/applicationinsights-channel-js@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.5.6.tgz#ea97f6d0b4eaccddbdbca36287d5a1f7aff6dd9e" + integrity sha512-J0PKqkOwlS5jGX6LylQ6DPLrw6HAMb0bLbs5w9a+jNFXm3yB1RPtxhlaylZZG7BO+4KjfanX2PdBesE2wOtLcw== + dependencies: + "@microsoft/applicationinsights-common" "2.5.6" + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + +"@microsoft/applicationinsights-common@2.5.6", "@microsoft/applicationinsights-common@^2.5.4": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.5.6.tgz#ce93e719ccdbfb264c1f159ef1809762eda9447d" + integrity sha512-ISrUcIHii2WbEG9diOqlJSM42t5qvHCfWCyatQUwJTW3fS+JDS4Kbq4vBEeMHRW5ka2SFSkO40uMwcZxL0SoOw== + dependencies: + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + +"@microsoft/applicationinsights-core-js@2.5.6", "@microsoft/applicationinsights-core-js@^2.5.4": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.5.6.tgz#c18e76c65d1478599f3323d798375e543ee2a421" + integrity sha512-KDepyGJIMc0CpFw5GVVwG9MYl8ztDGdVpCFPaY+hrYN8bkzSb2vHjT/jV8pefcSkmrDOK8THqm2nTYnPo127ag== + dependencies: + "@microsoft/applicationinsights-shims" "1.0.1" + "@microsoft/dynamicproto-js" "^0.5.2" + +"@microsoft/applicationinsights-dependencies-js@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-dependencies-js/-/applicationinsights-dependencies-js-2.5.6.tgz#4e8aadad85f70238e7c4b5abec89addb8d8363a9" + integrity sha512-Ld9OzIczRssZLoyuah2Lgp7O6tfgb/IuA1av4ZRKP9Ki5/4iyrC9dqRViM5P0rKHv5nOk9hghoVaYc+OCQn30g== + dependencies: + "@microsoft/applicationinsights-common" "2.5.6" + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + "@microsoft/dynamicproto-js" "^0.5.2" + +"@microsoft/applicationinsights-properties-js@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-properties-js/-/applicationinsights-properties-js-2.5.6.tgz#4de2a150c0deb6f0746c908cecb7f888ced798b2" + integrity sha512-tn9PzxUkomhSfO6vPeCNIjcCysmg4xRkntkkXconUbfZoYmrrh0jK11cmW1E3c7U5Pb8RyUqvPyGaBVOIG+QQw== + dependencies: + "@microsoft/applicationinsights-common" "2.5.6" + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + +"@microsoft/applicationinsights-react-js@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-react-js/-/applicationinsights-react-js-3.0.1.tgz#d13653d3d3e52c8187b056be4589f21937747ee2" + integrity sha512-x6IkcSsm7hjHwlIDJ4pyMTmCW4z9OrS7WwPZfJ/y2v5pXcsRttgZIbxMVlpp+2GV9lS41+bfonmyf/o5pdFCvA== + dependencies: + "@microsoft/applicationinsights-common" "^2.5.4" + "@microsoft/applicationinsights-core-js" "^2.5.4" + "@microsoft/applicationinsights-shims" "^1.0.1" + history "^4.10.1" + +"@microsoft/applicationinsights-shims@1.0.1", "@microsoft/applicationinsights-shims@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-1.0.1.tgz#814ae118940a892052ea0fc3677e60dc4f0dc374" + integrity sha512-nPjUBSpvX5Dnkovp2lZzrg0/uSvYNtbAclWwVP7t8J1hy5OJ3xr3KPNaz79+b84G16Rj861ybau9Gbk7inXkTg== + +"@microsoft/applicationinsights-web@^2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web/-/applicationinsights-web-2.5.6.tgz#3e15c514229a2b6c19a36cb6ad5d1d270e0dac5e" + integrity sha512-o5DlixrdFf2FAOv9AOgaN9u1zhVyYhbWoeXDZj9eaOdDgQQq9FzHGLwPUUhqMc/cQN+k2I2PDELnjqHag0VLKQ== + dependencies: + "@microsoft/applicationinsights-analytics-js" "2.5.6" + "@microsoft/applicationinsights-channel-js" "2.5.6" + "@microsoft/applicationinsights-common" "2.5.6" + "@microsoft/applicationinsights-core-js" "2.5.6" + "@microsoft/applicationinsights-dependencies-js" "2.5.6" + "@microsoft/applicationinsights-properties-js" "2.5.6" + "@microsoft/applicationinsights-shims" "1.0.1" + "@microsoft/dynamicproto-js" "^0.5.2" + +"@microsoft/dynamicproto-js@^0.5.2": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-0.5.3.tgz#fd7933c22dd646d20f37176406eda6042dfb7efc" + integrity sha512-ky1oQ030kQlj/fv1+NLmY/g4D6YJb55Yg22KAoZGixFnc7UMjHJHjqZ0v3gtdyZykan3ZxRwwc2n1nWayyM38g== + "@microsoft/load-themed-styles@^1.10.26": version "1.10.55" resolved "https://registry.yarnpkg.com/@microsoft/load-themed-styles/-/load-themed-styles-1.10.55.tgz#f48b0112db8f409dc7cc37674587be59d3992ec4" @@ -6156,7 +6244,7 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -history@^4.9.0: +history@^4.10.1, history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==