จะลบการบันทึกการดีบักทั้งหมดก่อนที่จะสร้างเวอร์ชันที่วางจำหน่ายของแอพ Android ได้อย่างไร


397

ตาม Google ฉันจะต้อง " ปิดการใช้งานการโทรไปยังวิธีการบันทึกในรหัสที่มา " ก่อนที่จะเผยแพร่แอพ Android ของฉันไปยัง Google Play แยกจากส่วนที่ 3 ของรายการตรวจสอบสิ่งพิมพ์ :

ตรวจสอบให้แน่ใจว่าคุณปิดใช้งานการบันทึกและปิดใช้งานตัวเลือกการแก้ไขข้อบกพร่องก่อนที่คุณจะสร้างแอปพลิเคชันของคุณสำหรับการเปิดตัว คุณสามารถปิดใช้งานการบันทึกได้โดยลบการเรียกไปยังวิธีการบันทึกในไฟล์ต้นฉบับของคุณ

โครงการโอเพ่นซอร์สของฉันมีขนาดใหญ่และเป็นเรื่องยากที่จะทำด้วยตนเองทุกครั้งที่ฉันปล่อย นอกจากนี้การลบบรรทัดบันทึกอาจมีความยุ่งยากเช่น:

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

หากฉันแสดงความคิดเห็นในบรรทัด Log เงื่อนไขจะมีผลกับบรรทัดถัดไปและโอกาสในการโหลด () จะไม่ถูกเรียก สถานการณ์เช่นนี้หายากพอที่ฉันสามารถตัดสินใจได้หรือไม่

ดังนั้นมีวิธีระดับซอร์สโค้ดที่ดีกว่าในการทำเช่นนั้น? หรือบางทีไวยากรณ์ ProGuard ที่ชาญฉลาดเพื่อลบบรรทัดบันทึกทั้งหมดอย่างมีประสิทธิภาพ แต่ปลอดภัย


2
+1 เพราะฉันจำไม่ได้ว่าสิ่งนี้อยู่ในรายการตรวจสอบสิ่งพิมพ์
rds

51
หากต้องการแสดงความคิดเห็นต่อบรรทัดที่ไม่ถูกบล็อกฉันใช้ "; //" แทน "//"
yingted

หากคุณต้องการเลิกทำสิ่งนี้คุณอาจต้องการใช้sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'แทน
yingted

2
ลิงก์ที่เพิ่ม Dimitar ไม่ทำงานอีกต่อไป ฉันพบนี้แทนsource.android.com/source/code-style.html#log-sparingly
JosephL

1
@mboy: อาจเป็นไปได้สำหรับการทำงานส่วนใหญ่ในปัจจุบัน แต่สำหรับ Android รุ่นเก่ามันก็มีประโยชน์ด้านความปลอดภัยเช่นกัน
Nicolas Raoul

คำตอบ:


488

ฉันพบทางออกที่ง่ายกว่ามากคือลืมการifตรวจสอบทั้งหมดไปทั่วสถานที่และเพียงแค่ใช้ProGuardเพื่อตัดการโทรLog.d()หรือLog.v()การเรียกใช้เมธอดเมื่อเราเรียกreleaseเป้าหมายAnt ของเรา

ด้วยวิธีนี้เรามักจะมีข้อมูลการดีบักที่ส่งออกสำหรับการสร้างปกติและไม่จำเป็นต้องทำการเปลี่ยนแปลงรหัสใด ๆ สำหรับการสร้างรุ่น ProGuard ยังสามารถทำหลาย ๆ ครั้งผ่านทาง bytecode เพื่อลบข้อความอื่น ๆ ที่ไม่ต้องการบล็อกว่างเปล่าและสามารถ inline วิธีการสั้น ๆ โดยอัตโนมัติตามความเหมาะสม

ตัวอย่างเช่นนี่คือการกำหนดค่า ProGuard ขั้นพื้นฐานสำหรับ Android:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

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

ดังนั้นคุณจะบันทึกลงในไฟล์จากนั้นโทร ProGuard จาก Ant ผ่าน JAR ที่คอมไพล์แล้วและแพลตฟอร์ม Android ที่คุณใช้

ดูตัวอย่างในคู่มือ ProGuard


อัปเดต (4.5 ปีต่อมา):ทุกวันนี้ฉันใช้การบันทึกTimberสำหรับ Android

ไม่เพียง แต่จะดีกว่าการLogใช้งานเริ่มต้นเท่านั้น- แท็กบันทึกจะถูกตั้งค่าโดยอัตโนมัติและง่ายต่อการบันทึกสตริงและข้อยกเว้นที่จัดรูปแบบแล้ว แต่คุณยังสามารถระบุพฤติกรรมการบันทึกที่แตกต่างกันได้ที่รันไทม์

ในตัวอย่างนี้คำสั่งการบันทึกจะถูกเขียนไปที่ logcat ในการสร้างข้อบกพร่องของแอพของฉัน:

ทิมเบอร์ตั้งค่าไว้ในตัวฉันแล้ว Application onCreate()วิธีการ :

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

