全部產品
Search
文件中心

Simple Log Service:Android SDK快速入門

更新時間:Sep 29, 2025

本文介紹如何快速使用Log ServiceAndroid SDK採集日誌。

前提條件

安裝Android SDK

快速使用

參考如下樣本,初始化SDK並調用addLog介面上報日誌。

重要
  • SDK支援初始化多個執行個體,LogProducerConfig執行個體與LogProducerClient執行個體需成對使用。

  • 上報日誌到Log Service時需使用阿里雲帳號或RAM使用者的AccessKey,用於鑒權及防篡改。為避免將AccessKey儲存在移動端應用中,造成安全風險,推薦您使用移動端日誌直傳服務配置AccessKey。具體操作,請參見採集-搭建移動端日誌直傳服務

// 建議您全域儲存LogProducerConfig執行個體和LogProducerClient執行個體。
private LogProducerConfig config = null;
private LogProducerClient client = null;

/**
 * 初始化SDK
 */

private void initProducer() {
    try {
        // Log Service的服務存取點。此處必須是以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");
        // 設定tag資訊,此tag資訊將被附加在每條日誌上。
        config.addTag("example", "example_tag");

        // 是否丟棄到期日誌。0表示不丟棄,把日誌時間修改為目前時間; 1表示丟棄。預設值為1。
        config.setDropDelayLog(0);
        // 是否丟棄鑒權失敗的日誌。0表示不丟棄,1表示丟棄。預設值為0。
        config.setDropUnauthorizedLog(0);

        // LogProducerCallback為可選配置, 如果您不需要關注日誌的發送成功或失敗狀態, 可以不註冊callback。
        // 如果需要動態化配置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的初始化參數。
                }
            }
        };
      
        // 需要關注日誌的發送成功或失敗狀態時, 第二個參數需要傳入一個callback。
        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) {
    // 通過STS服務擷取的AccessKey包含securityToken,需要使用以下方式更新。
    if (null != securityToken && !"".equals(securityToken)) {
        config.resetSecurityToken(accessKeyId, accessKeySecret, securityToken);
    } else {
        // 不是通過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", "中文️");
    log.putContent(null, "null");
    log.putContent("null", (String) null);
    client.addLog(log);
}

進階用法

動態配置參數

Android SDK支援動態化設定Endpoint、ProjectName、Logstore、AccessKey等參數。

  • 動態化配置Endpoint、ProjectName、Logstore。

    // 支援獨立配置或一起配置Endpoint、ProjectName、Logstore等參數。
    // 更新Endpoint。
    config.setEndpoint("your new-endpoint");
    // 更新Project名稱。
    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();
            }
        }
    };
    
    // 需要關注日誌的發送成功或失敗狀態時, 第二個參數需要傳入一個callback。
    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,需要使用以下方式更新。
        if (null != securityToken && !"".equals(securityToken)) {
            config.resetSecurityToken(accessKeyId, accessKeySecret, securityToken);
        } else {
            // 不是通過STS服務擷取的AccessKey,使用以下方式更新。
            config.setAccessKeyId(accessKeyId);
            config.setAccessKeySecret(accessKeySecret);
        }
    }
  • 動態化配置source、topic、tag。

    重要

    source、topic、tag無法針對某類日誌進行設定。一旦設定後,所有未成功發送到Log Service的日誌,都可能會更新。如果您需要通過source、topic、tag來跟蹤具體類別的日誌,可能會導致與您的業務預期不相符合。建議您在產生Log時新增欄位來標識對應的類別資訊。

    // 設定日誌來源。
    config.setSource("your new-source");
    // 設定日誌主題。
    config.setTopic("your new-topic");
    // 設定tag資訊,此tag資訊將被附加在每條日誌上。
    config.addTag("test", "your new-tag");

斷點續傳

Android SDK支援斷點續傳。開啟斷點續傳後,每次通過addLog方法寫入成功的日誌都會先儲存在本地binlog檔案中,確認日誌發送成功後才會刪除本機資料,確保日誌上傳實現At Least Once。

您可以在SDK初始化時加入以下代碼,實現斷點續傳。

重要
  • 初始化多個LogProducerConfig執行個體時,LogProducerConfig類的setPersistentFilePath方法需要傳入不同的值。

  • 如果您的App存在多進程且開啟了斷點續傳功能,您應只在主進程初始化SDK。如果子進程也有採集資料的需求,您需要確保setPersistentFilePath方法傳入的檔案路徑的唯一性,否則可能會導致日誌資料錯亂、丟失等問題。

  • 使用時應注意多線程導致的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);
    // 每個持久化檔案的大小,單位為Byte,格式為N*1024*1024。建議N的取值範圍為1~10。
    config.setPersistentMaxFileSize(N*1024*1024);
    // 本地最多緩衝的日誌數量,不建議超過1048576,預設為65536。
    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

設定tag,格式為tag:xxxx。預設值為空白符串。

setSource

String

設定source欄位的值。預設值為Android。

setPacketLogBytes

Int

每個緩衝的日誌包大小上限。超過上限後,日誌會被立即發送。

取值範圍為1~5242880,預設值為1024 * 1024,單位為位元組。

setPacketLogCount

Int

每個緩衝的日誌包中包含日誌數量的最大值。超過上限後日誌會被立即發送。

取值範圍為1~4096,預設值為1024。

setPacketTimeout

Int

被緩衝日誌的發送逾時時間,如果緩衝逾時,日誌會被立即發送。

預設值為3000,單位為毫秒。

setMaxBufferLimit

Int

單個Producer Client執行個體可以使用的記憶體上限,超出緩衝時add_log介面會立即返回失敗。

預設值為64 * 1024 * 1024。

setSendThreadCount

Int

發送線程數。開啟斷點續傳後,該值強製為1。

