IoT Java SDKでのアプリケーションのインストルメンテーション

IoT Java SDK は、ゲートウェイ、POS、車のインフォテインメント センターなどのエッジデバイスで実行されている IoT Java アプリケーションに含めることができます。ここでは、Java SDK をインストールし、IoT アプリケーションをインストゥルメント化する方法について説明します。

EUM アプリケーションキーを取得し、IoT C/C++ アプリケーションをインストゥルメント化するには、次の手順を実行します。

注: この Java SDK は、よりローエンドデバイス向けに特別に設計された非常に軽量なライブラリである点で、Splunk AppDynamics Java エージェントとは異なります。また、イベント情報を拡張でき、インストルメンテーション コードを設定および制御できるように多くの柔軟性が組み込まれています。

要件の確認

開始する前に、次の要件を満たしていることを確認します。

  • デバイスが次のいずれかのバージョンの Java ランタイムを実行していること。

    • Java SE 7

    • Java SE Embedded 7 

    • Java SE 8

    • Java SE Embedded 8 

  • EUM サーバにビーコンを送信する HTTPS インターフェイス
  • EUM アプリケーションキー

IoT Java SDK の取得

Java SDK は、GitHub から IoT Java SDK を複製またはダウンロードすることによって取得できます。「Build the SDK」に記載されている手順に従って、IoT Java SDK をビルドします。

IntelliJ IDE を使用している場合は、「Working with module dependencies」に記載されている手順に従ってファイル をプロジェクトに追加します。IntelliJ プロジェクトの [] の下に JAR ファイルが表示されていることを確認します。

IoT Java SDK のアップグレード

GitHub から IoT Java SDK のクローンのルートディレクトリで、次のようにします。

  • 次のリポジトリを更新します。 $ git pull origin master
  • SDK の構築」に記載されている手順に従って、IoT Java SDK を再構築します。

Gradle 構成への SDK 依存関係の追加

build.gradle ファイルに以下の内容を追加します。

dependencies {
   runtime group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25'
   runtime group: 'com.google.guava', name:'guava', version:'18.0'
   runtime group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
 }

インストルメンテーション コードの追加

IoT SDK のインポート

アプリケーションファイルで、Java IoT SDK を含む import ステートメントを追加します。

import com.appdynamics.iot.Instrumentation;

IoT Java エージェントの構成

EUM アプリケーションキーと URL を EUM コレクタに提供することによってインストルメンテーションを構成します。EUM コレクタ URL が指定されていない場合は、デフォルトの SaaS コレクタ URL が使用されます。

注: 各地域の EUM コレクタ URL については、「Splunk AppDynamics SaaS ドメインと IP 範囲」を参照してください。EUM コレクタ URL が指定されていない場合は、デフォルトの SaaS コレクタ URL が使用されます。
import com.appdynamics.iot.AgentConfiguration;
AgentConfiguration.Builder agentConfigBuilder = AgentConfiguration.builder();
AgentConfiguration agentConfig = agentConfigBuilder
        .withAppKey(<EUM_APP_KEY>)
        .build();

デバイス情報の設定

デバイスの名前と ID を設定する必要があります。名前は、「EV Model 3」や「Thermostat Model Star7」など、デバイスのタイプとモデルを識別する短い文字列で構成されている必要があります。デバイス ID は、UUID、車の VIN 番号、またはデバイスの MAC アドレスなど、デバイスの一意の識別子である必要があります。

次のコード例では、デバイス ID をランダム UUID に、名前を「Smart Shelf」に設定しています。

import java.util.UUID;
import com.appdynamics.iot.DeviceInfo;
...
DeviceInfo.Builder deviceInfoBuilder = DeviceInfo.builder("Smart Shelf P1", UUID.randomUUID().toString());
DeviceInfo deviceInfo = deviceInfoBuilder.withDeviceName("Smart Shelf").build();

バージョン情報の設定

次に示すように、ファームウェア、ハードウェア、OS、およびソフトウェアのバージョンを設定できます。

import com.appdynamics.iot.VersionInfo;
...
VersionInfo.Builder versionInfoBuilder = VersionInfo.builder();
VersionInfo versionInfo = versionInfoBuilder
 .withFirmwareVersion("2.3.4")
 .withHardwareVersion("1.6.7")
 .withOsVersion("8.9.9")
 .withSoftwareVersion("3.1.1").build();

エージェントの初期化

エージェントを初期化するには、AgentConfiguration オブジェクト、DeviceInfo オブジェクト、および VersionInfo オブジェクトを start メソッドに渡します。

Instrumentation.start(agentConfig, deviceInfo, versionInfo);

Build and Run the Application

Use your favorite Java IDE or CLI environment to build and run the application. Note that the Splunk AppDynamics IoT Java SDK needs to be the in build and runtime classpath.

For instructions on adding libraries to the classpath:

For instructions to build and run the app:

Add and Send Events

