419

Theo Google, tôi phải " hủy kích hoạt bất kỳ lệnh gọi nào đến các phương thức Ghi nhật ký trong mã nguồn " trước khi xuất bản ứng dụng Android của mình lên Google Play. Trích từ phần 3 của danh sách kiểm tra xuất bản :

Đảm bảo rằng bạn hủy kích hoạt ghi nhật ký và tắt tùy chọn gỡ lỗi trước khi bạn xây dựng ứng dụng của mình để phát hành. Bạn có thể hủy kích hoạt ghi nhật ký bằng cách xóa các lệnh gọi đến các phương thức Ghi nhật ký trong các tệp nguồn của mình.

Dự án nguồn mở của tôi rất lớn và thật khó để thực hiện nó theo cách thủ công mỗi khi tôi phát hành. Ngoài ra, việc xóa dòng Nhật ký có khả năng phức tạp, ví dụ:

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

Nếu tôi nhận xét dòng Nhật ký, thì điều kiện áp dụng cho dòng tiếp theo và rất có thể load () không được gọi. Những tình huống như vậy có đủ hiếm để tôi có thể quyết định rằng nó không nên tồn tại không?

Vì vậy, có cách nào tốt hơn ở cấp độ mã nguồn để làm điều đó không? Hoặc có thể một số cú pháp ProGuard thông minh để loại bỏ tất cả các dòng Nhật ký một cách hiệu quả nhưng an toàn?

|
512

Tôi tìm thấy một giải pháp dễ dàng hơn nhiều là quên tất cả các ifkiểm tra ở khắp nơi và chỉ sử dụng ProGuard để loại bỏ bất kỳ Log.d()hoặc Log.v()lệnh gọi phương thức nào khi chúng ta gọi releasemục tiêu Ant của mình.

Bằng cách đó, chúng tôi luôn có thông tin gỡ lỗi được xuất cho các bản dựng thông thường và không phải thực hiện bất kỳ thay đổi mã nào cho các bản dựng phát hành. ProGuard cũng có thể thực hiện nhiều lần chuyển qua mã bytecode để loại bỏ các câu lệnh không mong muốn khác, các khối trống và có thể tự động nội tuyến các phương thức ngắn nếu thích hợp.

Ví dụ: đây là cấu hình ProGuard rất cơ bản cho Android:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

Vì vậy, bạn sẽ lưu nó vào một tệp, sau đó gọi ProGuard từ Ant, chuyển vào JAR vừa mới biên dịch của bạn và JAR nền tảng Android mà bạn đang sử dụng.

Xem thêm các ví dụ trong sách hướng dẫn ProGuard.


Cập nhật (4,5 năm sau): Ngày nay tôi đã sử dụng Timber để ghi nhật ký Android.

Nó không chỉ đẹp hơn một chút so với việc Logtriển khai mặc định - thẻ nhật ký được đặt tự động và dễ dàng ghi nhật ký các chuỗi và ngoại lệ được định dạng - mà bạn cũng có thể chỉ định các hành vi ghi nhật ký khác nhau trong thời gian chạy.

Trong ví dụ này, các câu lệnh ghi nhật ký sẽ chỉ được ghi vào logcat trong các bản dựng gỡ lỗi của ứng dụng của tôi:

Gỗ được thiết lập trong Application onCreate()phương pháp của tôi :

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
}

Sau đó, bất kỳ nơi nào khác trong mã của tôi, tôi có thể đăng nhập dễ dàng:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

Xem ứng dụng mẫu Timber để biết ví dụ nâng cao hơn, trong đó tất cả các câu lệnh nhật ký được gửi đến logcat trong quá trình phát triển và trong quá trình sản xuất, không có câu lệnh gỡ lỗi nào được ghi lại, nhưng các lỗi được âm thầm báo cáo cho Crashlytics.

|
  • 1
    Và tại sao điều đó không có trong tệp proguard mặc định? –  16:08:51 22/07/2011
  • 1
    + rds vì nó sẽ làm cho số dòng của hệ thống xếp chồng sản xuất khác với số dòng trong mã của bạn, khi các dòng bị xóa. –  11:01:47 21/02/2012
  • 1
    Tôi có thể xác nhận rằng việc loại bỏ các cuộc gọi Nhật ký sẽ thay đổi số dòng trong các dấu ngăn xếp. Nó không phải lúc nào cũng không đồng bộ (tôi đã thực hiện một số thử nghiệm nhanh nhưng không thể xác định chính xác nguyên nhân là gì, có thể nếu bạn nối một chuỗi trong lệnh gọi Nhật ký), nhưng đôi khi nó sẽ bị lệch một vài dòng. IMO đáng gặp rắc rối vì khả năng dễ dàng loại bỏ các cuộc gọi Nhật ký. –  18:37:18 15/06/2012
  • 1
    @Fraggle Từ proguard-android.txt trong công cụ ADT: "Lưu ý rằng nếu bạn muốn kích hoạt tính năng tối ưu hóa, bạn không thể chỉ đưa cờ tối ưu hóa vào tệp cấu hình dự án của riêng mình; thay vào đó, bạn sẽ cần phải trỏ đến" proguard-android-Optimizer. txt "thay vì tệp này từ tệp" # project.properties của bạn. –  11:04:07 07/12/2012
  • 1
    Như espinchi đã nói trong câu trả lời dưới đây. "Vấn đề duy nhất với phương pháp này là, nếu bạn thực hiện Log.d (" tag "," Processed: "+ new ItemCounter (blabla) +" items "), ngay cả khi thông báo nhật ký này không xuất hiện trong phiên bản đã phát hành của bạn, một StringBuilder được sử dụng để tạo thông báo, việc tạo này có thể tốn kém. "Điều này cũng đúng trong trường hợp của Timber phải không? –  06:21:40 17/05/2016
