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?


397

Theo Google, tôi phải " hủy kích hoạt mọi cuộc gọi tới các phương thức Đăng nhập 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 :

Hãy chắc chắn rằng bạn hủy kích hoạt ghi nhật ký và vô hiệu hóa 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 cuộc gọi đến các phương thức Nhật ký trong các tệp nguồn của bạn.

Dự án nguồn mở của tôi rất lớn và thật khó để thực hiện thủ công mỗi khi tôi phát hành. Ngoài ra, việc xóa một dòng Nhật ký có khả năng khó khăn, 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à cơ hội là tải () không được gọi. Là những tình huống như vậy đủ hiếm để tôi có thể quyết định nó không nên tồn tại?

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


2
+1 vì tôi không nhớ đây là trong danh sách kiểm tra xuất bản.
rds

51
Để nhận xét một dòng không bị chặn, tôi sử dụng "; //" thay vì "//".
kéo dài

Nếu bạn cần có thể hoàn tác điều này, có lẽ bạn sẽ muốn sử dụng sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'thay thế.
kéo dài

2
Liên kết mà Dimitar thêm vào không hoạt động nữa. Tôi đã tìm thấy cái này thay vì source.android.com/source/code-style.html#log-sparingly .
JosephL

1
@mboy: Có lẽ cho hiệu năng chủ yếu hiện nay, nhưng trên các phiên bản Android cũ, nó cũng có lợi ích bảo mật.
Nicolas Raoul

Câu trả lời:


488

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 ở mọi nơi và chỉ cần sử dụng ProGuard để loại bỏ bất kỳ cuộc gọi Log.d()hoặc Log.v()phương thức nào khi chúng tôi gọi releasemục tiêu Ant của chúng tôi .

Theo cách đó, chúng tôi luôn có thông tin gỡ lỗi là đầu ra 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ã byte để 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 khi 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 tệp đó vào một tệp, sau đó gọi ProGuard từ Ant, chuyển qua JAR vừa được 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 hướng dẫn sử dụng ProGuard.


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

Không chỉ đẹp hơn một chút so với 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 - nhưng bạn cũng có thể chỉ định các hành vi ghi nhật ký khác nhau khi chạy.

Trong ví dụ này, các báo cáo 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 theo Application onCreate()phương pháp của tôi :

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

Sau đó, bất cứ 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 Gỗ để biết ví dụ nâng cao hơn, trong đó tất cả các báo cáo nhật ký được gửi tới logcat trong quá trình phát triển và, trong sản xuất, không có báo cáo gỡ lỗi nào được ghi lại, nhưng các lỗi được báo cáo âm thầm cho Crashlytics.


59
Và tại sao không có trong tập tin proguard mặc định?
rds

10
+ rds vì nó sẽ làm cho số dòng stacktraces 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.
Guy

5
Tôi có thể xác nhận rằng việc loại bỏ các cuộc gọi Log sẽ thay đổi số dòng trong stacktraces. Nó sẽ không luôn 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 chuỗi trong cuộc gọi Đăng nhập), nhưng đôi khi nó sẽ bị tắt vài dòng. Đáng để IMO gặp rắc rối vì khả năng dễ dàng loại bỏ các cuộc gọi Log.
Tony Chan

5
@Fraggle Từ proguard-android.txt trong các công cụ ADT: "Lưu ý rằng nếu bạn muốn kích hoạt tối ưu hóa, bạn không thể chỉ bao gồm các cờ tối ưu hóa trong tệp cấu hình dự án của riêng bạn, thay vào đó bạn sẽ cần phải trỏ đến" tối ưu hóa proguard-android. tệp txt "thay vì tệp này từ tệp" # project.properIES của bạn.
Raanan

