読者です 読者をやめる 読者になる 読者になる

割と普通なブログ

Microsoft Azure や ASP.NET、Java EE 系の話題を記載します

Microsoft Azure の Machine Learning を利用して日経平均株価を予測してみる~その①まずは作ってみる~

Windows Azure

Microsoft Azure にて Machine Learning がリリースされてから大分経過したのはご存知の方も多いと思う。話題にあがることは多いので知識として知っている方は多いと思うが、実際に利用したことのあるかたは実は少ないのではないだろうか?
実際に手を動かす方が理解は容易であるため、最近 20,000 円台を回復しつつまた下がった日経平均株価を予測するサンプルの作成を通して Microsoft Azure の Machine Learning(以下、Azure ML)の利用方法を学習したいと思う。

念のための注意となるが、今回のサンプルの結果を見て株や金融商品の売買をすることは控えて頂きたい。機械学習自体は歴史も長く奥の深い分野であり、今回紹介する内容は「Azure ML の利用方法をサンプル作成から紹介する」ものであって「機械学習を利用して日経平均株価を高精度で予測する」ものではない。

作成するサンプルの概要

今回は 日経平均プロフィル-日経の指数公式サイト- に公開されている過去15年分の日経平均株価のデータを利用し、翌日の日経平均株価を予測するサンプルを作成する。

上記のデータを利用し、機械学習の Web サービスを作成するが、入力と結果は以下になる。

Azure ML の管理画面である Azure Machine Learning Studio からの動作イメージは以下になる。
f:id:waritohutsu:20151216163046p:plain

以下のうち、Scored Labels 列の値である 19053.57… が予測した日経平均株価の値となる。
f:id:waritohutsu:20151216163108p:plain

Azure Machine Learning Studio を開く

手順は省くが、まずは Microsoft Azure の管理ポータルから Machine Learning を選択して以下の管理画面に到達してほしい。
f:id:waritohutsu:20151216163128p:plain

学習用データを Azure Machine Learning Studio に登録

日経平均プロフィル-日経の指数公式サイト- より日経平均株価のデータが日次で CSV 形式として保存されている nikkei_stock_average_daily_jp.csv を取得する。
CSV ファイルには データ日付, 終値, 始値, 高値, 安値 しか項目がないので、Excel でファイルを開き、前日終値, 前日始値, 前日高値, 前日安値列を追加して 終値, 始値, 高値, 安値の値を1日分ずらしてコピーする。
前日のデータが存在しなくなる初日データ(サンプル作成時は 2012/1/4 日)行は削除し、データ最終行にある「本資料は日経の著作物であり、本資料の全部又は一部を、いかなる形式によっても日経に無断で複写、複製、転載または流布することができません。」の文章が記載されている行も削除する。

最終的な形式は以下のようになるイメージだ。最後に、秀丸等でファイルを開きなおし、 Shift-JIS 形式になっているデータを UTF-8 形式に保存しなおせば完了だ*1
f:id:waritohutsu:20151216163143p:plain

Azure Machine Learning Studio を開き、[New]-[FROM LOCAL FILE]を選択する。
f:id:waritohutsu:20151216163201p:plain

以下の画面の様に作成した CSV データを選択し、Azure ML にデータセットを登録する。
f:id:waritohutsu:20151216163211p:plain

登録したデータからモデルを学習する

管理画面の[NEW]-[EXPERIMENT] から Blank Experiment を選び、からのシートを作成する。以下の画面が表示されればOKだ。
f:id:waritohutsu:20151216163350p:plain

次に、画面左の[Saved Datasets]-[My Datasets]から先ほど登録した日経平均株価CSV データを選択し、ドラッグアンドドロップで移動して作成する。

作成後、データセットの真ん中下側にある丸を右クリックし、Visualize を選択すると以下の画面が表示され、正常にデータが読み込めていることが理解できる。
f:id:waritohutsu:20151216163417p:plain