123

Tất cả các câu trả lời đều tốt, nhưng khi tôi hoàn thành quá trình phát triển của mình, tôi không muốn sử dụng các câu lệnh if xung quanh tất cả các lệnh gọi Nhật ký, cũng như không muốn sử dụng các công cụ bên ngoài.

Vì vậy, giải pháp tôi đang sử dụng là thay thế lớp android.util.Log bằng lớp Nhật ký của riêng tôi:

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    public static void i(String tag, String string) {
        if (LOG) android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) android.util.Log.w(tag, string);
    }
}

Điều duy nhất tôi phải làm trong tất cả các tệp nguồn là thay thế việc nhập android.util.Log bằng lớp của riêng tôi.

|
  • 1
    Vấn đề duy nhất với phương pháp này là, nếu bạn thực hiện Log.d ("tag", "Processed:" + new ItemCounter (blabla) + "items"), ngay cả khi thông báo nhật ký này không xuất hiện trong phiên bản đã phát hành của bạn, StringBuilder được sử dụng để tạo thông báo, có thể tốn kém để tạo. –  17:06:44 09/09/2011
  • 1
    Giải pháp này có một vấn đề lớn. espinchi chỉ đề cập đến phần nổi của tảng băng chìm. Vấn đề là khi bạn gọi Log.d("tag", someValue.toString());mà bạn rất dễ quên kiểm tra someValue xem có bị null không, điều đó có nghĩa là nó có thể ném NullPointerExceptionvào sản xuất. Nó gợi ý một giải pháp an toàn nhưng nó sẽ đánh lừa bạn. Chúng tôi cho chúng ta một private static boolean DEBUGvà sau đóif(DEBUG)Log.d(TAG, msg); –  19:10:09 08/08/2012
  • 1
    @espinchi Mối quan tâm của bạn dường như áp dụng cho tất cả các thư viện ghi nhật ký như được thảo luận trong câu trả lời này stackoverflow.com/a/15452492/433718 (Slf4j, backlog, ...). Nó không được đề xuất để sử dụng chúng? –  20:14:21 20/06/2013
  • 1
    Cách duy nhất để giảm thiểu chi phí được đề cập trong nhận xét đầu tiên của @espinchi là thay đổi phương pháp ghi nhật ký để chấp nhận các varargs thay vì String. Giải pháp hoàn chỉnh được ghi ở đây . Điều này dường như có một nhược điểm khác: mọi cuộc gọi phải được chỉnh sửa (không chỉ một dòng nhập). –  15:17:17 27/11/2013
  • 1
    Chỉ là FYI, nếu bạn đang sử dụng Android Studio và hệ thống xây dựng gradle, bạn có thể sử dụng static final boolean LOG = BuildConfig.DEBUGvà không phải sửa đổi tệp này bao giờ. –  19:23:00 20/05/2014
61

Tôi khuyên bạn nên có một boolean tĩnh ở đâu đó cho biết có đăng nhập hay không:

lớp MyDebug {
  static final boolean LOG = true;
}

Sau đó, bất cứ nơi nào bạn muốn đăng nhập mã của mình, chỉ cần thực hiện điều này:

if (MyDebug.LOG) {
  if (điều kiện) Log.i (...);
}

Bây giờ khi bạn đặt MyDebug.LOG thành false, trình biên dịch sẽ loại bỏ tất cả mã bên trong các kiểm tra như vậy (vì đây là mã cuối cùng tĩnh nên tại thời điểm biên dịch mã đó không được sử dụng).

Đối với các dự án lớn hơn, bạn có thể muốn bắt đầu có boolean trong các tệp riêng lẻ để có thể dễ dàng bật hoặc tắt ghi nhật ký ở đó nếu cần. Ví dụ: đây là các hằng số ghi nhật ký khác nhau mà chúng tôi có trong trình quản lý cửa sổ:

static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;