จากนั้นทุกที่อื่นในรหัสของฉันฉันสามารถเข้าสู่ระบบได้อย่างง่ายดาย:

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

ดูแอปตัวอย่าง Timberสำหรับตัวอย่างขั้นสูงที่ซึ่งคำสั่งบันทึกทั้งหมดจะถูกส่งไปยัง logcat ระหว่างการพัฒนาและในการผลิตจะไม่มีการบันทึกคำสั่งดีบัก แต่ในรายงานข้อผิดพลาดจะถูกรายงานไปยัง Crashlytics


59
และทำไมจึงไม่เป็นเช่นนั้นในไฟล์ proguard เริ่มต้น
rds

10
+ rds เพราะจะทำให้หมายเลขบรรทัดสแต็คการผลิตแตกต่างจากที่อยู่ในรหัสของคุณเนื่องจากจะถูกลบออก
Guy

5
ฉันสามารถยืนยันได้ว่าการตัดการบันทึกการโทรออกจะเป็นการเปลี่ยนหมายเลขบรรทัดเป็นสแต็กตรู มันจะไม่ซิงค์กันตลอดเวลา (ฉันทำการทดสอบอย่างรวดเร็วหลายครั้ง แต่ไม่สามารถระบุได้อย่างชัดเจนว่าสาเหตุคืออะไรถ้าคุณต่อสตริงในการโทรเข้าสู่ระบบ) แต่บางครั้งมันจะปิดไม่กี่บรรทัด คุ้มค่ากับปัญหา IMO สำหรับความสามารถในการลบบันทึกการโทรได้อย่างง่ายดาย
Tony Chan

5
@Fraggle จาก proguard-android.txt ในเครื่องมือ ADT: "โปรดทราบว่าหากคุณต้องการเปิดใช้งานการปรับให้เหมาะสมคุณจะไม่สามารถรวมการตั้งค่าสถานะการปรับให้เหมาะสมลงในไฟล์กำหนดค่าโครงการของคุณเองแทนคุณจะต้องชี้ไปที่" proguard-android-optimization txt "ไฟล์แทนไฟล์นี้จากไฟล์" # project.properties ของคุณ
Raanan

3
ในฐานะที่เป็น espinchi กล่าวในคำตอบด้านล่าง "ปัญหาเดียวของวิธีนี้คือถ้าคุณใช้ Log.d (" แท็ก "," ดำเนินการแล้ว: "+ ใหม่ ItemCounter (blabla) +" รายการ ") แม้ว่าข้อความบันทึกนี้จะไม่ปรากฏในเวอร์ชั่นที่คุณเผยแพร่ StringBuilder ใช้ในการสร้างข้อความซึ่งอาจมีราคาแพงในการสร้าง "นี่เป็นเรื่องจริงในกรณี Timber หรือไม่?
Chitrang

117

คำตอบที่ดีทั้งหมด แต่เมื่อฉันเสร็จสิ้นการพัฒนาของฉันฉันไม่ต้องการที่จะใช้ถ้าคำสั่งรอบบันทึกการโทรทั้งหมดและฉันไม่ต้องการใช้เครื่องมือภายนอก

ดังนั้นทางออกที่ฉันใช้คือการแทนที่คลาส android.util.Log ด้วยคลาสล็อกของฉัน:

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

สิ่งเดียวที่ฉันต้องทำในไฟล์ต้นฉบับทั้งหมดคือการแทนที่การนำเข้า android.util.Log ด้วยคลาสของฉันเอง


143
ปัญหาเดียวของวิธีนี้คือถ้าคุณใช้ Log.d ("แท็ก", "ดำเนินการ:" + ใหม่ ItemCounter (blabla) + "รายการ") แม้ว่าข้อความบันทึกนี้จะไม่ปรากฏในเวอร์ชันที่คุณเผยแพร่ StringBuilder ใช้ในการสร้างข้อความซึ่งอาจมีราคาแพงในการสร้าง
espinchi

9
วิธีนี้มีปัญหาใหญ่ espinchi พูดถึงเพียงส่วนเล็ก ปัญหาคือเมื่อคุณเรียกLog.d("tag", someValue.toString());ว่ามันง่ายมากที่จะลืมตรวจสอบค่าบางอย่างสำหรับการไม่เป็นโมฆะหมายความว่ามันอาจจะเป็นNullPointerExceptionในการผลิต มันแนะนำวิธีแก้ปัญหาที่ปลอดภัย แต่มันจะหลอกคุณ พวกเราprivate static boolean DEBUGและเราแล้วif(DEBUG)Log.d(TAG, msg);
ฟิลิป

2
@espinchi ความกังวลของคุณดูเหมือนจะนำไปใช้กับไลบรารีการบันทึกทั้งหมดเช่นที่กล่าวถึงในคำตอบนี้stackoverflow.com/a/15452492/433718 (Slf4j, backlog, ... ) ไม่แนะนำให้ใช้หรือไม่
เวิลด์