次に、学習には不要である データ日付, 始値, 高値, 安値列を削除する。前日のデータを利用して終値を計算するため、これらの列は不要なためだ。
[Data Transformation]-[Manipulation] から Project Columns を選択し、ドラッグアンドドロップでシート上に配置する。さらに、データセットの真ん中下側にある丸を左クリックし、そのままマウスをドラッグして Project Columns につなぐ。以下のような画面になれば成功だ。
f:id:waritohutsu:20151216163453p:plain

このままではどの列を選択するかを指示できていないため、画面右の Launch Column Selector を起動して必要な列だけピックアップするため、以下の様に設定する。
f:id:waritohutsu:20151216163503p:plain

次に、モデル向けの学習データ、学習精度を確認するためのデータに分割するため [Data Transformation]-[Sample and Split] の Split Data を選択してシートに配置し、以下のように Project Columns と接続する。
f:id:waritohutsu:20151216163521p:plain

今回は特に Split Data の設定はしないが、必要な場合は振り分けの Random Seed や分配率等を変えても良いだろう。

次に学習するモデルを配置する。まずは [Train] から Train Model をシート上に配置し、モデルに利用するアルゴリズムである線形回帰(Linear Regression)をシート上に配置し、それらを以下のように配置する。
f:id:waritohutsu:20151216163605p:plain

ここでさらに Train Model を選択し、画面右から Launch column Selector を押して予測する列を選択するが、今回は当然「終値」を選択する。ここまでで、実サンプルデータを利用してモデルに学習させるボックスの配置は完了した。

次に学習したモデルを評価するためのボックスを配置する。画面左より Score Model と Evaluate Model を選択し、以下のように配置する。Split データの一部を学習のために利用し、残ったデータをモデル評価のために利用しているのが理解できると思う。
f:id:waritohutsu:20151216170117p:plain

あらかた完成したので、画面下より[Save]-[Save As]を選択して任意の名前で保存する。次に、シートを実行するために [RUN] を押下する。
私が試した際には1分ほどで完了したが、以下の画面の様に各ボックスに緑のチェック印がついていれば完了だ。
f:id:waritohutsu:20151216170127p:plain

Evaluated Model の真ん中下を右クリックし、Visualize を選択すると以下のようにモデルに対する評価を確認できる。
f:id:waritohutsu:20151216170139p:plain

それぞれ、作成したモデルの正確さを評価する値になる。

Mean Absolute Error 145.023541
Root Mean Squared Error 204.021175
Relative Absolute Error 0.046778
Relative Squared Error 0.002936
Coefficient of Determination 0.997064

上記のうち、特に Root Mean Squared Error Coefficient of Determination から以下が理解できる。

  • Coefficient of Determination:1 に近いほどモデルの精度が高いため、今回はモデルの精度が良い
  • Root Mean Squared Error:二乗平均平方根の値であるため、予測値と実際の値とのかい離が大きいと思われる

上記から、予測精度は悪くないが、外した場合の誤差が大きいということが読み取れる。株価予測としては数値の誤差が大きいことに不安を感じるものの、ここまでで学習モデルの完了だ。

Web サービスとして公開する

学習したモデルを利用し、Web サービスとして他アプリと連携できるかたちで公開する。画面下より [Predict Web Service - Recommended] を押下する。シート内でごにょごにょとボックスが動いて以下の形になるはずだ。
f:id:waritohutsu:20151216170241p:plain

Web Service Input が Project Column につながっているが、このままだと不要なデータも引数として入力する必要があるので以下の形に変更し、さらに Project Column から「終値」を選択列から除外する。
f:id:waritohutsu:20151216170255p:plain

あとは [Run] で実行した後に [DEPLOY WEB SERVICE]を選択することでAzure Machine Learning Studio で Web サービスとして登録される。
無事に Web サービスとして登録されると以下の画面が表示される。
f:id:waritohutsu:20151216170303p:plain

上記の[Test]ボタンを押下すると引数を入力する画面が表示されるので、冒頭でスクリーンショットを利用して紹介した通り、日経平均株価の予測ができる。

考察について

2015年12月10日までのデータを利用して実際に試した結果は以下となった。

  • 予測値:19053.57
  • 実際の値:19,230.48