Với mã tương ứng như:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
|
  • 1
    Tôi cũng sẽ bỏ phiếu cho cách tiếp cận như vậy. Nó cũng được sử dụng trong mẫu thanh toán trong ứng dụng chính thức của Google. –  16:56:39 05/04/2011
  • 1
    Sẽ ít dài dòng hơn nếu chuyển điều kiện làm tham số đầu tiên? –  12:36:21 31/08/2012
  • 1
    Đây dường như là giải pháp tốt nhất mặc dù nó yêu cầu mã bổ sung trên mỗi câu lệnh nhật ký: Số dòng được giữ nguyên (điểm yếu của cách tiếp cận ProGuard), Không có mã nào để tạo thông báo nhật ký được thực thi ( điểm yếu của cách tiếp cận lớp trình bao bọc và dường như cũng có cách tiếp cận thư viện ghi nhật ký) . Việc sử dụng phương pháp này trong Google trong mẫu thanh toán ứng dụng theo @LA_ cũng hỗ trợ suy nghĩ của tôi. –  20:47:36 20/06/2013
  • 1
    @Snicolas Làm cách nào để bạn có thể chuyển điều kiện làm tham số đầu tiên mà không cần triển khai trình bao bọc? Hơn nữa, nếu bạn thêm nó dưới dạng tham số, thì trước khi nhập phương thức, tất cả các tham số cần được đánh giá, đó cũng là chuỗi thông báo. Điều kiện cần được kiểm tra trước khi xây dựng các thông số. Giải pháp được đề xuất có thể là giải pháp tốt nhất không có công cụ bên ngoài. –  12:45:17 21/07/2013
  • 1
    Mã nhị phân khôn ngoan, điều này là tốt nhất. Nhưng việc viết mã như thế này chỉ tốn rất nhiều công sức để có được một đầu ra nhật ký gỡ lỗi đơn giản. Khả năng đọc mã giảm đáng kể. Giành chiến thắng một số người, mất một số, tôi đoán ... –  08:19:22 12/12/2013
30

Giải pháp Proguard của Christopher là giải pháp tốt nhất, nhưng nếu vì bất kỳ lý do gì bạn không thích Proguard, đây là một giải pháp công nghệ rất thấp:

Nhật ký nhận xét:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

Nhật ký bỏ ghi chú:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

Một hạn chế là hướng dẫn ghi nhật ký của bạn không được trải dài trên nhiều dòng.

(Thực thi các dòng này trong một trình bao UNIX ở gốc dự án của bạn. Nếu sử dụng Windows, hãy tải một lớp UNIX hoặc sử dụng các lệnh Windows tương đương)

|
  • 1
    cần một "" sau -i trong Sed nếu chạy trên Mac (như thế này ) Cảm ơn. –  03:45:50 16/09/2012
  • 1
    Tôi cảm thấy rằng đây có thể là những gì tôi sẽ chỉ sử dụng cho một cái gì đó tôi đang làm việc trên bởi vì tôi không có nhiều may mắn làm việc đó với Proguard ở tất cả –  16:49:19 20/10/2012
  • 1
    Và điều gì sẽ xảy ra nếu bạn có Nhật ký sau một nhánh while không được đặt trong ngoặc, như bạn đã đề xuất trong bài đăng đầu tiên của mình? –  12:45:59 21/07/2013
  • 1
    @ type-a1pha: Nếu bạn áp dụng giải pháp này, thì bạn phải coi các khối dấu ngoặc là bắt buộc. –  00:08:10 22/07/2013
  • 1
    @NicolasRaoul Các bản sửa lỗi Dấu chấm phẩy vấn đề này ( //vs ;//) –  15:18:58 02/08/2013
20

Tôi muốn bổ sung một số điều chính xác về việc sử dụng Proguard với Android Studio và gradle, vì tôi đã gặp rất nhiều vấn đề khi xóa các dòng nhật ký khỏi tệp nhị phân cuối cùng.

Để tạo ra assumenosideeffectscác tác phẩm Proguard, cần có một điều kiện tiên quyết.

Trong tệp gradle của bạn, bạn phải chỉ định việc sử dụng proguard-android-optimize.txttệp làm tệp mặc định.

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

        // With the file below, it does not work!
        //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

Trên thực tế, trong proguard-android.txttệp mặc định , tối ưu hóa bị tắt với hai cờ:

-dontoptimize
-dontpreverify

Các proguard-android-optimize.txttập tin không thêm những dòng này, vì vậy bây giờ assumenosideeffectscó thể làm việc.

Sau đó, về mặt cá nhân, tôi sử dụng SLF4J , hơn thế nữa khi tôi phát triển một số thư viện được phân phối cho những người khác. Ưu điểm là mặc định không có đầu ra. Và nếu nhà tích hợp muốn một số đầu ra nhật ký, anh ta có thể sử dụng Logback cho Android và kích hoạt nhật ký, do đó, nhật ký có thể được chuyển hướng đến một tệp hoặc đến LogCat.

Nếu tôi thực sự cần tách nhật ký khỏi thư viện cuối cùng, thì tôi sẽ thêm vào tệp Proguard của mình (tất nhiên sau khi đã bật proguard-android-optimize.txttệp):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
|
  • 1
    Điều này không làm việc với Jack mới compiler-- stackoverflow.com/questions/37932114/... –  05:02:34 08/03/2017
  • 1
    Điều này đã giúp tôi; cả proguard-android-optimize.txttệp Proguard mặc định và -assumenosideeffectstệp Proguard tùy chỉnh đều cần thiết! Tôi đang sử dụng R8 shinker (mặc định ngày nay) và ghi nhật ký Android mặc định. –  15:45:21 28/04/2020
10

Tôi thực sự khuyên bạn nên sử dụng Timber từ Jake Wharton

https://github.com/JakeWharton/timber

nó giải quyết vấn đề của bạn bằng cách bật / tắt cộng với việc tự động thêm lớp thẻ

chỉ

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

nhật ký sẽ chỉ được sử dụng trong phiên bản gỡ lỗi của bạn, và sau đó sử dụng

Timber.d("lol");

hoặc là

Timber.i("lol says %s","lol");

để in

"Lớp / tin nhắn của bạn" mà không cần chỉ định thẻ

|
  • 1
    Gỗ rất tốt, nhưng nếu bạn đã có một dự án hiện có - bạn có thể thử github.com/zserge/log . Đó là một bản thay thế cho android.util.Log và có hầu hết các tính năng mà Timber có và thậm chí nhiều hơn thế. –  19:16:14 09/06/2015
  • 1
    zserge, giải pháp nhật ký của bạn có vẻ tốt. Rất nhiều tính năng. Bạn đã xem xét thêm các quy tắc Lint như Timber chưa? –  20:28:09 03/03/2017
8

Tôi đã sử dụng lớp LogUtils giống như trong ứng dụng mẫu IO của Google. Tôi đã sửa đổi điều này để sử dụng hằng số DEBUG dành riêng cho ứng dụng thay vì BuildConfig.DEBUG vì BuildConfig.DEBUG không đáng tin cậy . Sau đó, trong Lớp học của tôi, tôi có những thứ sau đây.

import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;

public class MyActivity extends FragmentActivity {
  private static final String TAG = makeLogTag(MyActivity.class);

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LOGV(TAG, "my message");
  }
}
|
  • 1
    +1 cho báo cáo lỗi Build.DEBUGmà tôi đã từng sử dụng. Tôi cũng đã từ bỏ các cách giải quyết "đúng" khác nhau và sử dụng một giải pháp phong cách tương tự cho bạn. –  08:42:15 12/12/2013