The following sections will show you how to create and send the supported events: Custom, Network Request, and Error.

Create a Basic Custom Event

A custom event can be used to report any performance, device, or business logic data. It is the most general, configurable and flexible data type available.

The Custom Event Builder takes two required parameters.

  • Event Type: A short human-readable description of the event, such as "FL Pressure Drop".
  • Description: A string describing the event, such as "Front Left Tire Pressure Drop".

To make reporting this event meaningful, it is recommended that you provide a timestamp and at least one kind of datatype.

  1. Create a basic custom event.
    import com.appdynamics.iot.events.CustomEvent;
    ...
    CustomEvent.Builder builder = CustomEvent.builder("FL Pressure Drop", "Front Left Tire Pressure Drop");
    long eventStartTime = System.currentTimeMillis();
    long duration = 6000;
    builder.withTimestamp(eventStartTime).withDuration(duration);
    builder.addLongProperty("PSI Drop", 37);
    CustomEvent customEvent = builder.build();
    Additional information can be added to the CustomEvent. For details, see the CustomEvent class in the latest Java IoT SDK documentation.
  2. Add the custom event to the instrumentation (this adds it to the in-memory buffer).
    Instrumentation.addEvent(customEvent);
  3. Send all the events to the EUM Server. This is a blocking call, so the application can send it on a separate thread as shown above.
    Instrumentation.sendAllEvents();

