{"id":4757,"date":"2017-09-26T08:58:02","date_gmt":"2017-09-26T15:58:02","guid":{"rendered":"https:\/\/www.microsoft.com\/reallifecode\/?p=4757"},"modified":"2020-03-18T10:59:00","modified_gmt":"2020-03-18T17:59:00","slug":"custom-analytics-dashboard-application-insights","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/custom-analytics-dashboard-application-insights\/","title":{"rendered":"Custom Analytics Dashboard for Application Insights"},"content":{"rendered":"<p>Recently, we worked with multiple startups that use <span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\"><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/application-insights\/\">Application Insights<\/a>,\u00a0an Azure service that enables the collection of large amounts of information about an application&#8217;s business analytics and health, plus quick and efficient access to that data by using <a href=\"https:\/\/kusto.azurewebsites.net\/docs\/\">Kusto Query Language<\/a>. During these engagements, we identified the need for an easy-to-use tool for visualizing the service&#8217;s data.\u00a0While building a <a href=\"https:\/\/dev.botframework.com\/\">Microsoft Bot Framework<\/a> application called <a href=\"https:\/\/hellopegg.io\/\">Pegg<\/a> with <a href=\"http:\/\/sageone.com\">Sage<\/a>, we learned the customer wanted to understand what was happening in their production environment after deploying their bots. We soon discovered that many partners, such as <a href=\"https:\/\/www.sbb.ch\/en\/home.html\">Swiss Railways<\/a>\u00a0(read about their bot application <a href=\"https:\/\/company.sbb.ch\/de\/medien\/medienstelle\/medienmitteilungen\/detail.html\/2017\/5\/2305-2\">here<\/a>), and other companies and start-ups had the same requirement, which is how this project began.<\/span><\/p>\n<p><span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\">We set out to build a solution that supports various scenarios by implementing the following features on a <strong>dashboard framework<\/strong>:<\/span><\/p>\n<ul>\n<li>Filtering<\/li>\n<li>D<span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\">rill-down<\/span><span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\"> and expanding data<\/span><\/li>\n<li>Reusable T<span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\">emplates (to enable sharing of schema)<\/span><\/li>\n<li>Extendability<span style=\"margin: 0px; color: #262626; font-family: 'Arial',sans-serif; font-size: 13pt;\"> (for visual components and data sources)<\/span><\/li>\n<li>Customization<\/li>\n<\/ul>\n<p>Our solution for visualizing Azure Application Insights data can be applied to different bot scenarios, as well as various other scenarios involving the service.<!--more--><\/p>\n<h2>The Challenge<\/h2>\n<p>Application Insights run on a large, powerful data engine called Kusto, which enables storage and retrieval of large quantities of analytics data in real-time. The customer required a dashboard solution with a unified approach for bot analytics and, potentially, other application types. The dashboard used today in\u00a0<a href=\"https:\/\/devblogs.microsoft.com\/bharry\/introducing-application-analytics\/\">Application Insights Analytics<\/a> is the Azure Portal native dashboard, which doesn&#8217;t support all of the features we needed (such as customization, reusability, and extensibility to support additional data sources).<\/p>\n<h2>The Solution<\/h2>\n<p style=\"margin-bottom: .25in; background: white;\"><span style=\"font-size: 13.0pt; font-family: 'Arial',sans-serif; color: #222222;\">We collected requirements from several companies and startups we were working with that had developed a bot application and wanted a Bot Analytics Dashboard to understand their applications&#8217; usage statistics and health in production.\u00a0<\/span><\/p>\n<h3>Functional Features<\/h3>\n<p>The following is a list of the prioritized requirements.<\/p>\n<ul>\n<li><strong>Reusable\u00a0templates:\u00a0<\/strong>Enable customers\u00a0to share templates between environments, similar applications, and on a larger scale. Define templates that are reusable between different parties on similar application types.<\/li>\n<li><strong style=\"font-size: 1.0625rem;\">Filtering:\u00a0<\/strong><span style=\"font-size: 1.0625rem;\">Simple drop-down lists to select how to filter the data according to time or any other field specific to the application.<\/span><\/li>\n<li><strong style=\"font-size: 1.0625rem;\">Drill-down:\u00a0<\/strong><span style=\"font-size: 1.0625rem;\">Ability to click on a data point (like bar, pie slice, scorecard, etc.) and get an extended dashboard focused on that entity. For example, clicking on an Intent in an Intents Usage <\/span>chart <span style=\"font-size: 1.0625rem;\">would open up a dashboard with a list of top entities in that intent.<\/span><\/li>\n<li><strong style=\"font-size: 1.0625rem;\">Customizability:\u00a0<\/strong><span style=\"font-size: 1.0625rem;\">Enable customers to continue and customize a dashboard after creating it from a template as each <\/span>customer\/application<span style=\"font-size: 1.0625rem;\"> requires additional specifications.<\/span><\/li>\n<\/ul>\n<h3>Experience<\/h3>\n<p>The following is a list of requirements from a user experience perspective:<\/p>\n<ul>\n<li><strong>Simplicity:\u00a0<\/strong>Offer a simple and intuitive experience for ramping-up, using and manipulating the dashboards\/templates and a continuous deployment experience.<\/li>\n<li><strong>Holistic experience:<\/strong>\u00a0Have one configuration to define data source queries, the dependencies between those queries, and the way to visualize them.<\/li>\n<li><strong>Data source connectability: <\/strong>Enable users\u00a0to withdraw and join data from multiple data sources (i.e., join LOB data from <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/cosmos-db\/introduction\">CosmosDB<\/a> with analytics data from Application Insights).<\/li>\n<li><strong>Chart and data source\u00a0<\/strong><b>extendability:<\/b>\u00a0Customers can add their own data source and chart types.<\/li>\n<\/ul>\n<p style=\"margin-bottom: .25in; background: white;\"><span style=\"font-size: 13.0pt; font-family: 'Arial',sans-serif; color: #222222;\">We looked into several popular and active dashboard frameworks which didn\u2019t fit our requirements. The options we found were lacking in <\/span><span style=\"font-size: 13.0pt; font-family: 'Arial',sans-serif; color: #222222;\">user and developer experience requirements, such as reusable templates, editing, drill-down, filtering and customization. These gaps would have required substantial development resources and compromise on important features. In order to focus on our primary requirements, we dismissed some our less critical ones and were able to put a relatively small effort into developing a new platform.<\/span><\/p>\n<p><strong>Ibex<\/strong>, which is the name we chose for our project, is open sourced and <a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\">available on GitHub<\/a>. Its namesake is an animal native to Israel, plus its horns reminded us of line charts.<\/p>\n<h3>Solution Architecture<\/h3>\n<p><img decoding=\"async\" class=\"wp-image-4981 size-full aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/Ibex-Architecture-diagram-1.png\" alt=\"Solution Architecture\" width=\"1304\" height=\"1587\" \/><\/p>\n<h3>Data Source Management<\/h3>\n<p>First, we load the dashboard configuration from the server API. Since the dashboard contains JavaScript functions for calculations and transformations on data, it is a JavaScript file. That file is loaded into a\u00a0 tag on the client side, executed, and stored as a global &#8216;dashboard&#8217; variable.<\/p>\n<p><strong><em>ConfigurationActions.ts<\/em><\/strong><\/p>\n<pre class=\"ts\">  loadDashboard(id: string) {\r\n    \r\n    return (dispatcher) =&gt; {\r\n      getScript('\/api\/dashboards\/' + id, () =&gt; {\r\n\r\n        return dispatcher({ dashboard: (window as any)['dashboard'] as IDashboardConfig });\r\n\r\n      });\r\n    };\r\n  }\r\n\r\n   getScript(source: string, callback) {\r\n    let script: any = document.createElement('script');\r\n    script.async = 1;\r\n    document.getElementsByTagName('body')[0].appendChild(script);\r\n    \r\n    script.onload = script.onreadystatechange = (_, isAbort) =&gt; callback;\r\n\r\n    script.src = source;\r\n  }\r\n<\/pre>\n<p>After loading the dashboard, the data sources are initialized via the\u00a0configuration store.<\/p>\n<p><em><strong>ConfigurationStore.ts<\/strong><\/em><\/p>\n<pre class=\"ts\">  loadDashboard(result: { dashboard: IDashboardConfig }) {\r\n    let { dashboard } = result;\r\n    DataSourceConnector.createDataSources();\r\n  }\r\n<\/pre>\n<p>Then we dynamically load the right data source plugin to handle the configuration for each data source in the dashboard.<\/p>\n<p><em><strong>DataSourceConnector.ts<\/strong><\/em><\/p>\n<pre class=\"ts\">  createDataSources(dashboard: IDataSourceContainer) {\r\n    dashboard.dataSources.forEach(source =&gt; {\r\n      let dataSource = DataSourceConnector.createDataSource(source);\r\n      DataSourceConnector.connectDataSource(dataSource);\r\n    });\r\n  }\r\n\r\n  createDataSource(config: any) {\r\n\r\n    var PluginClass = require('.\/plugins\/' + config.type);\r\n    var plugin: any = new PluginClass.default(config);\r\n\r\n    var ActionClass = DataSourceConnector.createActionClass(plugin);\r\n\r\n    var StoreClass = DataSourceConnector.createStoreClass(config, plugin, ActionClass);\r\n\r\n    DataSourceConnector.dataSources[config.id] = {\r\n      id: config.id,\r\n      config,\r\n      plugin,\r\n      action: ActionClass,\r\n      store: StoreClass,\r\n      initialized: false\r\n    };\r\n\r\n    return DataSourceConnector.dataSources[config.id];\r\n  }\r\n<\/pre>\n<p>When we create the data source plugin, we also dynamically create an <a href=\"http:\/\/alt.js.org\/\">Alt.js<\/a>\u00a0<strong>store<\/strong> and <strong>action<\/strong>\u00a0that handle the state management for each data source, as well as actions and events triggered on each data source (like\u00a0<em>refresh<\/em>, <em>updateDependencies<\/em>, etc.).<\/p>\n<pre class=\"ts\">  createActionClass(plugin: IDataSourcePlugin): any {\r\n    class NewActionClass {\r\n      constructor() { }\r\n    }\r\n\r\n    plugin.getActions().forEach(action =&gt; alt.addActions(action,  NewActionClass));\r\n\r\n    return alt.createActions(NewActionClass as any);\r\n  }\r\n\r\n  createStoreClass(config: any, plugin: any, ActionClass: any): any {\r\n\r\n    class NewStoreClass {\r\n      constructor() {\r\n        this.bindListeners({ updateState: plugin.getActions().map(action =&gt; ActionClass[action]) });\r\n      }\r\n\r\n      updateState(newData: any) {\r\n        this.setState(newData);\r\n      }\r\n    }\r\n    return alt.createStore(NewStoreClass as any, config.id + '-Store');\r\n  }\r\n\r\n<\/pre>\n<p>Finally, whenever a data source is updated, we look for any data sources that have a dependency on it and trigger an <em><strong>updateDependencies<\/strong><\/em> action.<\/p>\n<pre class=\"ts\">  connectDataSource(sourceDS: IDataSource) {\r\n    sourceDS.store.listen((state) =&gt; {\r\n      this.dataSources.forEach(checkDS =&gt; {\r\n\r\n        let dependencies = checkDS.plugin.getDependencies();\r\n        if (areDependenciesConnected(sourceDS, dependencies)) {\r\n\r\n          checkDS.action.updateDependencies.defer(getDependencyValues(sourceDS, dependencies));\r\n        }\r\n      });\r\n    })\r\n<\/pre>\n<h3>Visual Components Management<\/h3>\n<p style=\"direction: ltr;\">Visual components (a.k.a. Elements) are first requested and loaded via the Dashboard component.<\/p>\n<p><em><strong>Dashboard\/index.tsx<\/strong><\/em><\/p>\n<pre class=\"tsx\">class Dashboard extends React.Component {\r\n  render() {\r\n    const { dashboard } = this.props;\r\n\r\n    let elements = ElementConnector.loadElementsFromDashboard(dashboard);\r\n    let filters = ElementConnector.loadFiltersFromDashboard(dashboard);\r\n    let dialogs = ElementConnector.loadDialogsFromDashboard(dashboard);\r\n\r\n    return (<\/pre>\n<div>\n<p>&nbsp;<\/p>\n<div class=\"toolbar\">{filters}<\/div>\n<p>{elements} {dialogs}<\/p>\n<\/div>\n<p>); } }<\/p>\n<p>We then use <em><strong>ElementConnector<\/strong><\/em>\u00a0to dynamically create elements.<\/p>\n<p><em><strong>ElementConnector.ts<\/strong><\/em><\/p>\n<pre class=\"tsx\">  loadElementsFromDashboard(dashboard: IElementsContainer): React.Component[] {\r\n    return dashboard.elements.map((element, idx) =&gt; {\r\n      var ReactElement = plugins[element.type];\r\n      var { id, dependencies, source, actions, props, title, subtitle, size, theme, location } = element;\r\n      var layoutProps = _.find(layout, { 'i': id });\r\n\r\n      return (\r\n        \r\n      );\r\n    });\r\n  }\r\n<\/pre>\n<p>The class <em><strong>ReactElement<\/strong><\/em>\u00a0is always a class that inherits from\u00a0<em><strong>GenericComponent.\u00a0<\/strong><\/em>This structure has the following implementation for <em><strong>componentDidMount<\/strong><\/em>, which makes sure each change in the state of a store that is defined as a dependency will trigger a correlating state change in the Element itself.<\/p>\n<p><em><strong>GenericComponent.tsx<\/strong><\/em><\/p>\n<pre class=\"tsx\">abstract class GenericComponent extends React.Component {\r\n  constructor(props: T1) {\r\n    var initialState: IGenericState = {};\r\n    var result = DataSourceConnector.extrapolateDependencies(this.props.dependencies);\r\n    Object.keys(result.dependencies).forEach(key =&gt; {\r\n      initialState[key] = result.dependencies[key];\r\n    });\r\n\r\n    this.state = initialState as any;\r\n  }\r\n\r\n  componentDidMount() {\r\n    var result = DataSourceConnector.extrapolateDependencies(this.props.dependencies);\r\n    Object.keys(result.dataSources).forEach(key =&gt; {\r\n\r\n      this.onStateChange(result.dataSources[key].store.state);\r\n      result.dataSources[key].store.listen(this.onStateChange);\r\n    });\r\n  }\r\n\r\n  onStateChange(state: any) {\r\n    var result = DataSourceConnector.extrapolateDependencies(this.props.dependencies);\r\n    var updatedState: IGenericState = {};\r\n    Object.keys(result.dependencies).forEach(key =&gt; { updatedState[key] = result.dependencies[key]; });\r\n\r\n    this.setState(updatedState);\r\n  }\r\n}\r\n<\/pre>\n<p>For more information on plugins for data sources and elements, follow these links:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/docs\/add-new-data-source.md\">Adding a new Data Source to Ibex Dashboard<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/docs\/add-new-element.md\">Adding a Visual Element<\/a><\/li>\n<\/ul>\n<h3>Process Flow<\/h3>\n<p>The diagram below is a representation of how data flows between the different components on the client side:<\/p>\n<p><img decoding=\"async\" class=\"wp-image-5049 size-full alignnone\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/Ibex-Architecture-process-2.png\" alt=\"\" width=\"962\" height=\"598\" \/><\/p>\n<p>&nbsp;<\/p>\n<h2>Reuse Opportunities<\/h2>\n<p>The first dashboard templates developed with Ibex involved bot scenarios and Microsoft Bot Framework, but we quickly understood the need for similar analytics solutions in various other scenarios like Web Applications, Gaming Analytics, Blockchain Analytics, etc.<\/p>\n<p>Due to similar requirements in these other use cases, we developed a framework that can be used for any scenario that:<\/p>\n<ul>\n<li>Uses Application Insights (and\/or other data sources that are queryable in real-time)<\/li>\n<li>Needs to visualize data by querying a data source in real-time<\/li>\n<li>Doesn&#8217;t require caching<\/li>\n<li>Needs to visualize drill-down into data<\/li>\n<li>Requires the use\/creation of a dashboard template to support a common scenario<\/li>\n<\/ul>\n<p>Below are some example screenshots of Bot Analytics solutions.<\/p>\n<p><figure id=\"attachment_4982\" aria-labelledby=\"figcaption_attachment_4982\" class=\"wp-caption alignleft\" ><img decoding=\"async\" class=\"wp-image-4982 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/BotAnalyticsDashboard.png\" alt=\"Bot Analytics Instrumented Dashboard\" width=\"1229\" height=\"614\" \/><figcaption id=\"figcaption_attachment_4982\" class=\"wp-caption-text\">Bot Analytics Instrumented Dashboard<\/figcaption><\/figure><\/p>\n<p>&nbsp;<\/p>\n<p><figure id=\"attachment_4984\" aria-labelledby=\"figcaption_attachment_4984\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" class=\"wp-image-4984 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/bot-fmk-dashboard-intent.png\" alt=\"Bot Analytics Instrumented Dashboard: Drill-Down\" width=\"2560\" height=\"1232\" \/><figcaption id=\"figcaption_attachment_4984\" class=\"wp-caption-text\">Bot Analytics Instrumented Dashboard: Drill-Down<\/figcaption><\/figure><\/p>\n<p>&nbsp;<\/p>\n<p><figure id=\"attachment_4370\" aria-labelledby=\"figcaption_attachment_4370\" class=\"wp-caption alignnone\" ><img decoding=\"async\" class=\"wp-image-4370 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/handoff-page.png\" alt=\"Bot to Human Handoff\" width=\"1356\" height=\"822\" \/><figcaption id=\"figcaption_attachment_4370\" class=\"wp-caption-text\">Bot to Human Handoff (see <a href=\"https:\/\/www.microsoft.com\/developerblog\/2017\/06\/30\/bot-to-human-handover-in-node-js\/\">related code story<\/a>)<\/figcaption><\/figure><\/p>\n<p>&nbsp;<\/p>\n<p><figure id=\"attachment_4983\" aria-labelledby=\"figcaption_attachment_4983\" class=\"wp-caption alignnone\" ><img decoding=\"async\" class=\"wp-image-4983 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/QnA.png\" alt=\"Q&amp;A Dashboard\" width=\"1219\" height=\"597\" \/><figcaption id=\"figcaption_attachment_4983\" class=\"wp-caption-text\">Q&amp;A Dashboard<\/figcaption><\/figure><\/p>\n<h3>\u00a0Bot Analytics Explained<\/h3>\n<p>In a future post, we will demonstrate how the Ibex dashboard can be extended to provide an end-to-end Bot Analytics Dashboard.<\/p>\n<h3>Application Insights Web Application Dashboard<\/h3>\n<p>Another scenario where we implemented a dashboard was for <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/app-service\/\">App Service<\/a>\u00a0(Web App) on <strong>Azure<\/strong>.<\/p>\n<p><figure id=\"attachment_4986\" aria-labelledby=\"figcaption_attachment_4986\" class=\"wp-caption alignleft\" ><img decoding=\"async\" class=\"wp-image-4986 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/web-apps.png\" alt=\"App Service\u00a0(Web App) on Azure Dashboard\" width=\"1920\" height=\"903\" \/><figcaption id=\"figcaption_attachment_4986\" class=\"wp-caption-text\">App Service\u00a0(Web App) on Azure Dashboard<\/figcaption><\/figure><\/p>\n<p>This dashboard connects to the native Web App schema which is logged into Application Insights.<\/p>\n<h2>Conclusion<\/h2>\n<p>Ibex is one of the alternatives offered today for a dashboard framework, and its key differentiation is a focus on drill-down, reusable\u00a0templates, and holistic configuration. While it might take a little more effort to build an Ibex dashboard, the flexibility for drill-downs makes it easy to manipulate, and once you create a template sharing it is also simple.<\/p>\n<p>One of the main development efforts remaining for this project is user customization. Currently, you can customize dashboards online by editing their structure (see image below). While this approach is of great value for developers, it is not suitable for a general user, for whom a more intuitive approach is required (a drag-and-drop system, and a collection of input fields that control the various parameters).<\/p>\n<p><img decoding=\"async\" class=\"size-full wp-image-5116 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2020\/03\/Untitled-1.png\" alt=\"\" width=\"2538\" height=\"1240\" \/><\/p>\n<h2>Contribution and Development<\/h2>\n<p>Please refer to the <a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/docs\/README.md\">contribution and development guide<\/a>\u00a0for this project, which\u00a0includes a detailed list of features and documentation of all development aspects.<\/p>\n<h3>Sample Templates<\/h3>\n<p>To better understand the templates we built the following sample templates:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/server\/dashboards\/preconfigured\/sample.ts\"><strong>Basic Sample<\/strong><\/a>, a\u00a0very simple dashboard with a sample data source connected to visual elements<\/li>\n<li>\n<div><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/server\/dashboards\/preconfigured\/sample-with-filter.ts\"><strong>Basic Sample with Filters<\/strong><\/a>, the same as Basic Sample, but with a filter applied to the data source<\/div>\n<\/li>\n<li>\n<div><span style=\"font-size: 1.0625rem;\"><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/server\/dashboards\/preconfigured\/sample-with-dialog.ts\"><strong>Basic Sample with Dialog<\/strong><\/a>, the same as Basic Sample, but with a dialog displayed on entity clicking<\/span><\/div>\n<\/li>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\/blob\/master\/server\/dashboards\/preconfigured\/application-insights-sample.ts\"><strong>Application Insights Sample<\/strong><\/a>, a dashboard with a simple connection to Application Insights<\/li>\n<\/ul>\n<h3>Code<\/h3>\n<ul>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/ibex-dashboard\">Ibex on GitHub<\/a><\/li>\n<li><a href=\"https:\/\/channel9.msdn.com\/events\/Build\/2017\/P4160\">Early edition presentation on Build 2017<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>We created an easy-to-use tool for visualizing data from Microsoft Azure Application Insights with a dashboard framework. Our solution can be applied to different bot scenarios as well as other scenarios involving the service. <\/p>\n","protected":false},"author":21371,"featured_media":11868,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[10,13],"tags":[41,64,197,249],"class_list":["post-4757","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-app-services","category-bots","tag-analytics","tag-azure-application-insights","tag-health","tag-microsoft-bot-framework-mbf"],"acf":[],"blog_post_summary":"<p>We created an easy-to-use tool for visualizing data from Microsoft Azure Application Insights with a dashboard framework. Our solution can be applied to different bot scenarios as well as other scenarios involving the service. <\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/4757","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21371"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=4757"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/4757\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/11868"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=4757"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=4757"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=4757"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}