7

Tôi đang đăng giải pháp này áp dụng riêng cho người dùng Android Studio. Gần đây tôi cũng đã phát hiện ra Timber và đã nhập thành công nó vào ứng dụng của mình bằng cách làm như sau:

Đặt phiên bản mới nhất của thư viện vào build.gradle của bạn:

compile 'com.jakewharton.timber:timber:4.1.1'

Sau đó, trong Android Studios, đi tới Chỉnh sửa -> Tìm -> Thay thế trong Đường dẫn ...

Gõ vào Log.e(TAG,hoặc tuy nhiên bạn đã xác định thông điệp Log của bạn vào "Text to find"hộp văn bản. Sau đó, bạn chỉ cần thay thế nó bằngTimber.e(

Làm cách nào để xóa tất cả các cuộc gọi ghi nhật ký gỡ lỗi trước khi xây dựng phiên bản phát hành của ứng dụng Android?

Nhấp vào Tìm và sau đó thay thế tất cả.

Android Studios bây giờ sẽ xem qua tất cả các tệp trong dự án của bạn và thay thế tất cả Nhật ký bằng Timbers.

Vấn đề duy nhất tôi gặp phải với phương pháp này là gradle xuất hiện hàng triệu thông báo lỗi sau đó vì nó không thể tìm thấy "Timber" trong các lần nhập cho mỗi tệp java của bạn. Chỉ cần nhấp vào lỗi và Android Studios sẽ tự động nhập "Timber" vào java của bạn. Khi bạn đã thực hiện xong tất cả các tệp lỗi của mình, gradle sẽ biên dịch lại.

Bạn cũng cần đặt đoạn mã này vào onCreatephương thức của Applicationlớp bạn :

    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }

Điều này sẽ dẫn đến việc ghi nhật ký ứng dụng chỉ khi bạn đang ở chế độ phát triển chứ không phải trong sản xuất. Bạn cũng có thể có BuildConfig.RELEASEđể đăng nhập trong chế độ phát hành.

|
  • 1
    Hãy thử làm điều tương tự cho hàng nhập khẩu của bạn, và làm cho văn bản bảo Regular hộp Biểu hiện được kiểm tra để tìm: import android\.util\.Log\;Thay thế bằng:import android\.util\.Log\;\nimport timber\.log\.Timber\; –  15:04:53 11/08/2016
  • 1
    hoặc bạn có thể sử dụng tìm kiếm structual và thay thế như Chike Mgbemena show trong mình bài –  11:54:34 29/11/2016
  • 1
    @MaksimTuraev Liên kết của bạn không còn phù hợp nữa. Bây giờ nó là một blog về kiểu tóc. –  14:42:19 05/02/2019
  • 1
    Trông giống như bài được lấy ra = (không thể tìm thấy nó ở bất cứ đâu. –  14:54:16 05/02/2019
  • 1
    @MaksimTuraev đây là bản copy từ máy Wayback, nhưng những hình ảnh được chia - web.archive.org/web/20161004161318/http://chikemgbemena.com/... –  15:30:10 05/02/2019
7

Tôi sẽ cân nhắc sử dụng cơ sở ghi nhật ký của roboguice thay vì android.util.Log tích hợp sẵn

Cơ sở của họ tự động vô hiệu hóa nhật ký gỡ lỗi và chi tiết cho các bản dựng phát hành. Thêm vào đó, bạn nhận được một số tính năng tiện lợi miễn phí (ví dụ: hành vi ghi nhật ký có thể tùy chỉnh, dữ liệu bổ sung cho mỗi nhật ký và hơn thế nữa)

Sử dụng proguard có thể khá phức tạp và tôi sẽ không gặp khó khăn khi định cấu hình và làm cho nó hoạt động với ứng dụng của bạn trừ khi bạn có lý do chính đáng cho điều đó (tắt nhật ký không phải là một cách tốt)

|
6

Nếu bạn có thể chạy thay thế toàn cục (một lần) và sau đó duy trì một số quy ước mã hóa, bạn có thể làm theo mẫu thường được sử dụng trong khuôn khổ Android .

Thay vì viết

Log.d(TAG, string1 + string2 + arg3.toString());

có nó như

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

Giờ đây, proguard có thể loại bỏ StringBuilder và tất cả các chuỗi và phương thức mà nó sử dụng trên đường, khỏi DEX phát hành được tối ưu hóa. Sử dụng proguard-android-optimize.txtvà bạn không cần phải lo lắng về android.util. Đăng nhập proguard-rules.pro:

android {
  …
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

Với plugin gradle Android Studio, khá đáng tin cậy, vì vậy bạn không cần thêm hằng số để kiểm soát việc tước bỏ.BuildConfig.DEBUG

|
6

Per android.util.Log cung cấp một cách để bật / tắt nhật ký:

public static native boolean isLoggable(String tag, int level);

Mặc định phương thức isLoggable (...) trả về false, chỉ sau khi bạn setprop trong thiết bị thích điều này:

adb shell setprop log.tag.MyAppTag DEBUG

Nó có nghĩa là bất kỳ nhật ký nào trên mức DEBUG đều có thể được in ra. Tham khảo tài liệu android:

Kiểm tra xem liệu nhật ký cho thẻ được chỉ định có thể đăng nhập ở cấp được chỉ định hay không. Mức mặc định của bất kỳ thẻ nào được đặt thành INFO. Điều này có nghĩa là bất kỳ cấp độ nào ở trên và bao gồm cả INFO sẽ được ghi lại. Trước khi thực hiện bất kỳ lệnh gọi nào đến một phương thức ghi nhật ký, bạn nên kiểm tra xem thẻ của mình có được ghi lại hay không. Bạn có thể thay đổi mức mặc định bằng cách đặt thuộc tính hệ thống: 'setprop log.tag. 'Trong đó cấp độ là ĐỘNG TỪ, GỢI Ý, THÔNG TIN, CẢNH BÁO, LỖI, ĐÁNH GIÁ hoặc HỖ TRỢ. SUPPRESS sẽ tắt tất cả ghi nhật ký cho thẻ của bạn. Bạn cũng có thể tạo một tệp local.prop với nội dung sau: 'log.tag. =' Và đặt tệp đó vào /data/local.prop.

Vì vậy, chúng tôi có thể sử dụng log tùy chỉnh

public final class Dlog 
{
    public static void v(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.VERBOSE))
            Log.v(tag, msg);
    }

    public static void d(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.DEBUG))
            Log.d(tag, msg);
    }

    public static void i(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.INFO))
            Log.i(tag, msg);
    }

    public static void w(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.WARN))
            Log.w(tag, msg);
    }

    public static void e(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.ERROR))
            Log.e(tag, msg);
    }
}
|
4