本来日経平均株価を予測するためには、NYダウ平均、為替、先物、原油価格等の様々なパラメータ値はもちろんのこと、政治的な要因も大きく絡むために今回利用したパラメータだけではデータが少なすぎることが原因だと思われる。
本サンプルの拡張としては「パラメータ値を増やす」はもちろんだが、「今回利用した Linear Regression だけでなく異なる回帰アルゴリズムの利用を検討する」といった方法も考えられる。

予測精度そのものは改善の必要があるが、慣れている人間ならば上記のサンプルを20分~30分程度で作成することが可能な点も含め、Microsoft Azure の Machine Learning が容易に利用できることが理解できたと思う。

是非皆さまも利用を検討して頂きたい。

*1:UTF-8にしないとAzure Machine Learning Studio で文字化けする

AppInsights の Java SDK を利用して CentOS の CPU 使用率、メモリ使用量、Process IO rate を確認する

Java Windows Azure

前回までの記事で、Application Insights の JavaSDK を利用することで、Windows の場合に CPU 使用率、メモリ使用量、Process IO rate が取得できることが分かった。
Unix の場合はどうなのかと確認したところ、ApplicationInsights-Java/core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/ 内に Unix の性能情報を取得することができそうなクラスが存在することが分かった。

AppInsights の JavaJDK を仕込んだアプリを CentOS で動かしてみる

Azure のテンプレートから CentOS 7.1 を選択し、OpenJDK 1.8.0 と Tomcat をインストールし、Windows の場合と同様の war をデプロイして Java Web アプリを動かしてみた。
アプリ稼働後、数分でパフォーマンス情報が Application Insights のポータルに反映される。念のため yes >> /dev/null コマンドで CPU 負荷をかけると、以下のグラフの様に CPU 使用率が増加していることもわかる。
f:id:waritohutsu:20151206141759p:plain

どこの情報を取得しているのかソースコードを確認してみる

パッケージを確認すると、以下の7クラスが存在する。Abstract の名前が含まれるクラスは置いておくとして、CPU 使用率、メモリ使用量、Process IO rate を取得していることが推察できる。

  • AbstractUnixPerformanceCounter.java
  • UnixParsingState.java
  • UnixProcessIOPerformanceCounter.java
  • UnixProcessIOtParser.java
  • UnixTotalCpuPerformanceCounter.java
  • UnixTotalMemInfoParser.java
  • UnixTotalMemoryPerformanceCounter.java

CPU 使用率はどこを見ているのかを確認すると、UnixTotalCpuPerformanceCounter.java に記載されるように /proc/stat の値を確認していることが分かる。

メモリ使用量については UnixTotalMemoryPerformanceCounter.java を確認すると /proc/meminfo の情報を取得していることが分かる。

Process IO rate は UnixProcessIOPerformanceCounter.java より、/proc/"自身のプロセスID"/io から取得していることが分かる。

また、ここでは詳細に解説しないが、上記の各 PerformanceCounter クラスは、war アプリケーション起動時に初期化される。このため、特に開発者側が意識をせずに利用することができる点も利便性が高いといえる。

AppInsights の Java SDK が出力するログを確認したい場合

Java Windows Azure

Application Insights を利用する場合、設定ファイルが読み込まれたか?何か内部エラーが発生していないか等を詳細に確認したい場合がある。その際に利用できる InternalLogger について紹介する。

有効化する - コンソール出力編

以下のように ApplicationInsights.xml の SDKLogger タグを追加する。

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings" schemaVersion="2014-05-30">
        "中略"
	<SDKLogger>
	</SDKLogger>
	<DisableTelemetry>false</DisableTelemetry>
</ApplicationInsights>

ソースコードを確認すると理解できるが、SDKLogger タグが存在している場合に初期化処理が実行されるようになっている。

デフォルトでは以下のように CONSOLE に最初が"AI"として情報が出力される設定になっている。

