このトピックでは、Simple Log Service (SLS) Android SDK を使用してログを収集する方法について説明します。
前提条件
クイックスタート
次の例は、SDK を初期化し、addLog メソッドを呼び出してログを送信する方法を示しています。
SDK は複数のインスタンスの初期化をサポートしています。各
LogProducerConfigインスタンスは、対応するLogProducerClientインスタンスとペアにする必要があります。SLS にログを送信する場合、認証と改ざん防止のために、Alibaba Cloud アカウントまたは Resource Access Management (RAM) ユーザーの AccessKey を使用する必要があります。モバイルアプリケーションに AccessKey を保存することによるセキュリティリスクを防ぐために、モバイルログ直接アップロードサービスを使用して AccessKey を設定することをお勧めします。詳細については、「ログの収集 - モバイルログ直接アップロードサービスの構築」をご参照ください。
// LogProducerConfig および LogProducerClient インスタンスをグローバルに保存することをお勧めします。
private LogProducerConfig config = null;
private LogProducerClient client = null;
/**
* SDK を初期化します。
*/
private void initProducer() {
try {
// SLS のサービスエンドポイント。エンドポイントは https:// または http:// で始まる必要があります。
final String endpoint = "your endpoint";
final String project = "your project";
final String logstore = "your logstore";
config = new LogProducerConfig(context, endpoint, project, logstore);
// ログのトピックを設定します。
config.setTopic("example_topic");
// タグ情報を設定します。このタグは各ログに追加されます。
config.addTag("example", "example_tag");
// 期限切れのログを破棄するかどうかを指定します。0: ログを破棄しません。ログ時間は現在の時刻に変更されます。1: ログを破棄します。デフォルト値: 1。
config.setDropDelayLog(0);
// 認証に失敗したログを破棄するかどうかを指定します。0: ログを破棄しません。1: ログを破棄します。デフォルト値: 0。
config.setDropUnauthorizedLog(0);
// LogProducerCallback はオプションの構成です。ログ送信の成功または失敗のステータスを追跡する必要がない場合は、コールバックを登録する必要はありません。
// AccessKey を動的に設定するには、LogProducerCallback を設定し、onCall メソッドが呼び出されたときに AccessKey を更新します。
final LogProducerCallback callback = new LogProducerCallback() {
@Override
public void onCall(int resultCode, String reqId, String errorMessage, int logBytes, int compressedBytes) {
// resultCode: 状態コード。詳細については、「エラーコード」をご参照ください。
// reqId: リクエスト ID。このパラメーターは非推奨です。
// errorMessage: 失敗情報。
// logBytes: 元のログサイズ (バイト単位)。
// compressedBytes: 圧縮後のログサイズ (バイト単位)。
final LogProducerResult result = LogProducerResult.fromInt(resultCode);
if (LogProducerResult.LOG_PRODUCER_SEND_UNAUTHORIZED == result || LogProducerResult.LOG_PRODUCER_PARAMETERS_INVALID == result) {
// AccessKey または SDK の初期化パラメーターを更新します。
}
}
};
// ログ送信の成功または失敗のステータスを追跡するには、2 番目のパラメーターとしてコールバックを渡す必要があります。
client = new LogProducerClient(config, callback);
} catch (LogProducerException e) {
e.printStackTrace();
}
}
// AccessKey 情報をリクエストします。
private void requestAccessKey() {
// モバイルログ直接アップロードサービスを使用して AccessKey 情報を設定することをお勧めします。
// ...
// AccessKey 情報を取得したら、更新します。
updateAccessKey(accessKeyId, accessKeySecret, securityToken);
}
// AccessKey 情報を更新します。
private void updateAccessKey(String accessKeyId, String accessKeySecret, String securityToken) {
// Security Token Service (STS) から取得した AccessKey には securityToken が含まれています。次の方法で AccessKey を更新する必要があります。
if (null != securityToken && !"".equals(securityToken)) {
config.resetSecurityToken(accessKeyId, accessKeySecret, securityToken);
} else {
// AccessKey が STS から取得されていない場合は、次の方法で AccessKey を更新します。
config.setAccessKeyId(accessKeyId);
config.setAccessKeySecret(accessKeySecret);
}
}
/**
* ログをレポートします。
*/
public void addLog() {
Log log = new Log();
// 必要に応じてレポートするフィールドを調整します。
log.putContent("content_key_1", 123456);
log.putContent("content_key_2", 23.34f);
log.putContent("content_key_3", "Chinese️");
log.putContent(null, "null");
log.putContent("null", (String) null);
client.addLog(log);
}高度な機能
パラメーターの動的構成
Android SDK は、Endpoint、ProjectName、logstore、AccessKey などのパラメーターの動的構成をサポートしています。
Endpoint、ProjectName、logstore を動的に構成します。
// Endpoint、ProjectName、logstore などのパラメーターをまとめて、または個別に構成します。 // エンドポイントを更新します。 config.setEndpoint("your new-endpoint"); // プロジェクト名を更新します。 config.setProject("your new-project"); // logstore 名を更新します。 config.setLogstore("your new-logstore");AccessKey を動的に構成します。
AccessKey を動的に構成する場合は、LogProducerCallback と一緒に使用することをお勧めします。
// LogProducerClient を初期化したときに LogProducerCallback を初期化している場合は、次のコードを無視してください。 final LogProducerCallback callback = new LogProducerCallback() { @Override public void onCall(int resultCode, String reqId, String errorMessage, int logBytes, int compressedBytes) { // resultCode: 状態コード。詳細については、「エラーコード」をご参照ください。 // reqId: リクエスト ID。このパラメーターは非推奨です。 // errorMessage: 失敗情報。 // logBytes: 元のログサイズ (バイト単位)。 // compressedBytes: 圧縮後のログサイズ (バイト単位)。 final LogProducerResult result = LogProducerResult.fromInt(resultCode); if (LogProducerResult.LOG_PRODUCER_SEND_UNAUTHORIZED == result || LogProducerResult.LOG_PRODUCER_PARAMETERS_INVALID == result) { // AccessKey または SDK の初期化パラメーターを更新する必要があります。 requestAccessKey(); } } }; // ログ送信の成功または失敗のステータスを追跡するには、2 番目のパラメーターとしてコールバックを渡す必要があります。 client = new LogProducerClient(config, callback); // AccessKey 情報をリクエストします。 private void requestAccessKey() { // モバイルログ直接アップロードサービスを使用して AccessKey 情報を設定することをお勧めします。 // ... // AccessKey 情報を取得したら、更新します。 updateAccessKey(accessKeyId, accessKeySecret, securityToken); } // AccessKey 情報を更新します。 private void updateAccessKey(String accessKeyId, String accessKeySecret, String securityToken) { // STS から取得した AccessKey には securityToken が含まれています。次の方法で AccessKey を更新する必要があります。 if (null != securityToken && !"".equals(securityToken)) { config.resetSecurityToken(accessKeyId, accessKeySecret, securityToken); } else { // AccessKey が STS から取得されていない場合は、次の方法で AccessKey を更新します。 config.setAccessKeyId(accessKeyId); config.setAccessKeySecret(accessKeySecret); } }source、topic、tag を動的に構成します。
重要特定の種類のログに対して source、topic、または tag を設定することはできません。これらのパラメーターを設定すると、SLS への送信を待機しているすべてのログに適用されます。つまり、source、topic、tag を使用して特定のログの種類を追跡すると、結果が不正確になる可能性があります。ログを生成するときにフィールドを追加して、対応するログの種類を識別することをお勧めします。
// ログソースを設定します。 config.setSource("your new-source"); // ログのトピックを設定します。 config.setTopic("your new-topic"); // タグ情報を設定します。このタグは各ログに追加されます。 config.addTag("test", "your new-tag");
再開可能なアップロード
Android SDK は再開可能なアップロードをサポートしています。この機能を有効にすると、addLog メソッドを使用して追加されたログはローカルの binlog ファイルに保存されます。このローカルデータは、ログが正常に送信された後にのみ削除されます。これにより、ログが少なくとも 1 回はアップロードされることが保証されます。
SDK の初期化中に次のコードを追加して、再開可能なアップロードを有効にします。
複数の LogProducerConfig インスタンスを初期化する場合、
LogProducerConfigクラスのsetPersistentFilePathメソッドは、インスタンスごとに一意の値に設定する必要があります。アプリに複数のプロセスがあり、再開可能なアップロード機能が有効になっている場合は、メインプロセスでのみ SDK を初期化する必要があります。子プロセスもデータを収集する必要がある場合は、
setPersistentFilePathメソッドで指定されたファイルパスが各プロセスで一意であることを確認する必要があります。そうしないと、ログデータが失われたり破損したりする可能性があります。SDK を使用するときは、複数のスレッドで LogProducerConfig を再初期化しないでください。
private void initProducer() {
// 1: 再開可能なアップロード機能を有効にします。0: 機能を無効にします。デフォルト値: 0。
config.setPersistent(1);
// 永続化ファイルの名前。ファイルが配置されているフォルダが作成されていることを確認してください。
final String persistentFilePath = getFilesDir() + File.separator + "log_data";
config.setPersistentFilePath(persistentFilePath);
// スクロールする永続化ファイルの数。このパラメーターを 10 に設定することをお勧めします。
config.setPersistentMaxFileCount(10);
// 各永続化ファイルのサイズ。単位: バイト。フォーマットは N*1024*1024 です。N を 1 から 10 の値に設定することをお勧めします。
config.setPersistentMaxFileSize(N*1024*1024);
// ローカルにキャッシュできるログの最大数。このパラメーターを 1,048,576 より大きい値に設定しないことをお勧めします。デフォルト値: 65,536。
config.setPersistentMaxLogCount(65536);
}難読化ルール
-keep class com.aliyun.sls.android.producer.** { *; }
-keep interface com.aliyun.sls.android.producer.** { *; }Android の権限
<uses-permission android:name="android.permission.INTERNET" />構成パラメーター
すべての構成パラメーターは LogProducerConfig クラスによって提供されます。次の表にパラメーターを示します。
パラメーター | データ型 | 説明 |
setTopic | String | topic フィールドの値を設定します。デフォルト値は空の文字列です。 |
addTag | String | タグを設定します。フォーマットは |
setSource | String | source フィールドの値を設定します。デフォルト値は Android です。 |
setPacketLogBytes | Int | キャッシュされた各ログパッケージの最大サイズ。サイズが制限を超えると、ログはすぐに送信されます。 有効な値: 1~5,242,880。デフォルト値: 1024 × 1024。単位: バイト。 |
setPacketLogCount | Int | キャッシュされた各ログパッケージ内のログの最大数。数が制限を超えると、ログはすぐに送信されます。 有効な値: 1~4,096。デフォルト値: 1,024。 |
setPacketTimeout | Int | キャッシュされたログを送信するためのタイムアウト期間。キャッシュがタイムアウトすると、ログはすぐに送信されます。 デフォルト値: 3,000。単位: ミリ秒。 |
setMaxBufferLimit | Int | 単一の Producer Client インスタンスが使用できる最大メモリ。キャッシュサイズが制限を超えると、add_log インターフェイスはすぐに失敗を返します。 デフォルト値: 64 × 1024 × 1024。 |
setSendThreadCount | Int | 送信スレッドの数。再開可能なアップロードが有効になっている場合、このパラメーターは強制的に 1 に設定されます。 |
setPersistent | Int | 再開可能なアップロード機能を有効にするかどうかを指定します。
|
setPersistentFilePath | String | 永続化ファイルの名前。ファイルが配置されているフォルダが作成されていることを確認してください。複数の LogProducerConfig インスタンスを構成する場合は、名前が一意であることを確認してください。 デフォルト値は空です。 |
setPersistentForceFlush | Int | AddLog が呼び出されるたびにログを強制的にフラッシュする機能を有効にするかどうかを指定します。
信頼性の高いシナリオでこの機能を有効にすることをお勧めします。 |
setPersistentMaxFileCount | Int | スクロールする永続化ファイルの数。このパラメーターを 10 に設定することをお勧めします。デフォルト値: 0。 |
setPersistentMaxFileSize | Int | 各永続化ファイルのサイズ。単位: バイト。フォーマットは N × 1024 × 1024 です。N を 1 から 10 の値に設定することをお勧めします。 |
setPersistentMaxLogCount | Int | ローカルにキャッシュできるログの最大数。このパラメーターを 1,048,576 より大きい値に設定しないことをお勧めします。デフォルト値: 65,536。 |
setConnectTimeoutSec | Int | ネットワーク接続のタイムアウト期間。デフォルト値: 10。単位: 秒。 |
setSendTimeoutSec | Int | ログ送信のタイムアウト期間。デフォルト値: 15。単位: 秒。 |
setDestroyFlusherWaitSec | Int | フラッシャースレッドを破棄するための最大待機時間。デフォルト値: 1。単位: 秒。 |
setDestroySenderWaitSec | Int | 送信者スレッドプールを破棄するための最大待機時間。デフォルト値: 1。単位: 秒。 |
setCompressType | Int | データアップロードの圧縮タイプ。
|
setNtpTimeOffset | Int | デバイス時間と標準時間の差。値は `標準時間 - デバイス時間` です。この差は通常、ユーザーのクライアントデバイスの時間が同期されていない場合に発生します。デフォルト値: 0。単位: 秒。 |
setMaxLogDelayTime | Int | ログ時間とローカル時間の差。差が指定された値を超えると、SDK は setDropDelayLog オプションに基づいてログを処理します。単位: 秒。デフォルト値: 7,243,600 (7 日間)。 |
setDropDelayLog | Int | setMaxLogDelayTime を超える期限切れのログを破棄するかどうかを指定します。
|
setDropUnauthorizedLog | Int | 認証に失敗したログを破棄するかどうかを指定します。
|
setCallbackFromSenderThread | Boolean | 送信者スレッドからコールバックするかどうかを指定します。
|
エラーコード
すべてのエラーコードは LogProducerResult 列挙型で定義されています。次の表にエラーコードを示します。
エラーコード | 値 | 説明 | ソリューション |
LOG_PRODUCER_OK | 0 | 成功。 | 該当なし。 |
LOG_PRODUCER_INVALID | 1 | SDK が破棄されたか、無効です。 |
|
LOG_PRODUCER_WRITE_ERROR | 2 | データ書き込みエラーが発生しました。考えられる原因は、プロジェクトの書き込みトラフィックが上限に達したことです。 | プロジェクトの書き込みトラフィックの上限を調整します。詳細については、「リソースクォータの調整」をご参照ください。 |
LOG_PRODUCER_DROP_ERROR | 3 | ディスクまたはメモリキャッシュがいっぱいで、ログを書き込めません。 | maxBufferLimit、persistentMaxLogCount、persistentMaxFileSize パラメーターの値を調整して再試行します。 |
LOG_PRODUCER_SEND_NETWORK_ERROR | 4 | ネットワークエラーが発生しました。 | Endpoint、Project、logstore の構成を確認します。 |
LOG_PRODUCER_SEND_QUOTA_ERROR | 5 | プロジェクトの書き込みトラフィックが上限に達しました。 | プロジェクトの書き込みトラフィックの上限を調整します。詳細については、「リソースクォータの調整」をご参照ください。 |
LOG_PRODUCER_SEND_UNAUTHORIZED | 6 | AccessKey が期限切れか無効であるか、AccessKey アクセスポリシーが正しく構成されていません。 | AccessKey を確認します。 RAM ユーザーは SLS リソースを操作する権限を持っている必要があります。詳細については、「RAM ユーザーに権限を付与する」をご参照ください。 |
LOG_PRODUCER_SEND_SERVER_ERROR | 7 | サービスエラーが発生しました。 | チケットを送信してテクニカルサポートにお問い合わせください。 |
LOG_PRODUCER_SEND_DISCARD_ERROR | 8 | データは破棄されます。これは通常、デバイスの時刻がサーバーの時刻と同期していないために発生します。 | SDK はデータを自動的に再送信します。 |
LOG_PRODUCER_SEND_TIME_ERROR | 9 | 時刻がサーバーの時刻と同期していません。 | SDK はこの問題を自動的に修正します。 |
LOG_PRODUCER_SEND_EXIT_BUFFERED | 10 | SDK が破棄されると、キャッシュされたデータはレポートされません。 | データ損失を防ぐために、再開可能なアップロード機能を有効にすることをお勧めします。 |
LOG_PRODUCER_PARAMETERS_INVALID | 11 | SDK の初期化パラメーターが無効です。 | AccessKey、Endpoint、Project、logstore などのパラメーターの構成を確認します。 |
LOG_PRODUCER_PERSISTENT_ERROR | 99 | キャッシュされたデータのディスクへの書き込みに失敗しました。 | 1. キャッシュファイルのパスが正しく構成されているか確認します。 2. キャッシュファイルがいっぱいかどうか確認します。 3. システムディスクの空き容量が十分かどうか確認します。 |
よくある質問
ログが重複するのはなぜですか?
Android SDK は非同期でログを送信します。ネットワークの状態によっては、ログの送信に失敗し、再送信されることがあります。SDK は、200 の状態コードが返された場合にのみ送信操作が成功したと見なします。したがって、一部のログが重複する可能性があります。SQL クエリと分析ステートメントを使用してログの重複を排除することをお勧めします。
ログの重複率が高い場合は、まず SDK の初期化に問題がないか確認する必要があります。主な原因と解決策は次のとおりです。
再開可能なアップロードの構成が正しくない
再開可能なアップロード機能が正しく構成されていない場合は、
setPersistentFilePathメソッドに渡されるファイルパスがグローバルに一意であることを確認してください。SDK の初期化の繰り返し
SDK の初期化が繰り返される一般的な原因は、SDK の初期化のためのシングルトンパターンの実装が正しくないことです。SDK を初期化するには、次のメソッドを使用することをお勧めします。
public class AliyunLogHelper { private LogProducerConfig config; private LogProducerClient client; private static class Holder { private static final AliyunLogHelper INSTANCE = new AliyunLogHelper(); } public static AliyunLogHelper getInstance() { return Holder.INSTANCE; } private AliyunLogHelper() { initProducer(); } private void initProducer() { // 次のコードを初期化コードに置き換えます。 try { config = new LogProducerConfig(); client = new LogProducerClient(config); } catch (LogProducerException e) { e.printStackTrace(); } } public void addLog(Log log) { if (null == client) { return; } client.addLog(log); } }SDK の初期化が繰り返されるもう 1 つの原因は、複数のプロセスを使用することです。SDK はメインプロセスでのみ初期化することをお勧めします。または、異なるプロセスで SDK を初期化する場合は、各プロセスで
setPersistentFilePathに一意の値を設定する必要があります。
低速ネットワーク環境向けの構成の最適化
アプリケーションが低速ネットワーク環境で実行される場合は、次の例に示すように SDK 構成パラメーターを最適化することをお勧めします。
// SDK を初期化します。 private void initProducer() { // HTTP 接続と送信タイムアウト期間を調整して、ログの重複率を減らします。 // 必要に応じて特定のタイムアウト期間を調整します。 config.setConnectTimeoutSec(20); config.setSendTimeoutSec(20); // その他の初期化パラメーター。 // ... }
ログが欠落している場合はどうすればよいですか?
ログの送信プロセスは非同期です。ログが送信される前にアプリが閉じられると、未送信のログが失われる可能性があります。再開可能なアップロード機能を有効にすることをお勧めします。詳細については、「再開可能なアップロード」をご参照ください。
ログのレポートが遅延する場合はどうすればよいですか?
SDK は非同期でログを送信します。ネットワークの状態や特定のアプリケーションシナリオによっては、ログがすぐに送信されない場合があります。少数のデバイスでの遅延は正常です。遅延が広範囲にわたる場合は、次のエラーコードを確認してください。
エラーコード | 説明 |
LOG_PRODUCER_SEND_NETWORK_ERROR | Endpoint、Project、logstore の構成が正しいかどうかを確認します。 |
LOG_PRODUCER_SEND_UNAUTHORIZED | AccessKey が期限切れか無効であるか、または AccessKey アクセスポリシーが正しく構成されているかどうかを確認します。 |
LOG_PRODUCER_SEND_QUOTA_ERROR | プロジェクトの書き込みトラフィックが上限に達しました。詳細については、「リソースクォータの調整」をご参照ください。 |
Android SDK は DNS 事前解析とキャッシュポリシーをサポートしていますか?
次の例は、Android SDK、HTTPDNS SDK、および OkHttp を使用して DNS 事前解決とキャッシュポリシーを実装する方法を示しています。
カスタム DNS インターフェイスを実装します。
/** * OkHttp Dns インターフェイスに基づいてカスタム DNS 解析サービスを実装します (参考用)。 */ public class OkHttpDns implements Dns { private Context mContext; public OkHttpDns(Context context) { mContext = context; } @Override public List<InetAddress> lookup(String host) throws UnknownHostException { // !!注意!! // accountId を HTTPDNS サービスから取得した値に置き換えます。詳細については、HTTPDNS ドキュメントをご参照ください。 HTTPDNSResult httpdnsResult = HttpDns.getService(mContext, accountId) .getHttpDnsResultForHostSync(host, RequestIpType.both); List<InetAddress> inetAddresses = new ArrayList<>(); InetAddress address; try { if (httpdnsResult.getIps() != null) { // IPv4 アドレスを処理します。 for (String ipv4 : httpdnsResult.getIps()) { address = InetAddress.getByName(ipv4); inetAddresses.add(address); } } if (httpdnsResult.getIpv6s() != null) { // IPv6 アドレスを処理します。 for (String ipv6 : httpdnsResult.getIpv6s()) { address = InetAddress.getByName(ipv6); inetAddresses.add(address); } } } catch (UnknownHostException e) { } if (!inetAddresses.isEmpty()) { return inetAddresses; } return okhttp3.Dns.SYSTEM.lookup(host); } }カスタム NetworkInterface を実装します。
/** * OkHttp に基づいて SLS SDK の NetworkInterface を実装します (参考用)。 */ private static class OkHttpNetworkInterface implements LogProducerHttpTool.NetworkInterface { private OkHttpClient client; public OkHttpNetworkInterface(Context context) { client = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .dns(new OkHttpDns(context)) .build(); } @Override public NetworkResponse send(String urlString, String method, String[] header, byte[] body) { // まず SDK の元の実装を呼び出し、ログのレポートが失敗したときに OkHttp を使用してログをレポートすることをお勧めします。 LogHttpResponse logHttpResponse = LogProducerHttpTool.android_http_post(urlString, method, header, body); if (logHttpResponse.getStatusCode() != -1) { NetworkResponse response = new NetworkResponse(); response.statusCode = logHttpResponse.getStatusCode(); response.errorMessage = logHttpResponse.getErrorMessage(); return response; } // OkHttp を使用してログをレポートします。 Request.Builder builder = new Request.Builder(); builder.url(urlString); builder.post(RequestBody.create(body)); for (int i = 0; i < header.length; i += 2) { builder.addHeader(header[i], header[i + 1]); } // リクエストが成功したかどうかに関係なく、NetworkResponse を返す必要があります。そうしないと、SDK が期待どおりに実行されない可能性があります。 try (okhttp3.Response response = client.newCall(builder.build()).execute()) { NetworkResponse httpResponse = new NetworkResponse(); httpResponse.statusCode = response.code(); httpResponse.headers = response.headers().toMultimap(); httpResponse.errorMessage = response.message(); return httpResponse; } catch (IOException e) { NetworkResponse httpResponse = new NetworkResponse(); httpResponse.statusCode = -1; httpResponse.headers = null; httpResponse.errorMessage = e.getLocalizedMessage(); return httpResponse; } } }SLS SDK を初期化します。
public class AliyunSLS { private void initSLS(Context context) { // SLS SDK のカスタム NetworkInterface を設定します。 // !!!注意!!! // この設定は、すべての SLS SDK インスタンスに適用されます。 LogProducerHttpTool.setNetworkInterface(new OkHttpNetworkInterface(context)); // SLS SDK を初期化します。 initProducer(); } private void initProducer() { // ここで LogProducerConfig と LogProducerClient の初期化を実装します。 // ... } }