Thêm thông tin sau vào tệp proguard-rules.txt của bạn

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
|
3

Làm cách nào để xóa tất cả các cuộc gọi ghi nhật ký gỡ lỗi trước khi xây dựng phiên bản phát hành của ứng dụng Android?

Đây là những gì tôi đã từng làm trong các dự án Android của mình ..

Trong Android Studio, chúng ta có thể thực hiện thao tác tương tự bằng cách, Ctrl + Shift + F để tìm từ toàn bộ dự án (Command + Shift + F trong MacO) và Ctrl + Shift + R để Thay thế ((Command + Shift + R trong MacO))

|
  • 1
    Điều này dường như mở ra công việc với các dự án nhật thực. Tùy chọn tìm kiếm thậm chí không có sẵn trên các studio Android. –  16:54:42 20/03/2016
  • 1
    trong Android Studio bạn có thể làm tương tự với tìm kiếm Ctrl + Shift + F tắt –  06:20:18 21/03/2016
  • 1
    Mã ví dụ trong câu hỏi giải thích tại sao điều này không đáng tin cậy. –  01:59:55 22/03/2016
  • 1
    Nó có thể gây ra sự cố khi xóa bất kỳ lệnh nào có trong Nhật ký. Ví dụ chocolateLog.recipie (); –  01:36:44 20/05/2016
  • 1
    Không thể tìm thấy tùy chọn này cho Android Studio 2.1. Ngoài ra, tôi có thể sử dụng thủ thuật này trên từng tệp một bằng cách tìm kiếm / thay thế thông thường. –  10:43:15 19/09/2016