3
Như Espinchi nói dưới đây trả lời. "Vấn đề duy nhất với cách tiếp cận này là, nếu bạn thực hiện Log.d (" thẻ "," Đã xử lý: "+ ItemCorer mới (blabla) +" mục "), 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 điệp, có thể tốn kém để tạo. "Điều này có đúng trong trường hợp của Timber không?
Chitrang 17/05/2016

117

Tất cả các câu trả lời tốt, nhưng khi tôi đã hoàn thành với sự phát triển của mình, tôi đã không muốn sử dụng nếu các câu lệnh xung quanh tất cả các lệnh gọi Log, tôi cũng 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 Log 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.


143
Vấn đề duy nhất với cách tiếp cận này là, nếu bạn thực hiện Log.d ("tag", "Đã xử lý:" + ItemCorer mới (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, a StringBuilder được sử dụng để tạo thông điệp, có thể tốn kém để tạo.
Espinchi

9
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());nó rất dễ quên kiểm tra một số Giá trị không phải là null, điều đó có nghĩa là nó có thể NullPointerExceptiontạo ra sản phẩm. Nó đề xuất một giải pháp an toàn nhưng nó sẽ lừa bạn. Chúng tôi, chúng tôi private static boolean DEBUGvà sau đóif(DEBUG)Log.d(TAG, msg);
philipp

2
@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, ...). Có phải nó không được đề xuất để sử dụng chúng?
OneWorld

1
Cách duy nhất để giảm thiểu các chi phí được đề cập trong nhận xét 1 của @espinchi là thay đổi phương thức ghi nhật ký để chấp nhận varargs thay vì String. Giải pháp hoàn chỉnh được giải mã ở đây . Điều này rõ ràng có một nhược điểm khác: mọi cuộc gọi nên được chỉnh sửa (không chỉ một dòng nhập).
Stan

21
Chỉ là một FYI, nếu bạn đang sử dụng Android Studio và hệ thống xây dựng lớp, 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ờ.
ashishduh

61

Tôi đề nghị có một boolean tĩnh ở đâu đó cho biết có đăng nhập hay không:

lớp MyDebug {
  LOG boolean tĩnh cuối cùng = true;
}

Sau đó, bất cứ nơi nào bạn muốn đăng nhập mã của bạn, chỉ cần làm đ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 đó (vì nó là một kết thúc tĩnh, nó biết tại thời điểm biên dịch mà 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ó booleans trong các tệp riêng lẻ để có thể dễ dàng bật hoặc tắt đăng nhập ở đó khi cần. Ví dụ: đây là các hằng số ghi nhật ký khác nhau mà chúng ta 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.
LA_

4
Sẽ không ít dài dòng hơn để vượt qua điều kiện như tham số đầu tiên?
Snicolas

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 phương pháp ProGuard), Không có mã nào để tạo thông điệp 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à rõ ràng là 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 Googles trong mẫu thanh toán ứng dụng theo @LA_ cũng hỗ trợ cho suy nghĩ của tôi.
OneWorld

2
@Snicolas Làm thế nào bạn có thể vượt qua điều kiện như tham số đầu tiên mà không thực hiện trình bao bọc? Hơn nữa, nếu bạn thêm nó làm 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 phải được kiểm tra trước khi xây dựng các tham 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.
loại-a1pha

2
Mã nhị phân khôn ngoan, điều này là tốt nhất. Nhưng mã hóa như thế này chỉ là rất nhiều nỗ lực cho một đầu ra nhật ký gỡ lỗi đơn giản. Mã dễ đọc giảm đáng kể. Thắng một số, thua một số, tôi đoán ...
Richard Le Mesurier

30

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

Nhật ký bình luận:

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

Nhật ký không ghi chú:

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

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

