Liferay DXP 7.0は検索関連の機能でElasticsearchと密接な関係があります。 たまたまですが、ElasticsearchはELKスタックの一部です。 なので、"ELKスタックを丸ごと使えば何ができるのか?"というのは当然のことです。
その答えは、多くのチャンスがあるということです。ここではDXP 7.0用の "ダッシュボード "を作成し、複雑な情報をグラフィカルなフォーマットで表示するための様々なチャートやグラフを用意しています。 これらは情報を合成して素早く消費できるので人気があります。
この記事では、ELKスタックを使っていくつかのダッシュボードグラフを作成します(ELKスタックがインストールされていることを前提としています)。
ここで紹介した詳細は、開発したものを使用してテストしたものです。
- ライフレイDXPマスターブランチ
- Elasticsearch 2.3.3
- キバナ4.
- Logstash 2.3.2
- アパッチ HTTPd 2.4
ライフレイDXP
構築中のダッシュボードは、Liferay DXPに組み込まれたLiferay Audit機能を活用する予定です。 そのため、これらの機能はLiferay Portal 7.0 CEでは機能が不足しているため、動作しません。 Liferay Portal 7.0 CE の監査レイヤーを構築することは可能ですが、このドキュメントの範囲外です。
エラスティックサーチ
前述の通り、DXP 7.0では検索関連の機能にElasticsearchを使用しています。 本番環境では、ES はアプリケーション・コンテナの外に個別にインストールされ、一般公開されていないバックエンド・システムにインストールされる可能性が高いです。
きばな
KibanaはElasticsearchをベースにした可視化ツールです。 Kibanaでは、ESに対する検索クエリを使用して、その結果に基づいてチャートやグラフとして可視化することができます。 このドキュメントではいくつかのシンプルなビジュアライゼーションを紹介しますが、Kibana は複雑なクエリを使って魅力的なビジュアライゼーションを構築することをサポートしています。 ただし、複雑なクエリや可視化はこのドキュメントの範囲外です。
また、KibanaはバックエンドにもESサーバーか専用サーバーにインストールする必要があります。 クライアントブラウザはリソースのためにKibanaサーバーにアクセスする必要がありますが、これはプロキシを介して行う必要があります。 このドキュメントでは、Kibana サーバへのリクエストをプロキシするために Apache httpd を使用します。
ログスタッシュ
logstashという名前が誤解を招き、ログファイルのためのツールであるかのような印象を与えています。 実はlogstashは、構造化されていないデータソースからデータを抽出して別の場所に送信するのに適したETL(Extract, Transform, Load)ツールです。 ログファイルから情報を抽出してドキュメントとしてElasticsearchにロードするツールとしてスタートしましたが、データベースやファイル、twitterフィードなどからログスタッシュを動作させるための様々なプラグインが開発されています。 フィルタと変換のメカニズムは、抽出されたデータをElasticSearchドキュメントとしてロードするのに適したドキュメントに変換するのを容易にします。
ビート
logstashに関連するツールとしてbeatsフレームワークがあります。 ログスタッシュはリソース集約型のツールである可能性があり、すべての生産システムにインストールするには重すぎるかもしれません。 それを解決してくれるのが、ファイルビーツツールをはじめとするビーツフレームワークです。 beatsフレームワークは軽量なエージェントを提供しており、データをネットワーク経由でログスタッシュのインスタンスに転送する前に、ローカルでの読み込みやフィルタリングを行います。
このドキュメントではbeatsツールは使用していませんが、本番環境ではバックエンドにlogstashをインストールし(ESサーバか専用サーバ)、他のシステムでfilebeatsエージェントを使用してlogstashへのデータ送信を処理することをお勧めします。
アパッチHTTPd
このドキュメントでは、Liferay TomcatインスタンスとバックエンドのKibanaサーバーへのリクエストをプロキシするためにApache HTTPdを使用しています。
解決
ここで紹介した実装は、以下の要件を満たすように設計されています。
- ELKスタックを使用して、ログインアクティビティを表示するダッシュボードを作成します。
- 現在の1週間の1日あたりのログイン数
- 現在の日の1時間あたりのログイン数
- ELKスタックを使用して、1日あたりのフォーム送信数を表示するダッシュボードを作成します。
- ELKスタックを使用して、ページの人気度を表示するダッシュボードを作成します。
これらのダッシュボードを構築するために必要なデータはアクティビティベースであるため、データ収集の処理にはAuditフレームワークを活用します。 アーキテクチャ図は以下のようになります。
監査フレームワークを使用すると、イベントを生成するために使用されるロジックは、監査ファイルを維持するコードから分離されます。 また、監査フレームワークを活用することで、監査メッセージを生成するためのサンプルがDXPソース内で利用可能になります。
実装の詳細
要件を満たすダッシュボードを構築するためには、以下のようなものを生成する必要があります。
- ダッシュボードに必要なデータを提供するために適切な監査メッセージを生成するカスタム監査コード。 ログインダッシュボードは、OOTBユーザーモデルリスナー監査イベントジェネレータを使用して満たすことができます。
- 監査メッセージを受信し、監査ファイルに書き込むためのAuditMessageProcessorです。
- 監査ファイルを処理し、ESに文書を読み込むためのログスタッシュの設定ファイル。
- 各ダッシュボードのキバナの可視化。
- Liferay/TomcatとKibanaサーバにメッセージをプロキシするためのApacheの設定。
監査コード
ダッシュボードでは、以下のアクションに対して監査情報をトリガーする必要があります。
- ユーザーログイン-残念ながら、これはcom.liferay.portal.security.audit.event.generators.event.LoginPostActionのOOTB監査イベント生成コードですでに処理されています。 ログインが発生すると、このポストログインハンドラは監査メッセージを生成し、これらの監査メッセージはダッシュボードのグラフに使用することができます。
- フォーム送信 - LR7でフォームが送信されると、フォームフィールドはシリアル化され、DDMContentレコードとして保存される。 そのため、フォームがいつ保存、更新、削除されたかを識別するために、DDMContent ModelListenerが必要になります。 テスト中、フォームデータのデシリアライズに必要な対応するDDMStorageLink要素の前にDDMContentsが保存されていることが判明したため、最初のフォーム送信を監査するためにDDMStorageLinksのModelListenerが追加されました。
- ページビューは多くの方法で監査することができますが、この実装では、レンダリングされるページを識別するために ServicePreAction が使用されます。 LayoutAction クラスから適用されたコードを使用して、レンダリングされるページが識別され、監査されます。
添付のプロジェクトでは、関連するクラスは以下の通りです。
- com.liferay.portal.security.autudit.event.generator.DDMContentModelListener
- com.liferay.portal.security.autudit.event.generator.DDMStorageLinkModelListener
- com.liferay.portal.security.audit.event.generator.AuditServicePreAction
また、AuditMessageインスタンスを作成するために使用される com.liferay.portal.security.autudit.generators.util
パッケージには、いくつかのユーティリティクラスがあります。
監査メッセージ処理装置
DXPの監査処理メカニズムでは、AuditMessageインスタンスはLiferayMessageBusに置かれます。 デフォルトのメッセージバスリスナーである com.liferay.portal.security.autudit. router.internal.DefaultAuditRouter
は、各監査メッセージを受信し、登録された各AuditMessageProcessorインスタンスに転送します。
ELKスタックのlogstashコンポーネントを活用するために、ここで紹介する実装では監査メッセージをJSONファイルに書き出します。 各監査メッセージはJSONにシリアライズされ、回転ファイルに書き込まれます。
このための実装クラスは com.liferay.portal.security.audit.json.log.JsonLoggingAuditMessageProcessor
であり、対応するクラスは
com.liferay.portal.security.audit.json.log.JsonLoggingAuditMessageProcessorConfiguration
です。liferay.portal.security.autudit.json.log.JsonLoggingAuditMessageProcessorConfiguration .
イベントの展開と生成
ビジュアライゼーションをビルドアウトするには、ES インデックスで利用可能なドキュメントが必要です。 バンドルをビルドしてDXP環境にデプロイし、イベントの生成を開始します。 数回ログインしてポータル内を移動し、フォームを定義して値の提出を開始します。
次のステップに進むにつれて、環境に戻ってきて、いくつかの新しいイベントを生成し続けてください - これは、すべてのイベントが報告される時間のスパンを与えるでしょう。
ログスタッシュ設定ファイル
logstash設定ファイルは、特定のプロセスの入力、フィルタ、および出力を定義します。
入力構成
この実装では、入力は監査jsonファイルです。
input { file { # For the demo just using a fixed path to file. path => "/Users/dnebinger/liferay/clients/liferay/elk/bundles/logs/json/audit.json" # Since the file may roll over, we should start at the beginning start_position => beginning # Don't ignore older records ignore_older => 0 # our file is a json file so use it for the codec. codec => "json" } }
フィルタの構成
このフィルタは、読み込んだレコードに対していくつかの変換を行います。
filter { # Apply some changes to the incoming records mutate { add_field => { # Copy the eventType field to the action field. "action" => "%{eventType}" } # Strip out the values that we don't care to include in the index. remove_field => [ "sessionID","path","host" ] } # If path is provided, clone to the not analyzed and not indexed fields. if "" in [additionalInfo][path] { mutate { add_field => { "[additionalInfo][pathUrl]" => "%{[additionalInfo][path]}" } add_field => { "[additionalInfo][pathString]" => "%{[additionalInfo][path]}" } } } # The audit timestamp is mashed together, we need to extract it out. # Date follows the following format: 20160608135725305 date { match => [ "timestamp", "yyyyMMddHHmmssSSS" ] } }
出力構成
出力については、レコードをElasticsearchにアップロードします。
output { # Target elasticsearch elasticsearch { # Point at the backend server. If a cluster we'd use multiple hosts. hosts => ["192.168.1.2"] # Specify the index where our records should go index => "audit-%{+YYYY.MM.dd}" } # For debugging purposes, also write the records to the console. stdout { codec => rubydebug } }
ランニングログスタッシュ
インデックスの準備
インデックスをロードする前に、いくつかのフィールドを手動で定義して、分析とインデックスを無効にします。 このような変更は、インデックスが最初に読み込まれる前に行わなければなりません。
コマンドラインで以下のコマンドを実行して、フィールドを事前に定義します。
curl -XPUT 192.168.1.2:9200/audit/logs/_mapping -d ' { "logs": { "properties": { "additionalInfo": { "properties": { "pathUrl": { "type":"string", "index":"not_analyzed" }, "pathString": { "type":"string", "index":"no" } } } } } } '
監査ログがlogstashで処理されると、残りのカラムはデフォルトの設定で追加されます。
ログスタッシュの実行
設定ファイルの準備ができたら、logstashを起動します。 logstashディレクトリから、以下のコマンドを実行します。
bin/logstash agent -f audit.conf
すでにいくつかの監査イベントが作成されているので、logstashが起動するとファイルの処理が開始され、コンソールに以下のようなメッセージが表示されるはずです。
{ "companyId" => "20116", "classPK" => "20164", "clientHost" => "::1", "clientIP" => "::1", "serverName" => "localhost", "className" => "com.liferay.portal.kernel.model.User", "eventType" => "LOGIN", "serverPort" => 80, "userName" => "Test Test", "userId" => "20164", "timestamp" => "20160610231917907", "@version" => "1", "@timestamp" => "2016-06-11T03:19:17.907Z", "action" => "LOGIN" }
これらのメッセージが流れてくると、レコードはファイルから消費され、変換されてドキュメントとしてElasticsearchにロードされていきます。
キバナの可視化
Kibanaのビジュアライゼーションは、実際にはKibana UI内で作成されます。 最初のステップでは、インデックスパターンを定義します。 インデックスパターンは、可視化を構築するための基礎となるものです。
インデックスパターンの作成
下図のようにインデックスパターンを作成します。
検索クエリの定義
次は検索クエリの構築です。 まずは、現在の日の時間別ログイン数です。 Discoverタブをクリックして開始します。
右上のタイムフレームを変更することから始めます。 リンクをクリックして、タイムフレームを「今日」に変更します。
次に、Kibanaバナーの下のドロップダウンのインデックスを、新しく定義された監査インデックスパターンに変更します。 ページは以下のような感じになります。
利用可能なフィールドセクションから、アクション、userName、およびuserIdフィールドを追加します。 これは、レコードのタイムスタンプ、アクション(NAVIGATEやLOGINなど)、イベントのユーザー詳細を表示します。 時間ごとのログインのクエリを作成しているので、検索バーを変更して、次のように読み取ります。
action:LOGIN
と入力してエンターキーを押します。
保存ボタンをクリックして、クエリをログインとして保存します。 この検索は、Logins by HourとLogins by Dayの可視化が後から来る場合にも同じように使用されます。
次は検索でヒットするページです。 新規検索リンクをクリックして、新規検索を開始します。 クエリを設定します。
action:NAVIGATION
選択したフィールドにアクション、userName、additionalInfo.pageUrlを追加します。 ページのヒット数としてこの検索を保存します。
フォーム投稿検索では、新規検索リンクをクリックして、新規検索を開始します。 クエリを設定します。
action:ADD AND className:com.liferay.dynamic.data.mapping.model.DDMContent
選択したフィールドにアクションとclassNameを追加し、この検索をForms Submittedとして保存します。
ビジュアライゼーションの作成
Visualizeリンクをクリックして、ビューを切り替えます。
縦棒グラフをクリックして、Logins by Hourの可視化を作成します。 保存された検索からを選択し、ログイン検索を選択します。
右上の今日が選択されていることを確認してください。 Y 軸セクションで、カスタム ラベルをログインに設定します。
バケットセクションで、X軸オプションを選択します。 集計に日付ヒストグラムを選択し、間隔を毎時に設定し、カスタム・ラベルを時に設定します。
緑の>ボタンをクリックして、ビジュアライゼーションを更新します。 うまくいけば、こんな感じのものが出てくるといいですね。
この可視化を時間別ログインとして保存します。
次の可視化は、新規可視化ボタン、保存された検索から、ログイン検索をクリックします。 X 軸ラベルには、日を使用します。 右上の今週のオプションに変更します。 この可視化を日別ログインとして保存します。
次の可視化のために、上記の手順を繰り返しますが、提出されたフォームの検索を使用します。 Y軸には、フォームを使用し、X軸ラベルには日を使用します。 この可視化をForms Submitted By Dayとして保存します。
最後の可視化については、の新規可視化ボタンをクリックしますが、の円グラフを使用します。 検索にはページヒット数を使用します。 バケットタイプで、分割スライスを選択し、集計に用語を使用します。 フィールドにはadditionalInfo.pathUrl、サイズには10、カスタムラベルにはPageを使用します。
円グラフは次のような感じになるはずです。
選択肢 - 可視化またはダッシュボード
現在、Liferay内で個別に使用できる4つの異なるビジュアライゼーションが用意されています。 もう一つの選択肢は、Kibana Dashboardを作成することです。
キバナダッシュボードはキバナ側で定義されており、フリーフォームのLiferayページとして機能します。 ダッシュボード上では、可視化を追加、サイズ変更、移動することができます。
ダッシュボードのオプションはページの広い領域を取りたいが、どちらか一方をLiferayページに埋め込むことができます。 そのため、個別の可視化の方がうまくいく場合があります。
可視化をLiferayに配置する
Kibanaサーバーはバックエンドサーバー上にあるので、通常はブラウザでアドレスを指定することはできませんが、Kibanaはビジュアライゼーションを動作させるために、いくつかのリソース(jsとcss)をブラウザに公開する必要があります。
IFrameポートレットは、Liferayで可視化をページに配置するために使用されますが、Liferayはリソースをプロキシしません。
Kibana サーバーをブラウザに公開するには、フロントングウェブサーバーを使用して、ほとんどのリクエストを Liferay に、いくつかのリクエストを Kibana サーバーにルーティングします。
ここで使用した設定では、Apache HTTPdを使用して、AJP経由でLiferay/Tomcatにリクエストを送信し、/kibana/と/bundles/のリクエストをKibanaサーバにルーティングしました。 Kibana は server.basePath の設定と httpd 用に定義されたプロキシステートメントで設定する必要がありました。 これらのファイルはプロジェクト内で src/main/resources/kibana/kibana.yml と src/main/resources/apache/mod_jk.conf として公開されています。
可視化 URL の取得
IFrameポートレットを設定するには、URLが必要です。 これらのURLはKibanaから来ています(server.basePathを設定してKibanaを再起動した後)。
可視化を使っても、ダッシュボードを使っても、プロセスは同じです。 Liferayに配置する可視化またはダッシュボードを開き、共有...をクリックします。ボタンをクリックします。 2つの行があり、一番上がEmbed(IFrame)リンク、2番目がSharedリンクです。 どちらのリンクも同じですが、共有リンクには周囲のIFrameタグがありません。
それぞれのリンクの右側には、2つのボタンがあります。 1つ目は短いURLを生成するボタンで、完全な可視化のためのコード文字列を返し、2つ目はクリップボードにコピーするリンクです。 短いURLを使った方が管理しやすいですが、長いフォームもそれなりに価値があります。 使用したいフォームを選択し、値をコピーします。 Liferay側では、ページ上にIFrameポートレットを配置し、そのURLをソースURLとして使用するように設定します。 すべてが正常に動作していれば、可視化はページ上にあるはずです。
なので、URLの長い形式にも価値があります。 URLには、実際にビジュアライゼーションとダッシュボードのレンダリングに使用されるすべての詳細が記載されています。 ある程度の時間と理解があれば、Kibana を使って作業をしなくても、Kibana ビジュアライゼーションを構築したり修正したりすることができます。 確かにKibana UIを活用してビジュアライゼーションを定義した方が簡単ですが、必須ではありません。
追加情報
ダッシュボードの作成は、ELKスタックを使用してLiferayの機能を活用することで、非常に簡単に行うことができます。 スタック内のすべてのツールの柔軟性は、以前にはなかったあらゆる種類の可能性への扉を本当に開いてくれます。
google で検索して、Kibana Dashboard Examplesと検索すると、いくつかの本当にクールなものがあります。 そして今ではLiferayページ内に埋め込むことができ、Liferayデータからも埋め込むことができるようになりました。
このドキュメント、これらの例、およびサンプルコードは、可能性の表面を掻きむしっただけですが、これらは実際の例です。
現在進行中のあるプロジェクトでは、期間内に提出されたさまざまな種類のフォームの数を示すダッシュボードを表示するための要件があります。 この例のコードを使用すると、すべてのフォーム送信は監査レコードになるので、すべてのフォームデータは消費可能な形式でElasticsearchにあります。 フォームと異なるデータタイプに基づいて可視化することで、要件に応じて個々のダッシュボード・チャートが作成されます。 クライアントは、これらの素晴らしいレスポンシブチャートやグラフをすべて手に入れることができますが、実装チームは監査イベントジェネレータを追加し、Elasticsearchの検索に基づいてビジュアライゼーションを定義するだけで済みます。