3

Như nhận xét của zserge đã đề xuất,

Gỗ rất tốt, nhưng nếu bạn đã có một dự án hiện có - bạn có thể thử github.com/zserge/log. Nó thay thế cho android.util.Log và có hầu hết các tính năng mà Timber có và thậm chí nhiều hơn thế.

thư viện nhật ký của anh ấy cung cấp công tắc bật / tắt in nhật ký đơn giản như bên dưới.

Ngoài ra, nó chỉ yêu cầu thay đổi importdòng và không cần thay đổi gì đối với Log.d(...);câu lệnh.

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
|
  • 1
    Bạn có phải đặt dòng mã đó trong mỗi Hoạt động / Phân đoạn hay chỉ ở một nơi? –  17:15:44 29/08/2016
  • 1
    @NoahTernullo // trong tệp Ứng dụng dẫn xuất. DefaultApplication.java –  23:34:31 29/08/2016
3

Tôi có một giải pháp rất đơn giản. Tôi sử dụng IntelliJ để phát triển, vì vậy các chi tiết khác nhau nhưng ý tưởng nên áp dụng cho tất cả các IDE.

Tôi chọn gốc cây nguồn của mình, nhấp chuột phải và chọn để thực hiện "thay thế". Sau đó tôi chọn thay thế tất cả "Nhật ký". với "// Nhật ký.". Điều này loại bỏ tất cả các câu lệnh nhật ký. Để đặt chúng trở lại sau này, tôi lặp lại cùng một thay thế nhưng lần này là thay thế tất cả "// Nhật ký." với "Nhật ký.".

Hoạt động tuyệt vời đối với tôi. Chỉ cần nhớ đặt thay thế là phân biệt chữ hoa chữ thường để tránh các tai nạn như "Hộp thoại.". Để đảm bảo hơn, bạn cũng có thể thực hiện bước đầu tiên với "Nhật ký". dưới dạng chuỗi để tìm kiếm.

Xuất sắc.

|
  • 1
    Vui lòng đọc đoạn "Nếu tôi nhận xét dòng Nhật ký" trong câu hỏi của tôi. –  01:23:15 05/01/2015
  • 1
    OK, vâng, tôi nên đọc lại thường xuyên hơn sau khi xem các câu trả lời :). Nếu bạn gặp những trường hợp như vậy, bạn có thể muốn một giải pháp khác, chẳng hạn như đã đề xuất trước đây, chẳng hạn như đặt tất cả nhật ký của bạn sau giao diện khác. Đề nghị của tôi có lẽ hoạt động tốt hơn cho các đội nhỏ và các dự án, nơi mọi người muốn tránh overhead của libs ghi thêm, bạn biết người và mã tốt, vv –  23:13:36 05/01/2015
  • 1
    Thay thế Log.d bằng; // Log.d cũng sẽ xử lý tình huống "Nếu" đó. –  05:47:22 11/07/2015
1

Đây là cách tôi giải quyết nó trong Dự án Kotlin của mình trước khi đi vào sản xuất:

buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int d(...);
    public static int w(...);
    public static int v(...);
    public static int i(...);
    public static int e(...);
}
|
1

ProGuard sẽ làm điều đó cho bạn trên bản phát hành của bạn và bây giờ là tin tốt từ android.com:

http://developer.android.com/tools/help/proguard.html

Công cụ ProGuard thu nhỏ, tối ưu hóa và làm xáo trộn mã của bạn bằng cách xóa mã không sử dụng và đổi tên các lớp, trường và phương thức bằng các tên khó hiểu về mặt ngữ nghĩa. Kết quả là một tệp .apk có kích thước nhỏ hơn sẽ khó dịch ngược hơn. Vì ProGuard làm cho ứng dụng của bạn khó được thiết kế ngược hơn, điều quan trọng là bạn phải sử dụng nó khi ứng dụng của bạn sử dụng các tính năng nhạy cảm với bảo mật như khi bạn cấp phép cho ứng dụng của mình.

ProGuard được tích hợp vào hệ thống xây dựng Android, vì vậy bạn không cần phải gọi nó theo cách thủ công. ProGuard chỉ chạy khi bạn xây dựng ứng dụng của mình ở chế độ phát hành, vì vậy bạn không phải đối phó với mã khó hiểu khi xây dựng ứng dụng của mình ở chế độ gỡ lỗi. Việc chạy ProGuard là hoàn toàn không bắt buộc, nhưng rất được khuyến khích.

Tài liệu này mô tả cách bật và cấu hình ProGuard cũng như sử dụng công cụ truy xuất để giải mã dấu vết ngăn xếp bị xáo trộn

