normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

Application Insights と PowerBI を使って Java の Web アプリの PageView を監視する

半年ほど前に 割と普通なブログ > Application Insight を Java アプリケーションから利用する で紹介させて頂いた Java の Application Insights の利用方法だが、以下のように BLOB ストレージに永続ログとしてデータを出力できることをご存じだろうか。

連続エクスポートは Application Insights の標準以上の有料プランで利用する必要があるが、以下の様に "30日間のプレミアム試用版"という期間限定でお試しで無料で利用できるプランがある。
f:id:waritohutsu:20150930012936p:plainf:id:waritohutsu:20150930012946p:plain

今回はこちらを利用して Application Insights のログを Power BI の可視化までを無料で行ってみる。

連続エクスポートの設定

管理ポータルから Application Insights の設定タブより、宛先のストレージサービスを選択してログの出力先を指定する。
f:id:waritohutsu:20150930012954p:plain

この際、以下の様にエクスポートするデータの種類を選択できる。
f:id:waritohutsu:20150930013007p:plain

Java アプリケーションの作成

JSP ファイルが 3 個、Servlet が一つのシンプルな構成とする。特に解説はしないが、画面とサーブレットの関係が inde.jsp <- HomeServlet.jsp -> second.jsp かつ、third.jsp が独立している構成なことが理解いただけると思う。また、Application Insights へログ出力が必要な instrumentationKey 値は管理ポータルから取得すること。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mydomain</groupId>
	<artifactId>AppInsightWebApp</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>AppInsightWebApp Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.microsoft.azure</groupId>
			<artifactId>applicationinsights-web</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>com.microsoft.azure</groupId>
			<artifactId>applicationinsights-core</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>com.microsoft.azure</groupId>
			<artifactId>applicationinsights-logging-log4j2</artifactId>
			<version>1.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-api</artifactId>
			<version>2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.3</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.0.1</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>AppInsightWebApp</finalName>
	</build>
