誰も知らないUsage Metrics(利用状況総計値)の話

  • 4
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

Usage Metrics (利用状況総計値) という機能をご存知でしょうか?
管理パッケージとして配布したアプリケーションが
インストールされた組織でどのくらい利用されているのかを知ることができる、という
アプリケーションベンダーにとってはちょっと気になる機能です。

しかしながらこの機能、Summer'14 から正式リリースになったにも関わらず
いまだにWeb上には公式ドキュメント以外の記事がなかなか出てきません。
というわけで、今回はこの機能についてざっくり紹介してみようと思います。

有効化の手順

本機能はデフォルトでは有効化されていないので、Salesforce にケース申請して有効化してもらう必要があります。
その手順については以前こちらの記事に書きました。
Usage Metrics(利用状況総計値)のインストール手順 - dackdive's blog

ポイントとしては

  • 環境ハブを有効化しておく必要がある
  • 以下2つの組織が環境ハブで接続されている必要がある
    • リリース組織:パッケージのアップロードに使用する Development Edition 組織
    • レポート組織:利用状況データが毎日配信される組織

ということでしょうか。レポート組織は LMO でいいと思います。

Usage Metrics で取得できる情報

管理パッケージをインストールした組織から取得できるのはカスタムオブジェクトとVisualforceページの情報です。

カスタムオブジェクトについてはレコード件数が、
VisualforceページについてはPV数やユニークユーザー数、平均読み込み時間が取得できます。
(詳しいデータ構造については後述)

Usage Metrics データ収集のしくみ

Usage Metrics を有効化すると、レポート組織に毎日自動的にデータが配信されます。
データは MetricsDataFile というオブジェクト名で通常のSObjectと同じように扱うことができますが
SOQLやAPIでのアクセスのみ可能で、UIから参照することはできません。
また、特殊なデータだからか データストレージは消費しません。

MetricsDataFile のレコードはSalesforce のインスタンスごとに作成されます。
ので、1日に作成されるレコードは

2(カスタムオブジェクトとVisualforce) * Salesforce のインスタンス数

となります。
※ 複数のアプリケーションを開発している場合、さらに * アプリケーション数

Usage Metrics のデータ型

Usage Metrics (というより MetricsDataFile) のデータ構造から主なものを抜き出してみます。
ref. https://developer.salesforce.com/docs/atlas.ja-jp.196.0.packagingGuide.meta/packagingGuide/usage_metrics_metricsdatafile_object.htm

項目名 説明
MetricsDataFile Base64 Base64で符号化された利用状況データが含まれるテキストファイル
MetricsDataFileContentType string データファイルの形式。
現在有効なのは text/csv のみ
MetricsDataFileLength int データファイルのサイズ(バイト)
MetricsStartDate dateTime データ収集の開始日時
MetricsEndDate dateTime データ収集の終了日時
MetricsRunDate dateTime データ収集ジョブが実行された日付
MetricsType picklist データの種別
(CustomObject もしくはVisualforce)
NamespacePrefix string データ収集の対象であるパッケージの
名前空間プレフィックス
SendingInstance string このデータの収集元であるサーバインスタンス。

MetricsDataFile というオブジェクトに MetricsDataFile というフィールドがあってややこしいですね。
また、Base64 で〜と書いてますが、少なくともApexの場合は toString() するだけでデータは取得できるのであまり気にしなくていいと思います。

List<MetricsDataFile> mdfList = [
    SELECT
        Id,
        SendingInstance,
        MetricsStartDate,
        MetricsEndDate,
        MetricsRunDate,
        MetricsType,
        MetricsDataFile
    FROM MetricsDataFile
    WHERE SendingInstance LIKE 'AP%'
    AND MetricsType = 'Visualforce'
    LIMIT 10];

for (MetricsDataFile mdf : mdfList) {
  String mdfDataCSV = mdf.MetricsDataFile.toString();
  System.debug(LoggingLevel.INFO, '-------------------');
  System.debug(LoggingLevel.INFO, mdf.SendingInstance);
  System.debug(LoggingLevel.INFO, mdf.MetricsStartDate);
  System.debug(LoggingLevel.INFO, mdf.MetricsEndDate);
  System.debug(LoggingLevel.INFO, mdf.MetricsRunDate);
  System.debug(LoggingLevel.INFO, mdf.MetricsType);
  System.debug(LoggingLevel.INFO, '**Data**');
  System.debug(LoggingLevel.INFO, mdfDataCSV);
}