|
  • 1
    Tuy nhiên, nó dường như không xóa ghi nhật ký gỡ lỗi theo mặc định. Vì vậy, câu trả lời của Christopher nghe có vẻ hay hơn. –  08:04:58 26/03/2013
1

Tôi đã cải thiện giải pháp ở trên bằng cách cung cấp hỗ trợ cho các cấp độ nhật ký khác nhau và bằng cách thay đổi các cấp độ nhật ký tự động tùy thuộc vào việc mã đang được chạy trên thiết bị trực tiếp hay trên trình giả lập.

public class Log {

final static int WARN = 1;
final static int INFO = 2;
final static int DEBUG = 3;
final static int VERB = 4;

static int LOG_LEVEL;

static
{
    if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
        LOG_LEVEL = VERB;
    } else {
        LOG_LEVEL = INFO;
    }

}


/**
 *Error
 */
public static void e(String tag, String string)
{
        android.util.Log.e(tag, string);
}

/**
 * Warn
 */
public static void w(String tag, String string)
{
        android.util.Log.w(tag, string);
}

/**
 * Info
 */
public static void i(String tag, String string)
{
    if(LOG_LEVEL >= INFO)
    {
        android.util.Log.i(tag, string);
    }
}

/**
 * Debug
 */
public static void d(String tag, String string)
{
    if(LOG_LEVEL >= DEBUG)
    {
        android.util.Log.d(tag, string);
    }
}

/**
 * Verbose
 */
public static void v(String tag, String string)
{
    if(LOG_LEVEL >= VERB)
    {
        android.util.Log.v(tag, string);
    }
}


}
|
  • 1
    Vấn đề tương tự như giải pháp trước đó. Nếu tham số chuỗi được xây dựng bằng cách sử dụng các cuộc gọi đắt tiền, nó vẫn gây lãng phí tài nguyên. Việc kiểm tra việc gọi cần được thực hiện trước khi xây dựng các tham số được truyền vào. –  12:39:15 21/07/2013
0

Nếu bạn muốn sử dụng phương pháp lập trình thay vì sử dụng ProGuard, thì bằng cách tạo lớp của riêng bạn với hai trường hợp, một để gỡ lỗi và một để phát hành, bạn có thể chọn nội dung để đăng nhập trong một trong hai trường hợp.

Vì vậy, nếu bạn không muốn ghi lại bất kỳ thứ gì khi được phát hành, chỉ cần triển khai Trình ghi nhật ký không làm gì cả, như ví dụ dưới đây:

import android.util.Log

sealed class Logger(defaultTag: String? = null) {
    protected val defaultTag: String = defaultTag ?: "[APP-DEBUG]"

    abstract fun log(string: String, tag: String = defaultTag)

    object LoggerDebug : Logger() {
        override fun log(string: String, tag: String) {
            Log.d(tag, string)
        }
    }

    object LoggerRelease : Logger() {
        override fun log(string: String, tag: String) {}
    }

    companion object {
        private val isDebugConfig = BuildConfig.DEBUG

        val instance: Logger by lazy {
            if(isDebugConfig)
            LoggerDebug
            else
                LoggerRelease
        }

    }
}

Sau đó, để sử dụng lớp trình ghi nhật ký của bạn:

class MainActivity : AppCompatActivity() {

private val logger = Logger.instance

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    logger.log("Activity launched...")
    ...
    myView.setOnClickListener {
        ...

        logger.log("My View clicked!", "View-click")
    }
}

== CẬP NHẬT ==

Nếu chúng ta muốn tránh nối chuỗi để có hiệu suất tốt hơn, chúng ta có thể thêm một hàm nội tuyến với lambda sẽ chỉ được gọi trong cấu hình gỡ lỗi:

// Add this function to the Logger class.
inline fun commit(block: Logger.() -> Unit) {
    if(this is LoggerDebug)
        block.invoke(this)
}

Và sau đó:

 logger.commit {
     log("Logging without $myVar waste of resources"+ "My fancy concat")
 }

Vì chúng ta đang sử dụng một hàm nội tuyến, không có phân bổ đối tượng bổ sung và không có lệnh gọi phương thức ảo bổ sung nào.

|
  • 1
    nếu bạn làm như vậy Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), ngay cả khi thông báo nhật ký này không xuất hiện trong phiên bản đã phát hành của bạn, một StringBuilder được sử dụng để tạo thông báo, việc tạo này có thể tốn kém. –  01:46:49 21/12/2020
  • 1
    Trong một tình huống mã quan trọng về hiệu suất, bạn nói đúng, việc nối chuỗi có thể tốn kém để tạo, đặc biệt là bên trong các vòng lặp. Trong những trường hợp này, tôi sẽ xóa hoàn toàn mã ghi nhật ký bằng PorGuard hoặc một số phương pháp khác. Ngược lại, nếu chúng ta vẫn muốn tránh các nối chuỗi nhưng chúng ta muốn giải quyết vấn đề theo chương trình, chúng ta có thể sử dụng một khối hàm nội tuyến sẽ chỉ được gọi nếu chúng ta đang ở trong cấu hình gỡ lỗi. –  15:01:09 21/12/2020