1
วิธีเดียวที่จะลดค่าใช้จ่ายที่กล่าวถึงในความคิดเห็นที่ 1 เซนต์โดย @espinchi คือการเปลี่ยนวิธีการเข้าสู่ระบบที่จะยอมรับ varargs Stringแทน โซลูชั่นที่สมบูรณ์มีการอธิบายไว้ที่นี่ เห็นได้ชัดว่านี่มีข้อเสียเปรียบอื่น: ทุกการโทรควรแก้ไข (ไม่เพียงหนึ่งสายนำเข้า)
Stan

21
เพียงแค่ FYI หากคุณใช้ Android Studio และระบบ gradle build คุณสามารถใช้static final boolean LOG = BuildConfig.DEBUGและไม่ต้องแก้ไขไฟล์นี้เลย
ashishduh

61

ฉันขอแนะนำให้มีบูลีนคงที่บางแห่งระบุว่าจะเข้าสู่ระบบหรือไม่:

คลาส MyDebug {
  บูลีนสุดท้ายคงที่ LOG = จริง;
}

จากนั้นทุกที่ที่คุณต้องการเข้าสู่ระบบในรหัสของคุณเพียงแค่ทำสิ่งนี้:

if (MyDebug.LOG) {
  if (condition) Log.i (... );
}

ตอนนี้เมื่อคุณตั้งค่า MyDebug.LOG เป็นเท็จคอมไพเลอร์จะดึงรหัสทั้งหมดออกมาภายในการตรวจสอบดังกล่าว (เนื่องจากเป็นแบบสแตติกขั้นสุดท้ายจึงรู้ ณ เวลารวบรวมที่ไม่ได้ใช้รหัส)

สำหรับโครงการขนาดใหญ่คุณอาจต้องการเริ่มมีบูลีนในแต่ละไฟล์เพื่อให้สามารถเปิดใช้งานหรือปิดใช้งานการบันทึกได้อย่างง่ายดายตามต้องการ ตัวอย่างเช่นนี่คือค่าคงที่การบันทึกต่าง ๆ ที่เรามีในตัวจัดการหน้าต่าง:

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;

ด้วยรหัสที่สอดคล้องกันเช่น:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");

1
ฉันจะลงคะแนนให้วิธีการดังกล่าวด้วย นอกจากนี้ยังใช้ในตัวอย่างการเรียกเก็บเงินในแอปอย่างเป็นทางการของ Google
LA_

4
มันจะไม่ verbose น้อยกว่าที่จะผ่านเงื่อนไขเป็นพารามิเตอร์แรก?
Snicolas

1
สิ่งนี้ดูเหมือนจะเป็นทางออกที่ดีที่สุดแม้ว่าจะต้องใช้รหัสเพิ่มเติมในแต่ละคำสั่งบันทึก: หมายเลขบรรทัดจะถูกเก็บไว้ (จุดอ่อนของวิธีการของ ProGuard) ไม่มีรหัสในการสร้างข้อความบันทึกจะถูกดำเนินการ ( ความอ่อนแอของวิธีการ . การใช้วิธีนี้ใน Googles ในตัวอย่างการเรียกเก็บเงินแอปตาม @LA_ สนับสนุนความคิดของฉันด้วย
เวิลด์

2
@Snicolas คุณจะผ่านเงื่อนไขเป็นพารามิเตอร์แรกโดยไม่ต้องใช้ wrapper ได้อย่างไร? ยิ่งไปกว่านั้นถ้าคุณเพิ่มเป็นพารามิเตอร์จากนั้นก่อนที่จะเข้าสู่วิธีการพารามิเตอร์ทั้งหมดจะต้องมีการประเมินนั่นคือสตริงข้อความ เงื่อนไขจะต้องมีการทดสอบก่อนสร้างพารามิเตอร์ การแก้ปัญหาที่เสนออาจเป็นวิธีที่ดีที่สุดที่ไม่มีเครื่องมือภายนอก
type-a1pha

2
รหัสไบนารีฉลาดดีที่สุด แต่การเข้ารหัสเช่นนี้เป็นความพยายามอย่างมากสำหรับเอาต์พุตบันทึกการดีบักแบบง่าย การอ่านรหัสลดลงอย่างมาก ชนะบางสูญเสียบางอย่างผมคิดว่า ...
ริชาร์ดเลอ Mesurier

30

โซลูชัน Proguard ของ Christopher นั้นดีที่สุด แต่ถ้าคุณไม่ชอบ Proguard ไม่ว่าด้วยเหตุผลใดก็ตามนี่เป็นโซลูชันที่ใช้เทคโนโลยีต่ำมาก:

บันทึกความคิดเห็น:

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

บันทึกการยกเลิกหมายเหตุ:

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

ข้อ จำกัด คือคำแนะนำในการบันทึกของคุณจะต้องไม่ขยายเกินหลายบรรทัด

(ดำเนินการบรรทัดเหล่านี้ในเชลล์ UNIX ที่รูทของโครงการของคุณหากใช้ Windows รับเลเยอร์ UNIX หรือใช้คำสั่ง Windows ที่เทียบเท่า)


1
ต้องใช้ "" หลังจาก -i in Sed หากทำงานบน Mac (ตามนี้ ) ขอบคุณ
วิษณุ

ฉันรู้สึกว่านี่อาจเป็นสิ่งที่ฉันใช้เพื่อสิ่งที่ฉันกำลังทำอยู่เพราะฉันไม่มีโชคมากที่ได้ทำกับ Proguard เลย
Joe Plante

และจะเกิดอะไรขึ้นถ้าคุณมีบันทึกหลังจากที่ไม่ได้วงเล็บในขณะที่สาขาตามที่คุณแนะนำในโพสต์แรกของคุณ
type-a1pha

@ type-a1pha: หากคุณนำวิธีการแก้ปัญหานี้มาใช้คุณจะต้องพิจารณาถึง block bracket เป็นข้อบังคับ
Nicolas Raoul

2
@NicolasRaoul เซมิโคลอนแก้ไขปัญหานี้ ( //vs. ;//)
Alex Gittemeier

18

ฉันต้องการเพิ่มความสามารถบางอย่างเกี่ยวกับการใช้ Proguard กับ Android Studio และ gradle เนื่องจากฉันมีปัญหามากมายในการลบบรรทัดบันทึกจากไบนารีสุดท้าย

เพื่อให้สามารถassumenosideeffectsทำงานใน Proguard ได้ต้องมีข้อกำหนดเบื้องต้น

ในไฟล์ gradle ของคุณคุณจะต้องระบุการใช้งานproguard-android-optimize.txtเป็นไฟล์เริ่มต้น

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'
    }
}