</project>
package com.mydomain;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/home")
public class HomeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		String nextPage = request.getParameter("next");
		if (nextPage == null) {
			nextPage = "index.jsp";
		}

		RequestDispatcher dispatch = request.getRequestDispatcher("/"
				+ nextPage);
		dispatch.forward(request, response);
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	}
}
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<script type="text/javascript">
        var appInsights=window.appInsights||function(config){
            function r(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s=u.createElement(o),i,f;for(s.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)r("track"+i.pop());return r("setAuthenticatedUserContext"),r("clearAuthenticatedUserContext"),config.disableExceptionTracking||(i="onerror",r("_"+i),f=e[i],e[i]=function(config,r,u,e,o){var s=f&&f(config,r,u,e,o);return s!==!0&&t["_"+i](config,r,u,e,o),s}),t
        }({
            instrumentationKey:"<my instrumentationKey>"
        });
        
        window.appInsights=appInsights;
        appInsights.trackPageView();
    </script>
<title>AppInsights-site index</title>
</head>
<body>
	<h2>Hello World! - index page</h2>
	<a href="<%=request.getContextPath()%>/home?next=second.jsp">next
		page</a>
</body>
</html>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<script type="text/javascript">
        var appInsights=window.appInsights||function(config){
            function r(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s=u.createElement(o),i,f;for(s.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)r("track"+i.pop());return r("setAuthenticatedUserContext"),r("clearAuthenticatedUserContext"),config.disableExceptionTracking||(i="onerror",r("_"+i),f=e[i],e[i]=function(config,r,u,e,o){var s=f&&f(config,r,u,e,o);return s!==!0&&t["_"+i](config,r,u,e,o),s}),t
        }({
            instrumentationKey:"<my instrumentationKey>"
        });
        
        window.appInsights=appInsights;
        appInsights.trackPageView();
    </script>
<title>AppInsights-site second</title>
</head>
<body>
	<h2>Hello World!- second page</h2>
	<a href="<%=request.getContextPath()%>/home?next=index.jsp">next
		page</a>
</body>
</html>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<script type="text/javascript">
        var appInsights=window.appInsights||function(config){
            function r(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s=u.createElement(o),i,f;for(s.src=config.url||"//az416426.vo.msecnd.net/scripts/a/ai.0.js",u.getElementsByTagName(o)[0].parentNode.appendChild(s),t.cookie=u.cookie,t.queue=[],i=["Event","Exception","Metric","PageView","Trace"];i.length;)r("track"+i.pop());return r("setAuthenticatedUserContext"),r("clearAuthenticatedUserContext"),config.disableExceptionTracking||(i="onerror",r("_"+i),f=e[i],e[i]=function(config,r,u,e,o){var s=f&&f(config,r,u,e,o);return s!==!0&&t["_"+i](config,r,u,e,o),s}),t
        }({
            instrumentationKey:"<my instrumentationKey>"
        });
        
        window.appInsights=appInsights;
        appInsights.trackPageView();
    </script>
<title>AppInsights-site third@@@@@@</title>
</head>
<body>
	<h2>Hello World!- third page</h2>
</body>
</html>

次に、上記のアプリケーションを作成後、Tomcat 等で Web アプリケーションを起動する。

ログ出力の確認

起動した Web アプリケーションにブラウザ経由でアクセスすると、Application Insights へのログ出力が成功していれば以下のように管理ポータルからログが閲覧可能だ。
f:id:waritohutsu:20150930013020p:plain

更に、連続エクスポートが正常に行われている場合、blob container に以下の様にファイルが出力されているはずだ。今回は このうちの PageViews をターゲットとする。
f:id:waritohutsu:20150930013030p:plain

Stream Analytics でログを整形し、Power BI に出力する

Application Insights データの Power BI ビュー を参照いただければ問題ないと思うが、Stream Analytics の新規インスタンスを作成して以下の設定を行う。

  • 入力として BLOB ストレージを選択
  • 出力先として Power BI を選択
  • Stream Analytics のクエリを作成

上記で Stream Analytics の入出力設定は特に問題ないと思うが、Stream Analytics のクエリがドキュメントではうまく動かなかった( A.[view] 部分が間違っていた程度だが )ので以下を参考にしてほしい。

    SELECT
      flat.ArrayValue.name,
      count(*)
    INTO
      [pbi-output]
    FROM
      [export-input] A
    OUTER APPLY GetElements(A.[view]) as flat
    GROUP BY TumblingWindow(minute, 1), flat.ArrayValue.name

上記のクエリは以下の様な形式で BLOB に格納されているデータは以下のような URL でファイルを作成されログ出力される。

  • https://<アカウント名>.blob.core.windows.net/appinsightslog/forpowerbiappinsights_581bb4f68f56476c9c3ce13eca62c0f4/PageViews/2015-09-29/10/c565d13c-b5f9-42f4-b9eb-a82e05fa6695_20150929_100813.blob

ファイルの中身は以下となるが、上記のクエリは「"name": "AppInsights-site index"」における値を抜き出すクエリになっている。詳細は参考のリンク先における Stream Analytics のクエリを参考にしてほしい。

{
  "view": [
    {
      "urlData": {
        "port": 8080,
        "host": "localhost",
        "protocol": "http",
        "base": "/AppInsightWebApp/",
        "queryParameters": [ ],
        "hashTag": ""
      },
      "name": "AppInsights-site index",
      "count": 1,
      "durationMetric": {
        "value": 5980000.0,
        "count": 1.0,
        "sampledValue": 5980000.0
      },
      "url": "http://localhost:8080/AppInsightWebApp/"
    }
  ],
  "internal": {
    "data": {
      "id": "<my id>",
      "documentVersion": "1.6"
    }
  },
  "context": {
    "device": {
  <中略>

Power BI で閲覧

次に powerbi.microsoft.com へアクセスし、Stream Analytics の出力で設定した宛先を確認する。正常に Stream Analytics が動作していれば、以下の画面の様に「Stream Analytics の出力で設定した宛先」の箇所が追加されているので、こちらを操作して任意のグラフを作成できる。
f:id:waritohutsu:20150930015927p:plain

上記のように、Application Insights と Power BI を利用することで、容易に画面毎のアクセス数が確認できるビューを作成できることが分かった。もちろん Stream Analytics のクエリを変更することで任意の情報を Power BI 側に表示することが可能になる。