Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Nâng cao Logging Android với Timber

Timber là thư viện logging do Jake Wharton phát triển, bao bọc (wrapper) xung quanh android.util.Log. Timber khắc phục nhược điểm của Log chuẩn: tag tự động (tên class), không log trong release build, hỗ trợ tree tùy chỉnh (file, crashlytics, remote...), API ngắn gọn.

Tích hợp Timber

Bước 1: Thêm dependency

dependencies {
    implementation 'com.jakewharton.timber:timber:5.0.1'
}

Bước 2: Khởi tạo trong Application class (Khuyến nghị)

import android.app.Application;
import timber.log.Timber;

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        if (BuildConfig.DEBUG) {
            // Debug tree: log ra logcat với tag = class name
            Timber.plant(new Timber.DebugTree());
        } else {
            // Release tree: gửi log đến Crashlytics, server, file...
            Timber.plant(new ReleaseTree());
        }
    }
    
    // Custom tree cho release
    private static class ReleaseTree extends Timber.Tree {
        @Override
        protected void log(int priority, String tag, String message, Throwable t) {
            if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) {
                return; // Chỉ log Warning, Error trong release
            }
            // Gửi đến Firebase Crashlytics, Sentry, file log, server...
            // FirebaseCrashlytics.getInstance().recordException(t);
        }
    }
}

AndroidManifest.xml

<application
    android:name=".MyApplication"
    ... >
    ...
</application>

Sử dụng Timber

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // Các level log
        Timber.v("Verbose: Chi tiết debug");      // Verbose
        Timber.d("Debug: Thông tin debug");       // Debug
        Timber.i("Info: Thông tin chung");        // Info
        Timber.w("Warning: Cảnh báo");            // Warning
        Timber.e("Error: Lỗi");                   // Error
        Timber.wtf("What a Terrible Failure");    // WTF
        
        // Format string (như String.format)
        Timber.d("User %s đăng nhập lúc %d", "Nguyễn Văn A", System.currentTimeMillis());
        
        // Log exception
        try {
            // code gây exception
        } catch (Exception e) {
            Timber.e(e, "Lỗi khi xử lý: %s", "đọc file");
        }
        
        // Tag tùy chỉnh (hiếm khi cần)
        Timber.tag("Network").d("Request: %s", url);
    }
}

So sánh Log vs Timber

Đặc điểmandroid.util.LogTimber
Tag tự độngKhông (phải hardcode)Có (tên class)
Release buildVẫn log (phải check BuildConfig.DEBUG)Tự động tắt (DebugTree chỉ debug)
Exception loggingLog.e(tag, msg, e)Timber.e(e, msg)
ExtensibilityKhóDễ (Tree pattern)
String formattingManualBuilt-in (varargs)
Kotlin supportCơ bảnTốt (extension functions)

Advanced: Custom Tree

Log ra file (Internal storage)

public class FileLoggingTree extends Timber.Tree {
    private final File logFile;
    private static final int MAX_SIZE = 1024 * 1024; // 1MB
    
    public FileLoggingTree(Context context) {
        logFile = new File(context.getFilesDir(), "app.log");
    }
    
    @Override
    protected void log(int priority, String tag, String message, Throwable t) {
        if (priority < Log.INFO) return;
        
        String logLine = String.format("[%s] %s: %s%n", 
            getTimestamp(), tag, message);
        if (t != null) {
            logLine += Log.getStackTraceString(t) + "\n";
        }
        
        try (FileWriter fw = new FileWriter(logFile, true)) {
            fw.write(logLine);
            // Rotate log nếu quá lớn
            if (logFile.length() > MAX_SIZE) rotateLog();
        } catch (IOException ignored) {}
    }
    
    private void rotateLog() { /* rename file cũ, tạo mới */ }
    private String getTimestamp() { 
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US).format(new Date()); 
    }
}

// Plant thêm: Timber.plant(new FileLoggingTree(this));

Logcat format đẹp hơn

// Tùy chỉnh DebugTree
Timber.plant(new Timber.DebugTree() {
    @Override
    protected String createStackElementTag(StackTraceElement element) {
        // Tag format: ClassName.methodName:lineNumber
        return String.format("%s.%s:%d", 
            element.getClassName().substring(element.getClassName().lastIndexOf('.') + 1),
            element.getMethodName(),
            element.getLineNumber());
    }
});

Kotlin Extensions (Nếu dùng Kotlin)

// build.gradle.kts
implementation("com.jakewharton.timber:timber:5.0.1")

// Sử dụng
Timber.d { "Message lazy: ${expensiveComputation()}" } // Chỉ evaluate nếu log level đủ
Timber.tag("Network").d { "Response: $response" }

Chạy ứng dụng

Build debug → Logcat hiển thị log với tag tự động là tên class. Build release → Không log Verbose/Debug/Info, chỉ Warning/Error (nếu plant ReleaseTree).

Lưu ý

  • Chỉ plant Timber **một lần** trong Application.onCreate()
  • Không plant DebugTree trong release build
  • Dùng Timber.e(e, msg) cho exception - tự động log stacktrace
  • Tránh log thông tin nhạy cảm (token, password, PII) - ngay cả debug
  • Proguard/R8 tự động loại bỏ Timber call nếu config đúng