ที่จริงแล้วในproguard-android.txtไฟล์เริ่มต้นการเพิ่มประสิทธิภาพถูกปิดใช้งานด้วยสองธง:

-dontoptimize
-dontpreverify

proguard-android-optimize.txtไฟล์ไม่เพิ่มบรรทัดเหล่านั้นดังนั้นตอนนี้assumenosideeffectsสามารถทำงานได้

จากนั้นฉันใช้SLF4Jโดยส่วนตัวเมื่อฉันพัฒนาบางไลบรารีที่แจกจ่ายให้กับผู้อื่น ข้อดีคือโดยปกติแล้วจะไม่มีเอาต์พุต และถ้าผู้รวมระบบต้องการเอาท์พุทบันทึกบางอย่างเขาสามารถใช้ Logback สำหรับ Android และเปิดใช้งานบันทึกเพื่อให้สามารถเปลี่ยนเส้นทางบันทึกไปยังไฟล์หรือ LogCat

ถ้าฉันต้องการตัดบันทึกจากไลบรารีสุดท้ายฉันต้องเพิ่มไฟล์ Proguard ของฉัน (หลังจากเปิดใช้งานproguard-android-optimize.txtไฟล์แน่นอน):

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

สิ่งนี้ใช้ไม่ได้กับคอมไพเลอร์แจ็คใหม่
fattire

สิ่งนี้ช่วยฉัน ทั้งproguard-android-optimize.txtเป็นไฟล์ Proguard เริ่มต้นและ-assumenosideeffectsในไฟล์ Proguard ที่กำหนดเองก็จำเป็น! ฉันใช้ R8 shinker (ปัจจุบันเป็นค่าเริ่มต้น) และการบันทึก Android เป็นค่าเริ่มต้น
Jonik

10

ฉันขอแนะนำให้ใช้ Timber จาก Jake Wharton

https://github.com/JakeWharton/timber

มันแก้ปัญหาของคุณด้วยการเปิด / ปิดการใช้งานบวกเพิ่มระดับแท็กโดยอัตโนมัติ

แค่