MetricsDataFile というフィールドにお目当ての統計データがCSV形式で格納されるわけですが、
CSVのフォーマットはカスタムオブジェクトとVisualforceで異なります。

カスタムオブジェクトの場合

  • 組織 ID
  • 組織名
  • 組織のエディション
  • 組織のステータス (Active, Trial など)
  • カスタムオブジェクト名
  • カスタムオブジェクトのレコード件数

例)

"00Dxx0000001gbk","org1","Enterprise Edition","TRIAL","Alpha", "3500" 
"00Dxx0000001gbk","org1","Enterprise Edition","TRIAL","Beta", "1500"

org1 という組織では、ある日の時点で Alpha というカスタムオブジェクトが3500件、Beta というカスタムオブジェクトが1500件存在したことを示します。

Visualforceの場合

  • 組織 ID
  • 組織名
  • 組織のエディション
  • 組織のステータス
  • パッケージバージョン番号
  • Visualforce ページの名前
  • ページがアクセスされた回数
  • ページにアクセスしたユニークユーザ数
  • ページの平均読み込み時間 (ミリ秒)

例)

"00Dxx0000001gbk","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f1","65","11","353"
"00Dxx0000001gbk","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f2","1","1","128.0"
"00Dxx0000001gbk","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f3","1","1","107.0"
"00Dxx0000001gbf","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f1","5","1","73.6"
"00Dxx0000001gbf","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f2","1","1","72.0"
"00Dxx0000001gbf","org1","Enterprise Edition","TRIAL","1.0","/apex/gm12__f3","7","1","50.8"

org1 という組織ではある日の gm12__f1 というページのPV数は65、ユニークユーザー数は11、平均読み込み回数は353msであったことを示します。

開発時のポイント

まずは Usage Metrics Visualization から始めよう

どんなデータが見れるかいまいちイメージできない、何から始めたらいいかわかんないという人は
Usage Metrics Visualization というパッケージをインストールしてとりあえずデータを表示してみるのがいいと思います。
https://developer.salesforce.com/docs/atlas.ja-jp.196.0.packagingGuide.meta/packagingGuide/usage_metrics_visualization.htm

196.png

Usage Metrics Visualization は AppExchange で配布されている未管理パッケージです。
https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000B5FMvEAN

コードを読んだり独自でカスタマイズを加えることができるので
ここから徐々に自分の欲しいデータが見られるよう修正を加え、データの扱いに慣れてきたら自社に合った分析ツールを自作すると良いと思います。

Sandbox でもMetricsDataFileは使えるが、データはコピーされない

MetricsDataFile がどの組織でも使えるものではない以上、開発はレポート組織のSandbox組織で行うことになると思います。
レポート組織でMetricsDataFileが有効になると、そのSandboxについてもメタデータにはアクセスできます。
ただ注意したいのが、フルSandboxであってもMetricsDataFileのレコードはコピーされない ということです。
(なんとかしてくれって IdeaExchange に投稿しました)

ですので、開発時は

  • 本番組織から1週間分ぐらいの MetricsDataFile をダウンロードする
  • 加工してテスト用のデータを作成し、Sandboxに静的リソースとしてアップロードする
  • コード中の SOQL を実行している箇所でフラグに応じて静的リソースのデータを使うよう修正する

みたいなことが必要になると思います。

例として、Usage Metrics Visualization で実際にSOQLを実行しているのは MDFQueryDAOImpl.cls というクラスですが、
これを書き換えるとこんな感じでしょうか。