0

Dễ dàng với kotlin, chỉ cần khai báo một vài hàm cấp cao nhất

val isDebug: Boolean
    get() = BuildConfig.DEBUG

fun logE(tag: String, message: String) {
    if (isDebug) Log.e(tag, message)
}

fun logD(tag: String, message: String) {
    if (isDebug) Log.d(tag, message)
}
|
0

Bạn có thể thử sử dụng phương pháp thông thường đơn giản này:

Ctrl+ Shift+R

thay thế

Log.e(

Với

// Log.e(
|
  • 1
    Điều đó sẽ không hoạt động tốt với mã ví dụ được đưa ra trong câu hỏi. –  01:19:43 30/04/2020
0

theo cách của tôi:

1) bật Chế độ chọn cột (alt + shift + insert)

2) chọn trên một Log.d (TAG, "văn bản"); phần 'Nhật ký.'

3) sau đó thực hiện shift + ctrl + alt + j

4) nhấp vào mũi tên trái

5) làm ca + kết thúc

6) nhấn xóa.

điều này loại bỏ tất cả các cuộc gọi LOG cùng một lúc trong một tệp java.

|
0

Đây là giải pháp của tôi nếu bạn không muốn rắc rối với các thư viện bổ sung hoặc chỉnh sửa mã của mình theo cách thủ công. Tôi đã tạo sổ ghi chép Jupyter này để xem qua tất cả các tệp java và nhận xét tất cả các thông báo Nhật ký. Không hoàn hảo nhưng nó đã hoàn thành công việc cho tôi.

|
0

Tại sao không chỉ làm

if(BuildConfig.DEBUG)
  Log.d("tag","msg");

? Không cần thư viện bổ sung, không có quy tắc proguard có xu hướng làm hỏng dự án và trình biên dịch java sẽ chỉ bỏ qua mã bytecode cho lệnh gọi này khi bạn thực hiện bản phát hành.

|
  • 1
    Một điều bất tiện là nó dài dòng hơn là chỉ viết Log.d("tag","msg");, và cũng rất dễ quên viết if(BuildConfig.DEBUG)phần. –  10:47:49 12/09/2019
  • 1
    Một vấn đề khác với điều này là các chuỗi vẫn còn trong bản phát hành đóng gói. –  04:51:29 10/10/2019
0

Tôi biết đây là một câu hỏi cũ, nhưng tại sao bạn không thay thế tất cả các lệnh gọi nhật ký của mình bằng một cái gì đó như Boolean logCallWasHere = true; // --- phần còn lại của nhật ký của bạn ở đây

Đây là lý do tại sao bạn sẽ biết khi nào bạn muốn đặt chúng trở lại và chúng sẽ không ảnh hưởng đến lệnh gọi câu lệnh if của bạn :)

|
  • 1
    Thật thú vị, hy vọng những dòng như vậy sẽ bị trình biên dịch / trình tối ưu hóa bỏ qua. Tuy nhiên, tên biến cần phải là duy nhất vì một số phương thức có nhiều lệnh gọi nhật ký và bạn không thể khai báo cùng một biến hai lần. –  02:52:40 07/09/2017
  • 1
    Bạn có thể khai báo các biến trên đỉnh về hoạt động và loại bỏ các tuyên bố boolean từ dòng này;) –  17:47:14 08/09/2017
0

Có thể xóa nhật ký bằng cách sử dụng bash trong linux và sed:

find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

Hoạt động cho các bản ghi nhiều dòng. Trong giải pháp này, bạn có thể chắc chắn rằng các bản ghi không có trong mã sản xuất.

|
0

Tôi thích sử dụng Log.d (TAG, một số chuỗi, thường là String.format ()).

TAG luôn là tên lớp

Transform Log.d (TAG, -> Logd (trong văn bản của lớp của bạn

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

Bằng cách này khi bạn đã sẵn sàng tạo phiên bản phát hành, hãy đặt MainClass.debug thành false!

|
  • 1
    vấn đề với điều này và các giải pháp khác ngoài biểu mẫu bảo vệ hoặc nhận xét chúng là bạn đang để lại mã, có thể gây ra một lượng lớn các bản dựng chuỗi. trong một ứng dụng trung bình không phải là một vấn đề, nhưng nếu bạn đang cố gắng tối ưu hóa nó sẽ trở thành một vấn đề. –  16:57:36 20/04/2016
-1

cách đơn giản nhất;

sử dụng DebugLog

Tất cả nhật ký bị DebugLog vô hiệu hóa khi ứng dụng được phát hành.

https://github.com/MustafaFerhan/DebugLog

|
  • 1
    Điều này là hoàn toàn sai lầm. Điều này chỉ làm cho các nhật ký không được ghi lại, nó không xóa chúng khỏi mã, vì vậy chúng vẫn ở đó để giúp mọi người thiết kế ngược mã của bạn và nó vẫn phải trả phí định dạng chuỗi của tất cả các nhật ký đó. –  02:56:02 16/03/2016

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.