情報: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
AI: INFO 30-11-2015 00:08, 19: Registering WebApp with name 'AppInsightWebApp'
AI: INFO 30-11-2015 00:08, 19: Configuration file has been successfully found as resource
AI: WARN 30-11-2015 00:08, 19: 'MaxTelemetryBufferCapacity': null value is replaced with '500'
AI: WARN 30-11-2015 00:08, 19: 'FlushIntervalInSeconds': null value is replaced with '5'
AI: TRACE 30-11-2015 00:08, 19: Using Http Client version 4.3+
AI: TRACE 30-11-2015 00:08, 19: No back-off container defined, using the default 'EXPONENTIAL'
AI: WARN 30-11-2015 00:08, 19: 'Channel.MaxTransmissionStorageCapacityInMB': null value is replaced with '10'
AI: TRACE 30-11-2015 00:08, 19: C:\Users\"username"\AppData\Local\Temp\AISDK\native\1.0.1 folder exists
AI: TRACE 30-11-2015 00:08, 19: Java process name is set to 'javaw'
AI: TRACE 30-11-2015 00:08, 19: Successfully loaded library 'applicationinsights-core-native-win64.dll'
AI: TRACE 30-11-2015 00:08, 19: Registering PC 'JSDK_ProcessMemoryPerformanceCounter'
AI: TRACE 30-11-2015 00:08, 19: Registering PC 'JSDK_ProcessCpuPerformanceCounter'
AI: TRACE 30-11-2015 00:08, 19: Registering PC 'JSDK_WindowsPerformanceCounterAsPC'
AI: INFO 30-11-2015 00:08, 19: Registered WebApp 'AppInsightWebApp' key='AppInsightWebApp'
AI: INFO 30-11-2015 00:08, 19: Successfully registered the filter 'ApplicationInsightsWebFilter'
11 30, 2015 12:08:29 午前 org.apache.coyote.AbstractProtocol start
情報: Starting ProtocolHandler ["http-nio-8080"]
11 30, 2015 12:08:29 午前 org.apache.coyote.AbstractProtocol start
情報: Starting ProtocolHandler ["ajp-nio-8009"]
11 30, 2015 12:08:29 午前 org.apache.catalina.startup.Catalina start
情報: Server startup in 2452 ms
AI: TRACE 30-11-2015 00:08, 32: InProcessTelemetryChannel sending telemetry

有効化する - ファイル出力編

AppInsights Java SDKソースコードを確認すると SDKLoggerXmlElement にて JAXB を利用して拡張機能が実装されていることが分かる。
InternalLogger を確認すると CONSOLE 以外にも FILE が存在するので、さっそく試すと以下のエラーが出力された。

AI: INFO 30-11-2015 00:14, 19: Registering WebApp with name 'AppInsightWebApp'
AI: INFO 30-11-2015 00:14, 19: Configuration file has been successfully found as resource
AI: SDK Internal Logger internal error while initializing 'FILE': 'Unique log file prefix is not defined'.

上記から prefix の文字を含めて設定する必要があるらしいことが分かる。SDKLoggerXmlElement.java を確認すると設定項目があるので、以下を設定する。

	<SDKLogger type="FILE">
		<UniquePrefix>AppInsightWebApp</UniquePrefix>
	</SDKLogger>

無事アプリケーションが起動すると %TEMP%\javasdklogs\AppInsightWebApp-2015-11-29-23-20-05-JavaSDKLog3742616972029564770.jsl としてファイルが作成された。
ソースコードを確認すると File.createTempFile( uniquePrefix + LOG_FILE_PREFIX, LOG_FILE_SUFFIX, baseFolder) としてファイルを作成していることが確認できる。

クラウドサービスで Application Insights の Java SDK を利用する場合の TIPS

Java Windows Azure

本ブログで何度も紹介させて頂いている Application Insights だが、クラウドサービスの利用時に AVAILBABLE MEMORY, PROCESS IO RATE, PROCESSOR TIME が以下の様に出力されない場合がある。
f:id:waritohutsu:20151129143045j:plain

解決方法を先に言ってしまうと、クラウドサービスのスタートアップタスクで Visual C++ Redistributable Packages for Visual Studio 2013 をインストールすればよい。

何でログが出ないの?