MDFQueryDAOImpl.cls
public with sharing class MDFQueryDAOImpl implements MDFQueryDAO {

    private static final boolean IS_DEV = true;

    public List<MetricsDataFile> findMDFs(String sendingInstanceId, Date lookbackStartDate, Date lookbackEndDate) {
        if (IS_DEV) {
            return MDFMock.createMDFs(sendingInstanceId, lookbackStartDate, lookbackEndDate);
        } else {
            return [SELECT NamespacePrefix, MetricsDataFileLength, MetricsDataFile FROM MetricsDataFile
                        WHERE SendingInstance = :sendingInstanceId
                        AND MetricsStartDate >= :lookbackStartDate
                        AND MetricsStartDate <= :lookbackEndDate];
        }
    }
    // (略)
MDFMock.cls
public without sharing class MDFMock {

    public static List<MetricsDataFile> createMDFs(String sendingInstanceId, Date lookbackStartDate, Date lookbackEndDate) {
        List<MetricsDataFile> mdfList = new List<MetricsDataFIle>();
        // Custom Object
        List<StaticResource> srListCO = [SELECT Name, Body FROM StaticResource WHERE Name = 'MDFSampleCO' LIMIT 
1];
        // Visualforce
        List<StaticResource> srListVF = [SELECT Name, Body FROM StaticResource WHERE Name = 'MDFSampleVF' LIMIT 1];

        // --- 静的リソース -> MetricsDataFile への変換 ---

        return mdfList;
    }

with sharing だと MetricsDataFile にデータをセットできない

これもなにげにハマりポイントです。
たとえば、以下のVisualforceは表示時にエラーになります。

MDFError.page
<apex:page controller="MDFController">
</apex:page>
MDFController.cls
public with sharing class MDFController {

    public MDFController() {
        MetricsDataFile mdf = new MetricsDataFile();
        mdf.MetricsStartDate = Date.today();  // System.SObjectException: Field is not writeable
    }
}

正直 MetricsDataFile に関しては Daoクラスを with sharing にする意味があるのかもよくわかりませんが、
MDFQueryDAOImpl.cls を参考にするならば注意が必要です。

テストクラスでも MetricsDataFile の insert は不可

MetricsDataFile は読み取り専用なのでレコードを insert しようとするとエラーになります。
これはテストクラスにおいても同様です。
さすがにこれもどうにかならんのかということで IdeaExchange に投稿してます。
(こっちの方は多少Voteがあってうれしい)

ぶっちゃけ Usage Metrics Visualization の実運用には無理がある?

ここまで話しといてなんですが。
Usage Metrics Visualization がイケてないというより、Apex + Visualforce で実運用に耐える分析ツールを開発するのは難しいかも、という話です。

Usage Metrics Visualization を使ってみた印象ですが、ガバナ制限エラーが頻発します。
制限を超えてしまうのは CPU time であったりヒープサイズです。

参考までに、Winter'16 時点でのそれぞれの上限値は以下のようになっています。

Description Synchronous Limit Asynchronous Limit
Total heap size 6 MB 12 MB
Maximum CPU time on the Salesforce servers5 10,000 milliseconds 60,000 milliseconds

(https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm より引用)

それではどうすれば良いのかというと、MetricsDataFile はCSV部分だけ 日次で外部にエクスポートし、
分析ツールごと外部に構築してしまう
というのが現実的なんじゃないかなと思ってます。

幸い REST API からも MetricsDataFile にはアクセスできるので、REST API を定期的に叩いてデータをエクスポートするのがいいんではないでしょうか。
実はこれを私の好きなGoogle Apps Scriptで試してみようかなと思ったんですが今回は時間切れです。

Google Apps ScriptはOAuth2認証のためのライブラリがありますしクーロンを登録してメソッドを定期的に実行することもできますのでオススメです。
前にGitHubとOAuth2で連携するというのを試したことがあるんですが、認証まわりは同じノリでいけないかなーと思ってます。
[GoogleAppsScript]OAuth2でGitHubと連携し、issueをスプレッドシートに出力する - dackdive's blog

おわりに

というわけで、マイナーだけど気になる機能を知ってもらおうと思って書き始めたんですが
いかに使いづらいか を宣伝する結果になってしまった感があります。

ただ、冒頭でも書きましたが MetricsDataFile は有効化してもデータストレージを消費しません し、
紹介した Usage Metrics Visualization も カスタムオブジェクトを含まない ことを考えると
とりあえず入れてみてもいいのかなと思っています。

リファレンス

ISVforce ガイド(日本語版は最新がSummer'15)
https://developer.salesforce.com/docs/atlas.ja-jp.196.0.packagingGuide.meta/packagingGuide/usage_metrics_intro.htm

Summer'14 リリースノート
http://releasenotes.docs.salesforce.com/ja-jp/summer14/release-notes/rn_forcecom_isvforce_usage_metrics.htm