Simple Log Service (SLS) Android SDK を使用して、Android アプリケーションからログを収集してアップロードします。
前提条件
クイックスタート
SDK を初期化し、addLog メソッドを呼び出してログを送信します。
-
SDK は複数のインスタンスをサポートしています。各
LogProducerConfigインスタンスは、対応するLogProducerClientインスタンスとペアにする必要があります。 -
SLS にログを送信するには、認証および改ざん防止のために、Alibaba Cloud アカウントまたは 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 にはセキュリティトークンが含まれます。次の方法で 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 は、エンドポイント、プロジェクト名、Logstore、AccessKey などのパラメーターの動的設定をサポートしています。
-
エンドポイント、プロジェクト名、Logstore を動的に設定します。
// エンドポイント、プロジェクト名、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); } } -
ソース、トピック、タグを動的に設定します。
重要特定のログタイプに対してソース、トピック、またはタグを設定することはできません。これらのパラメーターは、Simple Log Service (SLS) に送信待ちのすべてのログに適用されます。ソース、トピック、タグを使用して特定のログタイプを追跡する場合、結果が不正確になる可能性があります。ログタイプを識別するために、ログ生成時にフィールドを追加することを推奨します。
// ログソースを設定します。 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 |
文字列 |
トピックフィールドの値を設定します。デフォルト値は空の文字列です。 |
|
addTag |
文字列 |
タグを設定します。形式は |
|
setSource |
文字列 |
ソースフィールドの値を設定します。デフォルト値は Android です。 |
|
setPacketLogBytes |
整数 |
キャッシュされる各ログパッケージの最大サイズです。この上限を超えると、ログは直ちに送信されます。 有効な値:1~5,242,880。デフォルト値:1024 × 1024。単位:バイト。 |
|
setPacketLogCount |
整数 |
キャッシュされる各ログパッケージ内のログの最大数です。この上限を超えると、ログは直ちに送信されます。 有効な値:1~4,096。デフォルト値:1,024。 |
|
setPacketTimeout |
整数 |
キャッシュされるログのタイムアウト期間です。このタイムアウトが経過すると、ログは直ちに送信されます。 デフォルト値:3,000。単位:ミリ秒。 |
|
setMaxBufferLimit |
整数 |
単一の Producer Client インスタンスが使用できる最大メモリサイズです。この上限を超えると、add_log インターフェイスは直ちにエラーを返します。 デフォルト値:64 × 1024 × 1024。 |
|
setSendThreadCount |
整数 |
送信スレッドの数です。レジューム可能なアップロードが有効な場合、このパラメーターは強制的に 1 に設定されます。 |
|
setPersistent |
整数 |
レジューム可能なアップロード機能を有効にするかどうかを指定します。
|
|
setPersistentFilePath |
文字列 |
永続化ファイルの名前です。親フォルダーが存在することを確認してください。複数の LogProducerConfig インスタンスを設定する場合、各名前は一意にする必要があります。 デフォルト値は空です。 |
|
setPersistentForceFlush |
整数 |
AddLog が呼び出されるたびにログをディスクにフラッシュするかどうかを指定します。
高信頼性のシナリオでは、この機能を有効にしてください。 |
|
setPersistentMaxFileCount |
整数 |
ローリング永続化ファイルの数です。このパラメーターを 10 に設定することを推奨します。デフォルト値:0。 |
|
setPersistentMaxFileSize |
整数 |
各永続化ファイルのサイズです。単位:バイト。形式は N × 1024 × 1024 です。N を 1~10 の値に設定することを推奨します。 |
|
setPersistentMaxLogCount |
整数 |
ローカルにキャッシュできるログの最大数です。このパラメーターを 1,048,576 より大きい値に設定しないことを推奨します。デフォルト値:65,536。 |
|
setConnectTimeoutSec |
整数 |
ネットワーク接続のタイムアウト期間です。デフォルト値:10。単位:秒。 |
|
setSendTimeoutSec |
整数 |
ログ送信のタイムアウト期間です。デフォルト値:15。単位:秒。 |
|
setDestroyFlusherWaitSec |
整数 |
フラッシャースレッドを破棄する際の最大待機時間です。デフォルト値:1。単位:秒。 |
|
setDestroySenderWaitSec |
整数 |
送信スレッドプールを破棄する際の最大待機時間です。デフォルト値:1。単位:秒。 |
|
setCompressType |
整数 |
データアップロードの圧縮タイプです。
|
|
setNtpTimeOffset |
整数 |
デバイス時刻と標準時の差です。値は |
|
setMaxLogDelayTime |
整数 |
ログ時刻とローカル時刻の許容される最大差です。この値を超える場合、SDK は setDropDelayLog 設定に基づいてログを処理します。単位:秒。デフォルト値:604,800 (7 日間)。 |
|
setDropDelayLog |
整数 |
setMaxLogDelayTime を超える期限切れのログを破棄するかどうかを指定します。
|
|
setDropUnauthorizedLog |
整数 |
認証に失敗したログを破棄するかどうかを指定します。
|
|
setCallbackFromSenderThread |
ブール値 |
送信スレッドからコールバックするかどうかを指定します。
|
エラーコード
すべてのエラーコードは LogProducerResult 列挙型で定義されています。
|
エラーコード |
値 |
説明 |
解決策 |
|
LOG_PRODUCER_OK |
0 |
成功。 |
該当なし。 |
|
LOG_PRODUCER_INVALID |
1 |
SDK が破棄されたか、無効です。 |
|
|
LOG_PRODUCER_WRITE_ERROR |
2 |
データ書き込みエラーが発生しました。プロジェクトの書き込みトラフィックが上限に達した可能性があります。 |
プロジェクトの書き込みトラフィックの上限を調整してください。 詳細については、「リソースクォータの調整」をご参照ください。 |
|
LOG_PRODUCER_DROP_ERROR |
3 |
ディスクキャッシュまたはメモリキャッシュがいっぱいで、ログを書き込むことができません。 |
|
|
LOG_PRODUCER_SEND_NETWORK_ERROR |
4 |
ネットワークエラーが発生しました。 |
エンドポイント、プロジェクト、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、エンドポイント、プロジェクト、Logstore などのパラメーターの設定を確認してください。 |
|
LOG_PRODUCER_PERSISTENT_ERROR |
99 |
キャッシュデータのディスクへの書き込みに失敗しました。 |
1. キャッシュファイルのパスが正しく設定されているかどうかを確認してください。 2. キャッシュファイルがいっぱいかどうかを確認してください。 3. システムのディスク領域が十分であるかどうかを確認してください。 |
よくある質問
ログが重複するのはなぜですか?
Android SDK はログを非同期で送信します。ネットワーク状況により、ログの送信が失敗し、再送信される場合があります。SDK は、ステータスコード 200 が返された場合にのみ送信操作が成功したと見なすため、一部のログが重複する可能性があります。SQL クエリと分析ステートメントを使用してログの重複排除を行うことを推奨します。
ログの重複率が高い場合は、SDK の初期化に問題がないか確認してください。一般的な原因と解決策は次のとおりです。
-
レジューム可能なアップロードの不正な設定
レジューム可能なアップロードが正しく設定されていない場合は、
setPersistentFilePathメソッドに渡されるファイルパスがグローバルに一意であることを確認してください。 -
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); } } -
もう 1 つの原因は、複数のプロセスを使用していることです。SDK はメインプロセスでのみ初期化してください。異なるプロセスで SDK を初期化する場合は、各プロセスで
setPersistentFilePathに一意の値を設定してください。
-
-
弱いネットワーク環境向けの設定の最適化
アプリケーションが弱いネットワーク環境で実行される場合は、次の例に示すように SDK の設定パラメーターを最適化してください。
// SDK を初期化します。 private void initProducer() { // HTTP 接続および送信のタイムアウト期間を調整して、ログの重複率を低減します。 // 必要に応じて、具体的なタイムアウト期間を調整してください。 config.setConnectTimeoutSec(20); config.setSendTimeoutSec(20); // その他の初期化パラメーター。 // ... }
ログが欠損している場合はどうすればよいですか?
ログ送信は非同期です。ログが送信される前にアプリが閉じられると、未送信のログが失われる可能性があります。レジューム可能なアップロード機能を有効にすることを推奨します。詳細については、「レジューム可能なアップロード」をご参照ください。
ログレポートが遅延している場合はどうすればよいですか?
SDK はログを非同期で送信します。ネットワーク状況や特定のアプリケーションシナリオにより、ログがすぐに送信されない場合があります。少数のデバイスでの遅延は正常です。遅延が広範囲に及ぶ場合は、次のエラーコードを確認してください。
|
エラーコード |
説明 |
|
LOG_PRODUCER_SEND_NETWORK_ERROR |
エンドポイント、プロジェクト、Logstore の設定が正しいかどうかを確認してください。 |
|
LOG_PRODUCER_SEND_UNAUTHORIZED |
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 のネイティブ実装を呼び出します。接続レベルのエラー (ステータスコード -1) が発生した場合にのみ、フォールバックとして 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 の初期化をここで実装します。 // ... } }