Send a Network Event

  1. Report a Network Request Event using the HttpRequestTracker class. This call automatically adds an event to the in-memory buffer, so you need to explicitly import the class.
    import com.appdynamics.iot.HttpRequestTracker;
    ...        
    String url = "http://ip.jsontest.com/?callback=showMyIP";        
    // Add a Network Event
    try {
        URL thisUrl = new URL(url);
        // [Splunk AppDynamics Instrumentation] Get a Tracker            
        HttpURLConnection con = (HttpURLConnection) thisUrl.openConnection();            
        final HttpRequestTracker tracker = Instrumentation.beginHttpRequest(thisUrl);
        con.setRequestMethod("POST");
        con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");            
        int responseCode = con.getResponseCode();            
        con.setDoInput(true);
        con.setDoOutput(true);
        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.flush();
        wr.close();
        System.out.println("Response Code :" + responseCode);            
        // [Splunk AppDynamics Instrumentation] Retrieve the headers from the response
        Map<String, List<String>> headerFields = null;
        System.out.println("Sending 'POST' request to URL :" + url);
        BufferedReader in;
        String inputLine;
        new InputStreamReader(con.getErrorStream()));
        if (responseCode >= 200 && responseCode < 300) {
            in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        } else {
            in = new BufferedReader(
        }
        StringBuffer response = new StringBuffer();
        if (headerFields != null && headerFields.size() > 0){
            while ((inputLine = in.readLine()) != null) {
               response.append(inputLine);
            }
            in.close();
            // [Splunk AppDynamics Instrumentation] Initiate adding NetworkRequestEvent
            if (responseCode >= 200 && responseCode < 300) {
                tracker.withResponseCode(responseCode).withError(response.toString()).reportDone();
                       .withResponseHeaderFields(headerFields)                    
                       .reportDone();
            } else {
                tracker.withResponseCode(responseCode).reportDone();
            }
        } else {
            tracker.withResponseCode(responseCode)
        } 
    // End: Add for Splunk AppDynamics Instrumentation - Initiate adding NetworkRequestEvent
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (Exception ex) {            
        ex.printStackTrace();
    }
  2. Send all the events to the EUM Server. This is a blocking call. The application can send it on a separate thread. The recommendation is to batch a number of events together before calling the sendAllEvents method.
    Instrumentation.sendAllEvents();

Send an Error Event

  1. Report an Error Event using the API.
    try {
        //Force creating an exception
        float f = (5 / 0);   
    } catch (Throwable t) {
        Instrumentation.addErrorEvent(t, Instrumentation.Severity.ALERT);
    }
  2. Send all the events to the EUM Server. This is a blocking call. The application can send it on a separate thread.
    Instrumentation.sendAllEvents();

ビジネストランザクションをネットワークリクエストと関連付ける(オプション)

ビジネストランザクション(BT)をネットワークリクエストと関連付けるには、ビジネスアプリケーションをインストゥルメント化し、コントローラ UI でビジネストランザクションを有効にしておく必要があります。IoT モニタリング用のビジネストランザクションの相関 IoT モニタリング用のビジネストランザクションの相関

次の手順では、BT レスポンスヘッダーを取得し、それらを使用して、その BT を IoT ネットワーク リクエスト イベントと関連付ける方法について説明します。

  1. Splunk AppDynamics HTTP リクエストヘッダー ADRUMADRUM_1 を含むネットワークリクエストをビジネスアプリケーションのいずれかに実行します。
    import com.appdynamics.iot.HttpRequestTracker;
    ...    
    // Create a network request to the business app.  
    String url = "<url_to_business_application>";        
    URL thisUrl = new URL(url);
    [AppDynamics Instrumentation] Get a Tracker            
    HttpURLConnection con = (HttpURLConnection) thisUrl.openConnection();            
    final HttpRequestTracker tracker = Instrumentation.beginHttpRequest(thisUrl);
    con.setRequestMethod("POST"); // Some HTTP method: GET, POST, PUT...
     
    // Add the AppDynamics HTTP headers ADRUM and ADRUM_1 to the request.
    con.setRequestProperty("ADRUM", "isAjax:true");
    con.setRequestProperty("ADRUM_1", "isMobile:true");
     
    // Make the request to your business app.
    con.setDoInput(true);
  2. コールは、関連するビジネストランザクションの情報を含むレスポンスヘッダーを返します。これらの BT レスポンスヘッダーを出力する場合は、次のように表示されます。
    {
      ADRUM_1=[globalAccountName:customer1_78203698-278e-428f-8726-bb381219c6cb], 
      null=[HTTP/1.1 200 OK], 
      ADRUM_0=[clientRequestGUID:2ff45113-6746-4c94-b6d0-4af26055613c], 
      ADRUM_3=[btERT:269], 
      ADRUM_2=[btId:4423], 
      Server=[Jetty(9.4.z-SNAPSHOT)], 
      ADRUM_5=[btDuration:327], 
      ADRUM_4=[serverSnapshotType:f], 
      Content-Length=[514], 
    }
  3. BT レスポンスヘッダーを含むビーコンを EUM サーバに送信します。
    // Fetch the response headers, which will include the BT headers (ADRUM_0, ADRUM_1, ...).
    Map<String, List<String>> headerFields = con.getHeaderFields();
     
    // Add the BT response headers to the request body of the Network Request event.
    // that you're reporting.
    tracker.withResponseCode(responseCode).withError(response.toString()).reportDone();
                       .withResponseHeaderFields(headerFields)                    
                       .reportDone();
     
    // Report the Network Request event to the EUM Server.
    Instrumentation.sendAllEvents();
  4. コントローラ UI では、[Device Details] ダイアログで関連するビジネストランザクションを確認できます。

SDK のロギングの有効化(オプション)

IoT Java SDK は、ロギングフレームワークとして Java(SLF4J)用シンプルロギングファサードを使用します。SLF4J と互換性のあるお気に入りのロギングエンジンを使用できます。

クラスパスでバインドが見つからない場合、SLF4J はデフォルトで非動作の実装になり、次のようなコンソールメッセージが表示されます。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation //
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

java.util.logging エンジンを使用するには、build.gradle ファイルに次の行を追加します。

dependencies {
   ....
   runtime group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.25'
   ....
 }

ライブラリからのすべてのデバッグメッセージを表示するには、次の行を /Library/Java/JavaVirtualMachines/<your-jdk-version>/Contents/Home/jre/lib/logging.propertiesファイルの最後に追加します。

com.appdynamics.iot.level = FINEST

step8IoT Java インストルメンテーションのカスタマイズ(オプション)

 IoT Java SDK を使用して、IoT Java インストルメンテーションをさらにカスタマイズできます。最新の IoT Java SDK ドキュメント、または以下に記載されている以前のバージョンを参照してください。

Run the Sample Java Application

The sample Java application sends sample data for Custom, Network Request, and Error events. The data mocks a smart car application, capturing usage information, network performance, and errors.

To run the sample app, follow the Getting Started instruction given in the iot-java-sdk GitHub repository.

IoT Java SDK のトラブルシューティング

ここでは、一般的な問題をデバッグする手順について説明します。

IoT Java エージェントをリンクできません

IoT Java エージェントをリンクしようとしたときに次のエラーが発生した場合は、 での依存関係が原因です。

loader constraint violation: when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader (instance of com/intellij/ide/plugins/cl/PluginClassLoader) of the current class, org/slf4j/LoggerFactory, and the class loader (instance of com/intellij/util/lang/UrlClassLoader) for the method's defining class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type org/slf4j/ILoggerFactory used in the signature
java.lang.LinkageError: loader constraint violation: when resolving method "org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader (instance of com/intellij/ide/plugins/cl/PluginClassLoader) of the current class, org/slf4j/LoggerFactory, and the class loader (instance of com/intellij/util/lang/UrlClassLoader) for the method's defining class, org/slf4j/impl/StaticLoggerBinder, have different Class objects for the type org/slf4j/ILoggerFactory used in the signature
	at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:273)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:241)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:254)
	at com.appdynamics.iot.Instrumentation.<clinit>(Instrumentation.java:39)
	...

この問題を修正するには、ロギングを有効にするために追加した依存関係を削除する必要があります。したがって、次に示すグループ  を指定する行を から削除します。

dependencies {
   ....
   runtime group: 'org.slf4j', name: 'slf4j-jdk14', version: '1.7.25'
   ....
}