クラウドサービスで利用する OS は Windows Server(設定によってバージョンは変わる)なのは周知だと思うが、Java SDK の JniPCConnector.java を確認すればわかるが、JNI を利用してネイティブコードを呼び出している。
さらにソースコード内で ApplicationInsights-Java の C++ ソースを確認すればわかる通り、ネイティブコードを利用してパフォーマンスカウンタに登録していることがわかる。
これらの処理を行う際、Windows Server では Visual C++ Redistributable Packages for Visual Studio 2013 のセットアップが必要になるが、本モジュールが不足しているために AVAILBABLE MEMORY, PROCESS IO RATE, PROCESSOR TIME が出力されないという問題が発生する。

対応方法

Azure Toolkit for Eclipse を利用したプロジェクト構成が前提となるが、以下の手順で問題は解決できる。

  • Visual C++ Redistributable Packages for Visual Studio 2013 から vcredist_x64.exe ファイルを取得
  • "project name"\WorkerRole1\approot\vcredist_x64.exe にモジュールを配置する
  • 以下のバッチファイルを参考に、"project name"\WorkerRole1\approot\startup.cmd に対して vcredist_x64.exe のセットアップ処理を記載する
:: *** This script will run whenever Azure starts this role instance.
:: *** This is where you can describe the deployment logic of your server, JRE and applications 
:: *** or specify advanced custom deployment steps
::     (Note though, that if you're using this in Eclipse, you may find it easier to configure the JDK,
::     the server and the server and the applications using the New Azure Deployment Project wizard 
::     or the Server Configuration property page for a selected role.)

echo Hello World!
vcredist_x64.exe /install /quiet

WildFly Swarm で JAX-RS アプリを動かしてみる #1

Java

今回は Microsoft Azure からちょっと外れ、WildFly Swarm という WildFly プロジェクトの派生プロジェクトをいじってみる。

WildFly Swarm って何?

WildFly Swarm とは Java EE 系の AP サーバ起動が遅かったり、Java EE 系の仕様策定が遅めで各仕様のバージョンアップを個別にしたい等、様々な課題に対しての解決策として提示された機能っぽい*1。Spring Boot の様に実行コンテナごと Fat Jar 化する機能を持っている。

まずは疎通をとってみる

簡単に疎通をとって動かしてみるサンプルとして JAX-RS が簡単そうだったので以下のように動かすとする。以下を参照いただければわかる通り、わずかな plugin 設定と wildfly-swarm-jaxrs への参照設定を追加する程度でアプリケーションの作成が可能になる。

<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/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.mydomain</groupId>
	<artifactId>HelloWildFlySwarm</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>HelloWildFlySwarm</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<build>
		<plugins>
			<plugin>
				<groupId>org.wildfly.swarm</groupId>
				<artifactId>wildfly-swarm-plugin</artifactId>
				<configuration>
					<mainClass>com.mydomain.HelloWildFlySwarm.App</mainClass>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>package</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>wildfly-swarm-jaxrs</artifactId>
			<version>1.0.0.Alpha4</version>
		</dependency>
	</dependencies>
</project>

以下を参照いただければ分かるが main 関数内で一つ以上の deploy を実施する必要がある。

package com.mydomain.HelloWildFlySwarm;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.wildfly.swarm.container.Container;
import org.wildfly.swarm.jaxrs.JAXRSArchive;

import com.mydomain.HelloWildFlySwarm.jaxrs.RootApi;

public class App {
	public static void main(String[] args) throws Exception {
		Container container = new Container();
		JAXRSArchive jaxrsDeployment = ShrinkWrap.create(JAXRSArchive.class);
		jaxrsDeployment.addClass(RootApi.class);
		jaxrsDeployment.addAllDependencies();
		container.start().deploy(jaxrsDeployment);
	}
}

また、上記では ShrinkWrap を利用している。

package com.mydomain.HelloWildFlySwarm.jaxrs;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/")
public class RootApi {

	@GET
	@Produces("text/plain")
	public String get() {
		return "Hello WildFly Swarm";
	}
}

以下の Maven コマンドをたたいて実行すると、AP サーバを立ち上げることなく REST アプリケーションの立ち上げが可能だ。

>mvn clean wildfly-swarm:run