(Thực hiện các dòng này trong shell UNIX ở gốc của dự án của bạn. Nếu sử dụng Windows, hãy lấy 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 (theo điều này ) Cảm ơn.
Vishal

Tôi cảm thấy rằng đây có thể là thứ mà cuối cùng tôi đang sử dụng cho thứ gì đó mà tôi đang làm vì tôi không có nhiều may mắn khi làm điều đó với Proguard
Joe Plante

Và điều gì sẽ xảy ra nếu bạn có Nhật ký sau một nhánh trong ngoặc đơn, như bạn đã đề xuất trong bài đăng đầu tiên của mình?
loại-a1pha

@ type-a1pha: Nếu bạn áp dụng giải pháp này, thì bạn phải xem các khối khung là bắt buộc.
Nicolas Raoul

2
@NicolasRaoul Dấu chấm phẩy khắc phục vấn đề này ( //so với ;//)
Alex Gittemeier

18

Tôi muốn thêm một số quy định về việc sử dụng Proguard với Android Studio và gradle, vì tôi có rất nhiều vấn đề để xóa các dòng nhật ký khỏi tệp nhị phân cuối cùng.

Để làm assumenosideeffects Proguard hoạt động, có một điều kiện tiên quyết.

Trong tệp lớp của bạn, bạn phải chỉ định việc sử dụng proguard-android-optimize.txttệ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ị vô hiệu hóa với hai cờ:

-dontoptimize
-dontpreverify

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

Sau đó, cá nhân, tôi sử dụng SLF4J , hơn nữa khi tôi phát triển một số thư viện được phân phối cho người khác. Ưu điểm là theo 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 vào LogCat.

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

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}

Điều này không làm việc với Jack mới compiler-- stackoverflow.com/questions/37932114/...
fattire

Đ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 sh8 R8 (hiện tại mặc định) và ghi nhật ký mặc định của Android.
Jonik

10

Tôi đánh giá cao việc 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 thêm lớp thẻ tự động

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 ver 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 / thông điệp của bạn" mà không chỉ định thẻ


2
Gỗ rất đẹp, nhưng nếu bạn đã có một dự án hiện tại - bạn có thể thử github.com/zserge/log . Đây là một sự thay thế thả xuống cho android.util.Log và có hầu hết các tính năng mà Timber có và thậm chí còn hơn thế nữa.
zserge

zserge, giải pháp đăng nhập của bạn có vẻ tốt. Rất nhiều tính năng. Bạn đã xem xét thêm quy tắc Lint như Timber có?
jk7 ngày

8

Tôi đã sử dụng lớp LogUtils như trong ứng dụng ví dụ Google IO. 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 điều 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 cho báo cáo lỗi Build.DEBUGmà tôi đã sử dụng. Tôi cũng đã từ bỏ các cách giải quyết "chính xác" khác nhau và sử dụng một giải pháp phong cách tương tự cho bạn.
Richard Le Mesurier

7

Tôi sẽ xem xét sử dụng cơ sở khai thác của roboguice thay vì android.util.Log tích hợp

Cơ sở của họ tự động vô hiệu hóa các bản ghi gỡ lỗi và dài dòng cho các bản dựng phát hành. Ngoài ra, bạn cò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á rắc rối và tôi sẽ không gặp rắc rối 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 việc đó (vô hiệu hóa nhật ký không phải là một lý do tốt)


Một cách tiếp cận rất hay khi bạn không thể sử dụng Obfuscation .... đặc biệt là vì phá vỡ roboguice vì proguard LOL
Snicolas

1
Liên kết được cập nhật cho cơ sở ghi nhật ký của robojuice: github.com/roboguice/roboguice/wiki/Logging-via-Ln
RenniePet

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 vào ứng dụng của mình bằng cách thực hiện 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 đến 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(

nhập mô tả hình ảnh ở đây

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

Android Studios sẽ đi qua tất cả các tệp của bạn 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à lớp này xuất hiện hàng triệu thông báo lỗi sau đó vì nó không thể tìm thấy "Gỗ" trong quá trình nhập cho mỗi tệp java của bạn. Chỉ cần nhấp vào các lỗi và Android Studios sẽ tự động nhập "Gỗ" vào java của bạn. Khi bạn đã thực hiện nó cho 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 đăng nhập ứng dụng chỉ khi bạn ở chế độ phát triển không được sản xuất. Bạn cũng có thể có BuildConfig.RELEASEđể đăng nhập trong chế độ phát hành.


3
Hãy thử làm điều tương tự cho hàng nhập của bạn và đảm bảo hộp Biểu thức chính quy được chọn Văn bản cần tìm: import android\.util\.Log\;Thay thế bằng:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson

hoặc bạn có thể sử dụng tìm kiếm cấu trúc và thay thế như các chương trình Chike Mgbemena trong bài đăng
Maksim Turaev

@MaksimTuraev Liên kết của bạn không còn phù hợp. Bây giờ nó là một blog về kiểu tóc.
Vadim Kotov

Có vẻ như bài đăng đã bị xóa = (không thể tìm thấy nó ở bất cứ đâu.
Maksim Turaev

@MaksimTuraev ở đây là một bản sao từ máy Wayback, nhưng hình ảnh bị hỏng - web.archive.org/web/20161004161318/http://chikemgbemena.com/edom
Vadim Kotov

6

Mỗi 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ý trên mức DEBUG có thể được in ra. Tài liệu tham khảo android:

Kiểm tra xem liệu một bản ghi cho thẻ được chỉ định có thể ghi được ở mức đượ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 INFO sẽ được ghi lại. Trước khi bạn thực hiện bất kỳ cuộc gọi nào đến phương thức ghi nhật ký, bạn nên kiểm tra xem thẻ của bạn có được ghi lại 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. 'Trường hợp cấp độ là ĐỘNG TỪ, DEBUG, INFO, WARN, ERROR, ASSERT hoặc SUPPRESS. SUPPRESS sẽ tắt tất cả việc đăng nhập cho thẻ của bạn. Bạn cũng có thể tạo tệp local.prop có tệp sau: 'log.tag. =' Và đặt tệp đó vào /data/local.prop.

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

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);
    }
}