public class MyApp extends Application {

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

บันทึกจะใช้ในเวอร์ชั่นการดีบักของคุณเท่านั้นจากนั้นใช้

Timber.d("lol");

หรือ

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

ปริ้น

"คลาส / msg ของคุณ" โดยไม่ระบุแท็ก


2
ป่าไม้เป็นสิ่งที่ดีมาก แต่ถ้าคุณมีโครงการที่มีอยู่ - คุณอาจลองgithub.com/zserge/log เป็นการแทนที่แบบแทนที่สำหรับ android.util.Log และมีคุณสมบัติส่วนใหญ่ที่ Timber มีและมากกว่านั้น
zserge

zserge วิธีแก้ปัญหาบันทึกของคุณดูดี คุณสมบัติมากมาย คุณได้พิจารณาเพิ่มกฎผ้าสำลีอย่างทิมเบอร์หรือไม่?
jk7

8

ฉันใช้คลาสLogUtilsเหมือนในแอปพลิเคชันตัวอย่างของ Google IO ผมปรับเปลี่ยนนี้จะใช้ดีบักคงใช้เฉพาะแทน BuildConfig.DEBUG เพราะBuildConfig.DEBUG จะไม่น่าเชื่อถือ จากนั้นในชั้นเรียนของฉันฉันมีดังต่อไปนี้

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 สำหรับรายงานข้อผิดพลาดBuild.DEBUGที่ฉันเคยใช้ ฉันยังยอมแพ้กับวิธีการ "แก้ไข" ที่หลากหลายและใช้โซลูชันสไตล์ที่คล้ายกับคุณ
Richard Le Mesurier

7

ฉันจะพิจารณาใช้เครื่องมืออำนวยความสะดวกการบันทึกของ roboguice แทน android.util.Log ในตัว

สิ่งอำนวยความสะดวกของพวกเขาจะปิดใช้งานการดีบักและบันทึกอย่างละเอียดสำหรับการสร้างรุ่น นอกจากนี้คุณจะได้รับคุณสมบัติที่ดีฟรี (เช่นพฤติกรรมการบันทึกที่ปรับแต่งได้ข้อมูลเพิ่มเติมสำหรับทุกบันทึกและอื่น ๆ )

การใช้ proguard อาจสร้างความยุ่งยากและฉันจะไม่ประสบปัญหาในการกำหนดค่าและทำให้มันทำงานกับแอปพลิเคชันของคุณได้เว้นแต่คุณจะมีเหตุผลที่ดี (การปิดใช้งานบันทึกไม่ใช่สิ่งที่ดี)


วิธีการที่ดีมากเมื่อคุณไม่สามารถใช้ Obfuscation .... โดยเฉพาะอย่างยิ่งเพราะ roboguice ทำลายเพราะ proguard LOL
Snicolas

1
ลิงค์ปรับปรุงสำหรับเครื่องมืออำนวยความสะดวกการบันทึกของ robojuice: github.com/roboguice/roboguice/wiki/Logging-via-Ln
RenniePet

7

ฉันโพสต์โซลูชันนี้ซึ่งใช้กับผู้ใช้ Android Studio ฉันเพิ่งค้นพบ Timber และได้นำเข้ามาสู่แอปของฉันด้วยการทำสิ่งต่อไปนี้:

วางไลบรารีเวอร์ชันล่าสุดลงใน build.gradle ของคุณ:

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

จากนั้นใน Android Studios ให้ไปที่แก้ไข -> ค้นหา -> แทนที่ในเส้นทาง ...

พิมพ์Log.e(TAG,หรือคุณกำหนดข้อความบันทึกของคุณลงใน"Text to find"กล่องข้อความ จากนั้นคุณก็แทนที่มันด้วยTimber.e(

ป้อนคำอธิบายรูปภาพที่นี่

คลิกค้นหาจากนั้นแทนที่ทั้งหมด

Android Studios จะผ่านไฟล์ทั้งหมดในโครงการของคุณและแทนที่บันทึกทั้งหมดด้วย Timbers

ปัญหาเดียวที่ฉันมีกับวิธีนี้คือ gradle เกิดขึ้นกับข้อความแสดงข้อผิดพลาดนับล้านครั้งหลังจากนั้นเพราะไม่พบ "Timber" ในการนำเข้าสำหรับไฟล์ java แต่ละไฟล์ของคุณ เพียงคลิกที่ข้อผิดพลาดและ Android Studios จะนำเข้า "Timber" เข้าสู่จาวาของคุณโดยอัตโนมัติ เมื่อคุณทำกับไฟล์ข้อผิดพลาดทั้งหมดแล้ว gradle จะรวบรวมอีกครั้ง

คุณต้องใส่รหัสในส่วนของคุณด้วย onCreateวิธีการApplicationเรียนของคุณด้วย:

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

สิ่งนี้จะส่งผลให้บันทึกแอพเฉพาะเมื่อคุณอยู่ในโหมดการพัฒนาที่ไม่ได้ใช้งานจริง นอกจากนี้คุณยังสามารถBuildConfig.RELEASEเข้าสู่โหมดการเปิดตัว


3
ลองทำสิ่งเดียวกันสำหรับการนำเข้าของคุณและตรวจสอบให้แน่ใจว่าได้ทำเครื่องหมายที่กล่องค้นหานิพจน์ปกติข้อความที่จะค้นหา: import android\.util\.Log\;แทนที่ด้วย:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson

หรือคุณสามารถใช้การค้นหาเชิงโครงสร้างและแทนที่เช่นรายการ Chike Mgbemena ในโพสต์
Maksim Turaev

@MaksimTuraev ลิงก์ของคุณไม่เกี่ยวข้องอีกต่อไป ตอนนี้มันเป็นบล็อกเกี่ยวกับทรงผม
Vadim Kotov

ดูเหมือนว่าโพสต์จะถูกลบ = (ไม่พบที่ใดเลย
Maksim Turaev

@MaksimTuraev นี่คือสำเนาจากเครื่อง Wayback แต่ภาพแตก - web.archive.org/web/20161004161318/http://chikemgbemena.com/?
Vadim Kotov

6

ต่อ android.util.Log ให้วิธีการเปิด / ปิดการใช้งานบันทึก:

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

ค่าเริ่มต้นวิธี isLoggable (... ) กลับเท็จหลังจากคุณ setprop ในอุปกรณ์เช่นนี้:

adb shell setprop log.tag.MyAppTag DEBUG

หมายถึงบันทึกใด ๆ ที่อยู่เหนือระดับ DEBUG สามารถพิมพ์ออกมาได้ เอกสารอ้างอิง android:

ตรวจสอบเพื่อดูว่าล็อกสำหรับแท็กที่ระบุนั้นสามารถล็อกได้ในระดับที่ระบุหรือไม่ ระดับเริ่มต้นของแท็กใด ๆ ถูกตั้งค่าเป็น INFO ซึ่งหมายความว่าระดับใด ๆ ข้างต้นและรวมถึง INFO จะถูกบันทึกไว้ ก่อนที่คุณจะโทรไปยังวิธีการบันทึกคุณควรตรวจสอบเพื่อดูว่าแท็กของคุณควรถูกบันทึกหรือไม่ คุณสามารถเปลี่ยนระดับเริ่มต้นได้โดยการตั้งค่าคุณสมบัติระบบ: 'setprop log.tag 'ที่ระดับเป็น VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT หรือ SUPPRESS SUPPRESS จะปิดการบันทึกทั้งหมดสำหรับแท็กของคุณ คุณยังสามารถสร้างไฟล์ local.prop ที่มีดังต่อไปนี้: 'log.tag. =' และวางไว้ใน /data/local.prop

ดังนั้นเราจึงสามารถใช้ประโยชน์บันทึกที่กำหนดเอง:

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

หากคุณสามารถทำงานได้ทั่วโลกแทนที่ (ครั้งเดียว) และหลังจากนั้นรักษาบางประชุมการเข้ารหัสคุณสามารถทำตามรูปแบบมักจะใช้ใน Android กรอบ

แทนที่จะเขียน

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

มีมันเป็น

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

ตอนนี้ proguard สามารถลบ StringBuilder และสตริงและวิธีการทั้งหมดที่ใช้ระหว่างทางจาก DEX ที่ได้รับการปรับปรุง ใช้proguard-android-optimize.txtและคุณไม่จำเป็นต้องกังวลเกี่ยวกับandroid.util.Logในproguard-rules.pro:

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

ด้วยปลั๊กอินการไล่ระดับสีของ Android Studio มีความน่าเชื่อถือมากดังนั้นคุณไม่จำเป็นต้องมีค่าคงที่เพิ่มเติมเพื่อควบคุมการลอกBuildConfig.DEBUG


4

เพิ่มการติดตามลงในไฟล์proguard-rules.txtของคุณ

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

4

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือสิ่งที่ฉันเคยทำในโครงการ Android ของฉัน ..

ใน Android Studio เราสามารถดำเนินการที่คล้ายกันได้โดยกด Ctrl + Shift + F เพื่อค้นหาจากโครงการทั้งหมด (Command + Shift + F ใน MacOs) และ Ctrl + Shift + R เพื่อแทนที่ ((Command + Shift + R ใน MacOs))


ดูเหมือนว่าจะเปิดทำงานกับโครงการ eclipse ตัวเลือกการค้นหาไม่สามารถใช้งานได้ในสตูดิโอ Android
Simon

2
ใน Android Studio คุณสามารถทำการค้นหาที่คล้ายกันด้วย Ctrl + Shift + F ช็อตคัต
Lins Louis

โค้ดตัวอย่างในคำถามอธิบายว่าทำไมสิ่งนี้จึงไม่น่าเชื่อถือ
Nicolas Raoul

มันอาจทำให้เกิดปัญหาในการลบคำสั่งใด ๆ ที่มีในบันทึก เช่น chocolateLog.recipie ();
Andrew S

ไม่พบตัวเลือกนี้สำหรับ Android Studio 2.1 นอกจากนี้ฉันสามารถใช้เคล็ดลับนี้ใน 1 ไฟล์ในเวลาเดียวกันโดยการค้นหา / แทนที่ปกติ
VVB

3

ฉันมีทางออกที่ง่ายมาก ฉันใช้ IntelliJ เพื่อการพัฒนาดังนั้นรายละเอียดจึงแตกต่างกันไป แต่ความคิดควรนำไปใช้กับ IDE ทั้งหมด

ฉันเลือกที่จะรูทของทรีซอร์สของฉันคลิกขวาแล้วเลือก "แทนที่" ฉันเลือกแทนที่ "บันทึก" ทั้งหมด ด้วย "// Log." สิ่งนี้จะลบคำสั่งบันทึกทั้งหมด หากต้องการนำพวกเขากลับมาในภายหลังฉันจะทำซ้ำการแทนที่เดิม แต่คราวนี้เป็นการแทนที่ทั้งหมด "// บันทึก" ด้วย "บันทึก"

ใช้งานได้ดีมากสำหรับฉัน เพียงจำไว้ว่าให้ตั้งค่าตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เพื่อหลีกเลี่ยงอุบัติเหตุเช่น "กล่องโต้ตอบ" เพื่อเพิ่มความมั่นใจคุณสามารถทำขั้นตอนแรกด้วย "บันทึก" เป็นสตริงเพื่อค้นหา

สุกใส


2
โปรดอ่านย่อหน้า"หากฉันแสดงความคิดเห็นในบรรทัดบันทึก"ในคำถามของฉัน
นิโคลัสราอูล

ตกลงใช่ฉันควรอ่านบ่อยขึ้นหลังจากเรียกดูคำตอบ :) หากคุณมีกรณีดังกล่าวคุณอาจต้องการโซลูชันที่แตกต่างเช่นแนะนำก่อนเช่นวางบันทึกทั้งหมดของคุณไว้ข้างหลังส่วนต่อประสานอื่น คำแนะนำของฉันอาจทำงานได้ดีขึ้นสำหรับทีมและโครงการขนาดเล็กที่ผู้คนต้องการหลีกเลี่ยงค่าใช้จ่ายในการเข้าสู่ระบบ libs พิเศษคุณรู้จักผู้คนและรหัสดี ฯลฯ
kg_sYy

1
การแทนที่ Log.d ด้วย; // Log.d จะดูแลสถานการณ์ "If" เช่นกัน
Jasper

3

ตามความเห็นของ zsergeแนะนำ

Timber ดีมาก แต่ถ้าคุณมีโครงการที่มีอยู่แล้วคุณอาจลอง github.com/zserge/log เป็นการแทนที่แบบแทนที่สำหรับ android.util.Log และมีคุณสมบัติส่วนใหญ่ที่ Timber มีและมากกว่านั้น

ไลบรารีบันทึกข้อมูลของเขามีสวิตช์เปิด / ปิดการพิมพ์ที่ใช้งานง่ายดังต่อไปนี้

นอกจากนี้ก็เพียง แต่ต้องเปลี่ยนimportเส้นและไม่มีอะไรที่ต้องมีการเปลี่ยนแปลงสำหรับLog.d(...);คำสั่ง

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT

คุณต้องใส่บรรทัดของรหัสนั้นในแต่ละกิจกรรม / ชิ้นส่วนหรือเพียงแค่ที่เดียว?
โนอาห์เทอร์โนโล

@NoahTernullo // ในไฟล์แอปพลิเคชันที่ได้รับ DefaultApplication.java
Youngjae

1

ฉันได้ปรับปรุงวิธีการแก้ปัญหาข้างต้นโดยให้การสนับสนุนสำหรับระดับการบันทึกที่แตกต่างกันและโดยการเปลี่ยนระดับการบันทึกโดยอัตโนมัติขึ้นอยู่กับว่ามีการเรียกใช้รหัสบนอุปกรณ์สดหรือตัวจำลอง

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
ปัญหาเช่นเดียวกับวิธีแก้ไขปัญหาก่อนหน้า หากพารามิเตอร์สตริงเป็นการสร้างโดยใช้การโทรที่มีราคาแพงก็ยังคงเปลืองทรัพยากร การตรวจสอบการโทรจำเป็นต้องทำก่อนสร้างพารามิเตอร์ที่ส่งผ่าน
type-a1pha

1

ProGuard จะทำเพื่อคุณในการสร้างรุ่นและตอนนี้ข่าวดีจาก android.com:

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

เครื่องมือ ProGuard ย่อขนาดปรับให้เหมาะสมและทำให้รหัสของคุณยุ่งเหยิงโดยการลบรหัสที่ไม่ได้ใช้และเปลี่ยนชื่อคลาสฟิลด์และเมธอดด้วยชื่อที่คลุมเครือ ผลลัพธ์คือไฟล์. apk ที่เล็กกว่าซึ่งยากต่อการย้อนกลับวิศวกร เนื่องจาก ProGuard ทำให้แอปพลิเคชันของคุณยากต่อการย้อนกลับวิศวกรจึงเป็นสิ่งสำคัญที่คุณจะต้องใช้เมื่อแอปพลิเคชันของคุณใช้คุณสมบัติที่มีความอ่อนไหวต่อความปลอดภัยเช่นเมื่อคุณออกใบอนุญาตแอปพลิเคชันของคุณ

ProGuard ถูกรวมเข้ากับระบบการสร้าง Android ดังนั้นคุณไม่จำเป็นต้องเรียกใช้มันเอง ProGuard ทำงานเฉพาะเมื่อคุณสร้างแอปพลิเคชันของคุณในโหมดเผยแพร่ดังนั้นคุณไม่จำเป็นต้องจัดการกับรหัสที่สับสนเมื่อคุณสร้างแอปพลิเคชันของคุณในโหมดดีบัก การมีการรัน ProGuard เป็นทางเลือกโดยสมบูรณ์ แต่แนะนำเป็นอย่างยิ่ง

เอกสารนี้อธิบายถึงวิธีการเปิดใช้งานและกำหนดค่า ProGuard รวมถึงใช้เครื่องมือ retrace เพื่อถอดรหัสร่องรอยสแต็กที่ยุ่งเหยิง


2
ดูเหมือนจะไม่ลบการบันทึกการดีบักตามค่าเริ่มต้น ดังนั้นคำตอบของคริสโตเฟอร์ฟังดูดีกว่า
Nicolas Raoul

0

ฉันชอบที่จะใช้ Log.d (TAG บางสตริงมักจะเป็น String.format ())

TAG เป็นชื่อคลาสเสมอ

แปลง Log.d (TAG, -> Logd (ในข้อความของคลาสของคุณ

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

ด้วยวิธีนี้เมื่อคุณพร้อมที่จะสร้างรุ่นวางจำหน่ายตั้ง MainClass.debug เป็นเท็จ!


1
ปัญหาที่เกิดขึ้นกับโซลูชันนี้และวิธีแก้ไขปัญหาอื่น ๆ นอกเหนือจากรูปแบบพรานหรือแสดงความคิดเห็นคือคุณกำลังทิ้งโค้ดไว้ซึ่งอาจทำให้เกิดการสร้างสตริงจำนวนมาก ในแอปเฉลี่ยไม่ใช่ปัญหา แต่ถ้าคุณพยายามปรับให้เหมาะสมมันจะกลายเป็นปัญหา
Lassi Kinnunen

0

บันทึกสามารถลบได้โดยใช้ bash ใน linux และ sed:

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

ใช้งานได้กับบันทึกหลายบรรทัด ในการแก้ปัญหานี้คุณสามารถมั่นใจได้ว่าบันทึกไม่ได้อยู่ในรหัสการผลิต


0

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ทำไมคุณไม่เปลี่ยนการเรียกบันทึกทั้งหมดด้วย Boolean logCallWasHere = true // --- บันทึกที่เหลือของคุณที่นี่

นี่เป็นสาเหตุที่คุณจะรู้ว่าเมื่อใดที่คุณต้องการเอากลับคืนมาและพวกเขาจะไม่ส่งผลกระทบต่อ if statement ของคุณ :)


น่าสนใจหวังว่าบรรทัดดังกล่าวจะถูกละเว้นโดยคอมไพเลอร์ / เพิ่มประสิทธิภาพ ชื่อตัวแปรจะต้องไม่ซ้ำกันเนื่องจากบางวิธีมีการเรียกใช้บันทึกหลายครั้งและคุณไม่สามารถประกาศตัวแปรเดียวกันสองครั้ง
Nicolas Raoul

คุณสามารถประกาศตัวแปรที่อยู่ด้านบนของกิจกรรมและลบการประกาศบูลีนออกจากบรรทัดนี้)
masood elsad

0

ทำไมไม่ทำ

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

? ไม่จำเป็นต้องใช้ไลบรารีเพิ่มเติมไม่มีกฎของ proguard ที่มีแนวโน้มที่จะทำให้โครงการล้มเหลวและคอมไพเลอร์ของจาวาก็จะละทิ้ง bytecode สำหรับการโทรนี้เมื่อคุณสร้าง build release


ไม่สะดวกก็คือว่ามันมีมากขึ้นอย่างละเอียดมากกว่าเพียงแค่การเขียนLog.d("tag","msg");และมันเป็นเรื่องง่ายที่จะลืมเขียนif(BuildConfig.DEBUG)ส่วนหนึ่ง
Nicolas Raoul

1
ปัญหาอีกประการหนึ่งคือสตริงยังคงอยู่ในรุ่นที่บรรจุ
straya

0

นี่คือทางออกของฉันหากคุณไม่ต้องการยุ่งกับห้องสมุดเพิ่มเติมหรือแก้ไขรหัสของคุณด้วยตนเอง ฉันสร้างสมุดบันทึก Jupyter นี้เพื่อดูไฟล์ java ทั้งหมดและคอมเม้นต์ข้อความบันทึกทั้งหมด ไม่สมบูรณ์แบบ แต่ทำงานให้ฉันได้แล้ว


0

ทางของฉัน:

1) เปิดใช้งานโหมดการเลือกคอลัมน์ (alt + shift + insert)

2) เลือกหนึ่ง Log.d (TAG, "text"); ส่วน 'บันทึก'

3) จากนั้นกด Shift + ctrl + alt + j