jar ファイルが 30MB 程度になっており、なかなかのサイズになっていた。実行コンテナ、依存 jar も含まれるからまぁそんなもんかというところ。

TIPS

初回動作させる際に Failed to resolve artifact 'org.jboss.msc:jboss-msc:1.2.6.Final' が発生していた。Swarm でなく Maven 側の依存解決の問題となるが、以下を一時的に追加して自身のローカルリポジトリに以下の jar を配置することで回避できる。

		<dependency>
			<groupId>org.jboss.msc</groupId>
			<artifactId>jboss-msc</artifactId>
			<version>1.2.6.Final</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.spec.javax.sql</groupId>
			<artifactId>jboss-javax-sql-api_7.0_spec</artifactId>
			<version>1.1.0.Final</version>
		</dependency>

*1:誰か間違っていたら突っ込みをお願いしたい

Power BI 向けの組織アカウントをお手軽に作ってみる

Windows Azure

Stream Analytics の出力先に Power BI が指定可能になってしばらく経った。リアルタイム分析*1を行う場合、Power BI は非常に有力な機能であり、当ブログでも以下のように機能を紹介してきた。

今回は Power BI を利用する際にちょっとハマる出来事について紹介したい。

Stream Analytics の出力先に Power BI を指定すると認証に失敗する?

管理ポータルから Stream Analyics のインスタンスを選択し、「出力の追加」をを指定すると Power BI(プレビュー)が選択できる。
こちらを選択した後、アカウント選択が可能だが、「既存の Microsoft Power BI ユーザ」を選択して xxxx@hotmail.com の様な Microsoft アカウント を入力すると以下のようにエラーになる。
f:id:waritohutsu:20151112132446p:plain

こちらの原因は Power BI 側の承認は組織アカウントのみをサポートしている点だ。組織アカウントと Microsoft アカウントの差異については以下の記事を参考にしてほしい。

お手軽に組織アカウントを作るには?

Active Directory Domain Service を作成し、ディレクトリ同期機能を使って Azure Active Directory にアカウント同期をするといった処理を行えばもちろん組織アカウントの作成は可能だが、そんなにメンドクサイことはしたくない場合も多いだろう(特に検証時)。

その場合、既存の Azure Active Directory のディレクトリに対し、直にユーザを作成する方法があげられる。以下のように、ソースが Microsoft アカウント でなく Microsoft Azure Active Directory ならば Power BI 側でアカウント作成が可能だ。
f:id:waritohutsu:20151112132513p:plain

「ユーザーの追加」ボタンを押下し、以下のように「組織内の新しいユーザー」を作成する。
f:id:waritohutsu:20151112132520p:plain

ウィザードに従ってアカウントの作成後、再度 Stream Analytics の「出力の追加」先で「新規ユーザー」を指定し、作成した組織アカウントでログインを実施する(例:"user name"@"directory name".onmicrosoft.com
)。
設定が完了すると、以下のように「種類」が Power BI の出力先が作成される。
f:id:waritohutsu:20151112132527p:plain

*1:数分程度の誤差は生じる

Application Insigts のリクエストデータを SQL Database に格納する

Windows Azure ASP.NET MVC

アプリケーションのログデータを永続化/可視化してくれる Application Insights だが、本ブログでは今まで過去2回にわたり、Web アプリのログに対して Stream Analytics を介し Power BI を利用した可視化を紹介していた。

上記は Java の Web アプリで行っていたが、今回は ASP.NET を利用したログ出力を行い、SQL Database へと Application Insights のログを永続化する方法を紹介する。

ASP.NET で Application Insights を利用する方法

こちらは非常にシンプルだ。以下のサイトを参考に Application Insights の SDK を有効化するだけでアプリケーションの監視が可能になる*1
Application Insights SDK を追加して ASP.NET アプリを監視する

上記のステップを通して Application Insights の SDK を有効化したアプリケーションを、Azure Resource Manager で作成した Windows Server 2012 R2 仮想マシンIIS にデプロイした。

Application Insights のログを SQL Database に出力するまでにステップ