6

Nếu bạn có thể chạy thay thế toàn cầu (một lần) và sau đó duy trì một số quy ước mã hóa, bạn có thể theo mô hình thường được sử dụng trong khung 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());

Bây giờ 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, từ bản phát hành DEX đượ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.Log trong proguard-rules.pro:

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

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


4

Thêm 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(...);
}

4

nhập mô tả hình ảnh ở đây

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

Trong Android Studio, chúng tôi 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 MacOs) và Ctrl + Shift + R để thay thế ((Command + Shift + R trong MacOs))


Điều này dường như để mở 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.
Simon

2
trong Android Studio, bạn có thể thực hiện tìm kiếm tương tự với phím tắt Ctrl + Shift + F
Lins Louis

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.
Nicolas Raoul

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ụ: sôcôlaLog.recipie ();
Andrew S

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 1 tệp cùng một lúc bằng cách tìm kiếm / thay thế bình thường.
VVB

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 được áp dụng trên tất cả các IDE.

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

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

Xuất sắc.


2
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.
Nicolas Raoul

OK, vâng tôi nên đọc lại thường xuyên hơn sau khi duyệt các câu trả lời :). Nếu bạn gặp trường hợp như vậy, bạn có thể muốn một giải pháp khác như được đề xuất trước đó, chẳng hạn như đặt tất cả nhật ký của bạn đằng sau giao diện khác. Đề xuất của tôi có lẽ hoạt động tốt hơn cho các nhóm và dự án nhỏ hơn, nơi mọi người muốn tránh chi phí đăng nhập thêm, bạn biết rõ về người và mã, v.v.
kg_sYy

1
Thay thế Log.d bằng; // Log.d cũng đảm nhiệm kịch bản "Nếu" đó.
Jasper

3

Như bình luận của zserge đã đề xuất,