setPersistent

Int

是否開啟斷點續傳功能。

  • 1:開啟。

  • 0(預設值):關閉。

setPersistentFilePath

String

持久化的檔案名稱,需保證檔案所在的檔案夾已建立。配置多個LogProducerConfig執行個體時,需確保唯一性。

預設值為空白。

setPersistentForceFlush

Int

是否開啟每次AddLog強制重新整理功能。

  • 1:開啟。開啟後對效能會有影響,建議謹慎開啟。

  • 0(預設值):關閉。

在高可靠性情境時建議開啟,

setPersistentMaxFileCount

Int

持久化檔案滾動個數,建議設定成10。預設值為0。

setPersistentMaxFileSize

Int

每個持久化檔案的大小,單位為Byte,格式為N*1024*1024。建議N的取值範圍為1~10。

setPersistentMaxLogCount

Int

本地最多緩衝的日誌數量,不建議超過1048576,預設為65536。

setConnectTimeoutSec

Int

網路連接逾時時間。預設值為10,單位為秒。

setSendTimeoutSec

Int

日誌發送逾時時間。預設值為15,單位為秒。

setDestroyFlusherWaitSec

Int

flusher線程銷毀最大等待時間。預設值為1,單位為秒。

setDestroySenderWaitSec

Int

sender線程池銷毀最大等待時間。預設值為1,單位為秒。

setCompressType

Int

資料上傳時的壓縮類型。

  • 0:不壓縮。

  • 1(預設值):LZ4壓縮。

setNtpTimeOffset

Int

裝置時間與標準時間的差值,值為標準時間-裝置時間。一般這種差值是由於使用者用戶端裝置時間不同步情境。預設值為0,單位秒。

setMaxLogDelayTime

Int

日誌時間與本機時間的差值。超過該差值後,SDK會根據setDropDelayLog選項進行處理。單位秒,預設值為7243600,即7天。

setDropDelayLog

Int

是否丟棄超過setMaxLogDelayTime的到期日誌。

  • 0(預設值):不丟棄,把日誌時間修改為目前時間。

  • 1:丟棄。

setDropUnauthorizedLog

Int

是否丟棄鑒權失敗的日誌。

  • 0(預設值):不丟棄。

  • 1:丟棄。

setCallbackFromSenderThread

Boolean

是否從sender線程回調callback。

  • true(預設值):從sender線程回調。

  • false:從主線程回調。

錯誤碼

全部的錯誤碼定義在枚舉類LogProducerResult,詳細說明如下表所示。

錯誤碼

數值

說明

解決方案

LOG_PRODUCER_OK

0

成功。

不涉及。

LOG_PRODUCER_INVALID

1

SDK已銷毀或無效。

  1. 檢查是否正確初始化SDK。

  2. 檢查是否調用了destroy()方法。

LOG_PRODUCER_WRITE_ERROR

2

資料寫入錯誤,可能原因是Project寫入流量已達上限。

調整Project寫入資料傳輸量上限。具體操作,請參見調整資源配額

LOG_PRODUCER_DROP_ERROR

3

磁碟或記憶體緩衝已滿,日誌無法寫入。

調整maxBufferLimit、persistentMaxLogCount、persistentMaxFileSize參數值後重試。

LOG_PRODUCER_SEND_NETWORK_ERROR

4

網路錯誤。

檢查Endpoint、Project、Logstore的配置情況。

LOG_PRODUCER_SEND_QUOTA_ERROR

5

Project寫入流量已達上限。

調整Project寫入資料傳輸量上限。具體操作,請參見調整資源配額

LOG_PRODUCER_SEND_UNAUTHORIZED

6

AccessKey到期、無效或AccessKey權限原則配置不正確。

檢查AccessKey。

RAM使用者需具備動作記錄服務資源的許可權。具體操作,請參見為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重複初始化的原因是多進程。建議您只在主進程初始化SDK,或者在不同進程中初始化SDK時,設定setPersistentFilePath為不同的值,確保唯一性。

  • 弱網環境配置最佳化

    如果您的應用是在弱網環境下使用,建議您參考如下樣本最佳化SDK配置參數。

    // 初始化SDK。
    private void initProducer() {
        // 調整HTTP連結和發送逾時時間,有利於減少日誌重複率。
        // 您可以根據實際情況調整具體的逾時時間。
        config.setConnectTimeoutSec(20);
        config.setSendTimeoutSec(20);
      
        // 其他初始化參數。
        // ...
    }

日誌缺失,如何處理?

日誌上報的過程是非同步,如果在上報日誌前App被關閉,則日誌有可能無法被上報,造成日誌丟失。建議您開啟斷點續傳功能,具體操作,請參見斷點續傳

日誌上報延時,如何處理?

SDK發送日誌的過程是非同步,受網路環境以及應用使用情境的影響,日誌可能不會立即上報。如果只有個別裝置出現日誌上報延時,這種情況是正常的,否則請您根據如下錯誤碼進行排查。

錯誤碼

說明

LOG_PRODUCER_SEND_NETWORK_ERROR

檢查Endpoint、Project、Logstore的配置是否正確。

LOG_PRODUCER_SEND_UNAUTHORIZED

檢查AccessKey是否到期、有效或AccessKey權限原則配置是否正確。

LOG_PRODUCER_SEND_QUOTA_ERROR

調整Project寫入流量已達上限。具體操作,請參見調整資源配額

Android SDK是否支援DNS預解析和緩衝策略?

以下提供Android SDK結合HTTPDNS SDK以及OkHttp實現DNS預解析和緩衝策略的樣本。

  1. 實現自訂 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);
      }
    }
  2. 實現自訂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;
        }
      }
    
    }
    
  3. 完成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 的初始化。
        // ...
      }
    }