normalian blog

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

AppInsights と Power BI を使って Java Web アプリの例外発生をリアルタイム監視する

前回の記事である Application Insights と Power BI を使って Java の Web アプリの PageView を監視する にて、Application Insights の連続エクスポート/Stream Analytics の Power BI 出力を活用して PageView のログが簡単に可視化できることが分かった。
今回は Application Insights に例外ログを出力して Power BI に可視化するまでの手順を紹介する。なお、Application Insights の連続エクスポート、Stream Analytics の設定、Power BI 設定の細かな手順については前回の記事を参照してほしい。

Java アプリケーションの記載

以下の様に入力文字列が空の場合に IllegalArgumentException を出力するコードを作成する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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 instrumentation key>"
        });
        
        window.appInsights=appInsights;
        appInsights.trackPageView();
    </script>
<title>AppInsights-site index page</title>
</head>
<body>
	<h2>ポストしてみる</h2>
	<form method="post" action="<%=request.getContextPath()%>/home">
		文字列: <input name="message" type="text" placeholder="文字列を入力してください" />
		<br /> <input type="submit" value="送信 " />
	</form>
</body>
</html>
package com.mydomain;

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;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.microsoft.applicationinsights.TelemetryClient;

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

	private static final Logger logger = LogManager.getLogger(HomeServlet.class);

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public HomeServlet() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		TelemetryClient telemetryClient = new TelemetryClient();

		String message = request.getParameter("message");
		if (message == null || message.length() == 0) {
			IllegalArgumentException ex = new IllegalArgumentException("please input message");
			telemetryClient.trackException(ex);
			logger.warn("input warning");
		}
		RequestDispatcher dispatch = request.getRequestDispatcher("/second.jsp");
		dispatch.forward(request, response);
	}
}

非常にシンプルなコードなので、特に解説は不要だと思う。

Stream Analytics を設定する

前回の記事を参考にインプットに BLOB ストレージ、アウトプットに Power BI を指定する。特に Stream Analytics のインプットのパターンは以下となる。

  • プレフィックスのパターン: forpowerbiappinsights_/Exceptions/{date}/{time}
  • 日付形式: YYYY-MM-DD
  • 時刻形式: HH

そんな BLOB の格納される Exception のログは以下になる。

{  
   "basicException":[  
      {  
         "assembly":"Unknown",
         "exceptionType":"java.lang.IllegalArgumentException",
         "outerExceptionType":"java.lang.IllegalArgumentException",
         "outerExceptionThrownAtMethod":"com.mydomain.HomeServlet.doPost",
         "outerExceptionThrownAtAssembly":"",
         "failedUserCodeMethod":"com.mydomain.HomeServlet.doPost",
         "failedUserCodeAssembly":"",
         "exceptionGroup":"java.lang.IllegalArgumentException at com.mydomain.HomeServlet.doPost",
         "id":"Representative",
         "typeName":"java.lang.IllegalArgumentException",
         "handledAt":"UserCode",
         "count":1,
         "method":"com.mydomain.HomeServlet.doPost",
         "problemId":"",
         "outerExceptionMessage":"please input message"
      },
      {  
         "parsedStack":[  
            {  
               "method":"com.mydomain.HomeServlet.doPost",
               "fileName":"HomeServlet.java",
               "level":0,
               "line":78
            },
            {  
               "method":"javax.servlet.http.HttpServlet.service",
               "fileName":"HttpServlet.java",
               "level":1,
               "line":648
            },
            <中略>

上記の "basicException" 内にある "exceptionType" を取得するクエリは以下となる。"parsedStack" 部分をスキップしないと空白の count(*) が出力されるため、HAVING 句で "basicException" の無い項目はスキップしている。

    SELECT
      flat.ArrayValue.exceptionType,
      count(*)
    INTO
      [pbi-exception-output]
    FROM
      [export-input] A
    OUTER APPLY GetElements(A.[basicException]) as flat
    GROUP BY TumblingWindow(minute, 1), flat.ArrayValue.exceptionType
    HAVING LEN(flat.ArrayValue.exceptionType) > 0

Power BI でのリアルタイム表示

Power BI にログインし、Stream Analytics のアウトプットで指定したデータセットが表示されていることを確認する。ポータルから表示方法を設定し、"以下の様に ダッシュボード" にレポートを作成する*1
f:id:waritohutsu:20151012215131p:plain

体感値として、1分弱の時間で Application Insights の連続エクスポートが永続ログを吐き出し、2分~3分で Power BI 側に情報が反映される様だ。

*1:ダッシュボードに保存しないと、リアルタイムでの更新が確認できない