Gỗ rất đẹp, nhưng nếu bạn đã có một dự án hiện tại - bạn có thể thử github.com/zserge/log. Đây là một sự thay thế thả xuống cho android.util.Log và có hầu hết các tính năng mà Timber có và thậm chí còn hơn thế nữa.

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

Ngoài ra, nó chỉ yêu cầu thay đổi importdòng, và không có gì cần thay đổi cho 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

Bạn có phải đặt dòng mã đó trong mỗi Hoạt động / Đoạn hoặc chỉ ở một nơi không?

@NoahTernullo // trong tệp Ứng dụng dẫn xuất. Mặc địnhApplication.java
Youngjae

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 tự động thay đổi cấp độ nhật ký 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 đây. Nếu tham số chuỗi được xây dựng bằng các cuộc gọi đắt tiền, nó vẫn lãng phí tài nguyên. Việc kiểm tra cuộc gọi cần được thực hiện trước khi xây dựng các tham số được thông qua.
loại-a1pha

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 với các tên khó hiểu về ngữ nghĩa. Kết quả là một tệp .apk có kích thước nhỏ hơn, khó khăn hơn để thiết kế ngược. Vì ProGuard làm cho ứng dụng của bạn khó trở thành kỹ sư đảo ngược, đ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ó bằng tay. ProGuard chỉ chạy khi bạn xây dựng ứng dụng của mình ở chế độ phát hành, do đó bạn không phải đối phó với mã bị xáo trộn khi bạn xây dựng ứng dụng của mình ở chế độ gỡ lỗi. Có ProGuard chạy là hoàn toàn tùy chọn, nhưng rất khuyến khích.

Tài liệu này mô tả cách bật và định 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ị che khuất


2
Mặc dù vậy, nó dường như không xóa nhật ký gỡ lỗi. Vì vậy, câu trả lời của Christopher nghe tốt hơn.
Nicolas Raoul

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

Chuyển đổi Log.d (TAG, -> Logd (trong văn bản của lớp bạn

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

Theo 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 giải pháp này và các giải pháp khác ngoài việc bảo vệ hoặc nhận xét chúng là việc bạn để lại mã, có thể gây ra một số lượng lớn các chuỗi xây dựng. trong một ứng dụng trung bình không phải là vấn đề, nhưng nếu bạn đang cố gắng tối ưu hóa thì nó trở thành một vấn đề.
Lassi Kinnunen

0

Nhật ký có thể được xóa bằ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 đa 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 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 cuộc 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 cuộc gọi if của bạn :)


Thật thú vị, hy vọng các dòng như vậy sau đó bị bỏ qua bởi trình biên dịch / trình tối ưu hóa. Tuy nhiên, tên biến cần phải là duy nhất, bởi vì một số phương thức có một số 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.
Nicolas Raoul

Bạn có thể khai báo biến trên đầu hoạt động và xóa khai báo boolean khỏi dòng này;)
masood elsad

0

Tại sao không làm

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

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


Một 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ệc viết if(BuildConfig.DEBUG)phần.
Nicolas Raoul

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.
straya

0

Đây là giải pháp của tôi nếu bạn không muốn gây rối với các thư viện bổ sung hoặc chỉnh sửa mã 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 điệp Nhật ký. Không hoàn hảo nhưng nó đã hoàn thành công việc cho tôi.


0

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) bấm 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

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(

Điều đó sẽ không hoạt động tốt với mã ví dụ được đưa ra trong câu hỏi.
Nicolas Raoul

0

Dễ dàng với kotlin, chỉ cần khai báo một vài chức năng 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)
}

-1

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

sử dụng DebugLog

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

https://github.com/MustafaFerhan/DebugLog


Điều này là hoàn toàn sai. Điều này chỉ khiến nhật ký không được ghi nhật ký, 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ế lại mã của bạn và nó vẫn có chi phí định dạng chuỗi của tất cả các nhật ký đó.
Glenn Maynard
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.