4) คลิกลูกศรซ้าย

5) ทำ shift + end

6) กดปุ่มลบ

สิ่งนี้จะลบการเรียก LOG ทั้งหมดในครั้งเดียวในไฟล์ java


0

คุณสามารถลองใช้วิธีการทั่วไปที่เรียบง่ายนี้:

Ctrl+ Shift+R

แทนที่

Log.e(

กับ

// Log.e(

ที่จะไม่ทำงานกับรหัสตัวอย่างที่กำหนดในคำถาม
Nicolas Raoul

0

ใช้งานง่ายด้วย kotlin เพียงประกาศฟังก์ชั่นระดับสูง

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

วิธีที่ง่ายที่สุด

ใช้ DebugLog

DebugLog ถูกปิดใช้งานทั้งหมดเมื่อเปิดตัวแอป

https://github.com/MustafaFerhan/DebugLog


นี่เป็นสิ่งที่ผิดอย่างยิ่ง นี่เป็นสาเหตุที่ทำให้บันทึกไม่ได้ถูกบันทึก แต่จะไม่ลบออกจากรหัสดังนั้นพวกเขายังคงอยู่ที่นั่นเพื่อช่วยให้ผู้คนย้อนกลับสร้างรหัสของคุณและยังมีค่าใช้จ่ายในการจัดรูปแบบสตริงของบันทึกเหล่านั้นทั้งหมด
Glenn Maynard
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.