以下の手順が必要になる。連続エクスポートの設定については特に記載は不要だと思うので、他の手順について紹介する。

  • Application Insights の連続エクスポートの有効化
  • ログ格納用に SQL Database のテーブル作成
  • Stream Analytics の設定

今回は サーバによって受信された要求 である Request データを取り扱う。こちらは以下のようなファイル名でデータが格納される。

  • "appinsights name"_"your instrument key"/Requests/{date}/{time}/93ddecba-d09f-4027-aaed-f773de32495e_20151108_085830.blob

Request データのファイルの中身は以下のような形式だ。

{
    "context": {
        "application": {
            "version": "Unknown"
        }, 
        "cloud": {}, 
        "custom": {
            "dimensions": [], 
            "metrics": []
        }, 
        "data": {
            "eventTime": "2015-11-08T08:11:49.5138835Z", 
            "isSynthetic": false, 
            "samplingRate": 100.0
        }, 
        "device": {
            "browser": "Internet Explorer", 
            "browserVersion": "Internet Explorer 11.0", 
            "deviceModel": "Virtual Machine", 
            "deviceName": "Virtual Machine", 
            "id": "(my vm name)", 
            "locale": "en-US", 
            "network": "6", 
            "oemName": "Microsoft Corporation", 
            "os": "Windows", 
            "osVersion": "Windows 10", 
            "roleInstance": "(my vm name)", 
            "screenResolution": {}, 
            "type": "PC"
        }, 
        "location": {
            "city": "Tokyo", 
            "clientip": "119.242.23.0", 
            "continent": "Asia", 
            "country": "Japan", 
            "province": "Tōkyō"
        }, 
        "operation": {
            "id": "3579395009003726603", 
            "name": "GET Account/Register", 
            "parentId": "3579395009003726603"
        }, 
        "serverDevice": {}, 
        "session": {
            "id": "C1A7FDE1-AD1B-4D65-9F05-3DA2421E88FB", 
            "isFirst": false
        }, 
        "user": {
            "accountAcquisitionDate": "2015-11-08T04:20:59Z", 
            "anonAcquisitionDate": "0001-01-01T00:00:00Z", 
            "anonId": "16BADE4E-E5E6-4652-B518-8F012442C25D", 
            "authAcquisitionDate": "0001-01-01T00:00:00Z", 
            "isAuthenticated": false
        }
    }, 
    "internal": {
        "data": {
            "documentVersion": "1.61", 
            "id": "fcac8a5b-7615-4c53-9e51-28ccd6f40eac"
        }
    }, 
    "request": [
        {
            "count": 1, 
            "durationMetric": {
                "count": 1.0, 
                "max": 373751007.0, 
                "min": 373751007.0, 
                "sampledValue": 373751007.0, 
                "stdDev": 0.0, 
                "value": 373751007.0
            }, 
            "id": "3579395009003726603", 
            "name": "GET Account/Register", 
            "responseCode": 200, 
            "success": true, 
            "url": "http://(my vm public ip)/Account/Register", 
            "urlData": {
                "base": "/Account/Register", 
                "hashTag": "", 
                "host": "<my vm public ip>"
            }
        }
    ]
}

シンプルな JSON 形式だが、request 部分は中身が配列になっているので、Stream Analytics でクエリを書く際に注意が必要なポイントになる。

ログ格納用に SQL Database のテーブル作成

自分のサブスクリプションである必要はないが、SQL Database インスタンスを一つ作成(インスタンスサイズは何でも良いが、Standard: S0 以上を推奨する)し、以下のテーブルを作成する。

CREATE TABLE [dbo].[RequestTable](
    [eventTime] [datetime] NOT NULL,
    [isSynthetic] [nvarchar](50) NULL,
    [samplingRate] [real] NULL,

    [browser] [nvarchar](50) NULL,
    [browserVersion] [nvarchar](50) NULL,
    [deviceModel] [nvarchar](50) NULL,
    [deviceName] [nvarchar](50) NULL,
    [deviceId] [nvarchar](50) NULL,
    [locale] [nvarchar](50) NULL,
    [network] [nvarchar](50) NULL,
    [oemName] [nvarchar](50) NULL,
    [os] [nvarchar](50) NULL,
    [osVersion] [nvarchar](50) NULL,

    [roleInstance] [nvarchar](50) NULL,
    [type] [nvarchar](50) NULL,

    [clientIp] [nvarchar](50) NULL,
    [continent] [nvarchar](50) NULL,
    [country] [nvarchar](50) NULL,
    [province] [nvarchar](50) NULL,
    [city] [nvarchar](50) NULL,

    [operationId] [nvarchar](50) NULL,
    [operationName] [nvarchar](50) NULL,
    [parentId] [nvarchar](50) NULL,

    [sessionId] [nvarchar](max) NULL,
    [sessionIsFirst] [nvarchar](50) NULL,

    [requestName] [nvarchar](50) NULL,
    [responseCode] [nvarchar](50) NULL,
    [requestSuccess] [nvarchar](50) NULL,
    [requestUrl] [nvarchar](50) NULL
)

CREATE CLUSTERED INDEX [pvTblIdx] ON [dbo].[RequestTable]
(
    [eventTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

全ての JSON データを格納するテーブル構造となっていはいないが、もし必要な場合は自分で修正してほしい。

Stream Analytics の設定

まずは Stream Analytics の input として以下の形式で BLOB を 設定する。

  • 名前:input-blob
  • ソースタイプ:データストリーム
  • ソース:BLOBストレージ
  • パスパターン:(appinsights name)_(your instrument key)/Requests/{date}/{time}
  • 日付の形式:YYYY-MM-DD
  • 時刻の形式:HH

次に、Stream Analytics の output として以下を設定する。サーバ名、データベース名、ユーザ名、パスワード等は適切に設定すること。

  • 名前:output-sqldb
  • シンク:SQL データベース
  • テーブル:RequestTable

上記の設定後、以下のクエリを Stream Analytics に設定する。

    SELECT 
      flat.ArrayValue.name as requestName
      ,flat.ArrayValue.responseCode as responseCode
      ,flat.ArrayValue.success as requestSuccess
      ,flat.ArrayValue.url as requestUrl

      ,A.context.data.eventTime as eventTime
      ,A.context.data.isSynthetic as isSynthetic
      ,A.context.data.samplingRate as samplingRate

      ,A.context.device.browser as browser
      ,A.context.device.browserVersion as browserVersion
      ,A.context.device.deviceModel as deviceModel
      ,A.context.device.deviceName as deviceName
      ,A.context.device.id as deviceId
      ,A.context.device.locale as locale
      ,A.context.device.network as network
      ,A.context.device.oemName as oemName
      ,A.context.device.os as os
      ,A.context.device.osVersion as osVersion
      ,A.context.device.roleInstance as roleInstance
      ,A.context.device.type as type

      ,A.context.location.clientip as clientIp
      ,A.context.location.continent as continent
      ,A.context.location.country as country
      ,A.context.location.province as province
      ,A.context.location.city as city

      ,A.context.operation.id as operationId
      ,A.context.operation.name as operationName
      ,A.context.operation.parentId as parentId

      ,A.context.session.id as sessionId
      ,A.context.session.isFirst as sessionIsFirst
    INTO
      [output-sqldb]
    FROM [input-blob] A
    CROSS APPLY GetElements(A.request) AS flat

上記の完了後、Stream Analytics ジョブを実行すると SQL Database 側にデータが格納されているはずだ。

実行結果の確認

今回は Visual StudioSQL Server オブジェクトエクスプローラから確認する。サーバに接続し、テーブルを右クリックして データ表示 を選択した結果は以下となる。
f:id:waritohutsu:20151108213952p:plain

さらに以下のクエリを発行し、国別のアクセスを確認してみる。

SELECT COUNTRY, COUNT(COUNTRY) as NUM FROM [dbo].[RequestTable] GROUP BY COUNTRY

f:id:waritohutsu:20151108214004p:plain

なぜか United States のリクエストがあり若干衝撃だが、こういったデータの取得も可能になる。

*1:ただし、依存関係の追跡 (および IIS パフォーマンス カウンター)も監視する場合には追加で手順が必要な点も記載がある