1. 程式人生 > >Android Log的多場景使用

Android Log的多場景使用

背景:不同情況下,我們有不同的需求,下面我們總結一下所有的case採用不同的策略。

方案一:

通過一個開關,決定是否輸出log,也可以決定是否輸出到檔案,方便我們除錯使用。

package ls.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;

import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;

import com.config.AppConfig;

/**
 * Title: LogUtils.java Description: 日誌工具類:開發過程中,日誌輸出
 * 
 * @author song
 * @date 2014-9-9 下午1:22:55
 * @version V1.0
 */
public class LogUtils {
	/**
	 * isWrite:用於開關是否吧日誌寫入txt檔案中</p>
	 */
	private static final boolean isWrite = false;
	/**
	 * isDebug :是用來控制,是否列印日誌
	 */
	private static final boolean isDeBug = true;
	/**
	 * 存放日誌檔案的所在路徑
	 */
	private static final String DIRPATH = AppConfig.LOG_DIRPATH;
	// private static final String DIRPATH = "/log";
	/**
	 * 存放日誌的文字名
	 */
	private static final String LOGNAME = AppConfig.LOG_FILENAME;
	// private static final String LOGNAME = "log.txt";
	/**
	 * 設定時間的格式
	 */
	private static final String INFORMAT = "yyyy-MM-dd HH:mm:ss";
	/**
	 * VERBOSE日誌形式的識別符號
	 */
	public static final int VERBOSE = 5;
	/**
	 * DEBUG日誌形式的識別符號
	 */
	public static final int DEBUG = 4;
	/**
	 * INFO日誌形式的識別符號
	 */
	public static final int INFO = 3;
	/**
	 * WARN日誌形式的識別符號
	 */
	public static final int WARN = 2;
	/**
	 * ERROR日誌形式的識別符號
	 */
	public static final int ERROR = 1;

	/**
	 * 把異常用來輸出日誌的綜合方法
	 * 
	 * @param @param tag 日誌標識
	 * @param @param throwable 丟擲的異常
	 * @param @param type 日誌型別
	 * @return void 返回型別
	 * @throws
	 */
	public static void log(String tag, Throwable throwable, int type) {
		log(tag, exToString(throwable), type);
	}

	/**
	 * 用來輸出日誌的綜合方法(文字內容)
	 * 
	 * @param @param tag 日誌標識
	 * @param @param msg 要輸出的內容
	 * @param @param type 日誌型別
	 * @return void 返回型別
	 * @throws
	 */
	public static void log(String tag, String msg, int type) {
		switch (type) {
		case VERBOSE:
			v(tag, msg);// verbose等級
			break;
		case DEBUG:
			d(tag, msg);// debug等級
			break;
		case INFO:
			i(tag, msg);// info等級
			break;
		case WARN:
			w(tag, msg);// warn等級
			break;
		case ERROR:
			e(tag, msg);// error等級
			break;
		default:
			break;
		}
	}

	/**
	 * verbose等級的日誌輸出
	 * 
	 * @param tag
	 *            日誌標識
	 * @param msg
	 *            要輸出的內容
	 * @return void 返回型別
	 * @throws
	 */
	public static void v(String tag, String msg) {
		// 是否開啟日誌輸出
		if (isDeBug) {
			Log.v(tag, msg);
		}
		// 是否將日誌寫入檔案
		if (isWrite) {
			write(tag, msg);
		}
	}

	/**
	 * debug等級的日誌輸出
	 * 
	 * @param tag
	 *            標識
	 * @param msg
	 *            內容
	 * @return void 返回型別
	 * @throws
	 */
	public static void d(String tag, String msg) {
		if (isDeBug) {
			Log.d(tag, msg);
		}
		if (isWrite) {
			write(tag, msg);
		}
	}

	/**
	 * info等級的日誌輸出
	 * 
	 * @param  tag 標識
	 * @param  msg 內容
	 * @return void 返回型別
	 * @throws
	 */
	public static void i(String tag, String msg) {
		if (isDeBug) {
			Log.i(tag, msg);
		}
		if (isWrite) {
			write(tag, msg);
		}
	}

	/**
	 * warn等級的日誌輸出
	 * 
	 * @param tag 標識
	 * @param msg 內容
	 * @return void 返回型別
	 * @throws
	 */
	public static void w(String tag, String msg) {
		if (isDeBug) {
			Log.w(tag, msg);
		}
		if (isWrite) {
			write(tag, msg);
		}
	}

	/**
	 * error等級的日誌輸出
	 * 
	 * @param  tag 標識
	 * @param  msg 內容
	 * @return void 返回型別
	 */
	public static void e(String tag, String msg) {
		if (isDeBug) {
			Log.w(tag, msg);
		}
		if (isWrite) {
			write(tag, msg);
		}
	}

	/**
	 * 用於把日誌內容寫入制定的檔案
	 * 
	 * @param @param tag 標識
	 * @param @param msg 要輸出的內容
	 * @return void 返回型別
	 * @throws
	 */
	public static void write(String tag, String msg) {
		String path = FileUtils.createMkdirsAndFiles(DIRPATH, LOGNAME);
		if (TextUtils.isEmpty(path)) {
			return;
		}
		String log = DateFormat.format(INFORMAT, System.currentTimeMillis())
				+ tag
				+ "========>>"
				+ msg
				+ "\n=================================分割線=================================";
		FileUtils.write2File(path, log, true);
	}

	/**
	 * 用於把日誌內容寫入制定的檔案
	 * 
	 * @param tag
	 *            標籤
	 * @param ex
	 *            異常
	 */
	public static void write(Throwable ex) {
		write("", exToString(ex));
	}

	/**
	 * 把異常資訊轉化為字串
	 * 
	 * @param ex 異常資訊
	 * @return 異常資訊字串
	 */
	private static String exToString(Throwable ex) {
		Writer writer = new StringWriter();
		PrintWriter printWriter = new PrintWriter(writer);
		ex.printStackTrace(printWriter);
		printWriter.close();
		String result = writer.toString();
		return result;
	}
}

方案二:

方案一雖然在release情況無法輸出了,但是對於發不出去的包,通過反編譯還是可以看到log的,可以推出來我們的一些邏輯,這對於我們是不需要的

DEBUG通過一個final的boolean值控制,編譯的時候,會自動去掉。

if (DEBUG) {
    Log.e(TAG, "err", e);
}

方案三:
方案二雖然可以實現,但是還是比較複雜的。每次需要寫三行,我們可以通過另一種方式實現這種策略(混淆配置)
在混淆檔案中:
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** d(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);

}

但是不可以配置:-dontoptimize

方案四:

這是一種系統開發常用的方法,我們的應用釋出出去了,這時我們發現了問題,但是沒有log怎麼除錯呢?這是就出現了方案四,我們可以動態配置在當前手機是否開啟log

直接參照系統原始碼吧:

這裡面有個知識點:

android.util.Log.isLoggable(TAG, level);
這個開關時可以動態配置的,正常沒有配置的時候,是返回false的。

當設定了

adb shell setprop log.tag.TAG VERBOSE
之後,就會返回true了
/packages/services/Telephony/src/com/android/services/telephony/Log.java  

/*
 * Copyright 2014, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.services.telephony;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.IllegalFormatException;
import java.util.Locale;

/**
 * Manages logging for the entire module.
 */
final public class Log {

    // Generic tag for all In Call logging
    private static final String TAG = "Telephony";

    public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
    public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
    public static final boolean INFO = isLoggable(android.util.Log.INFO);
    public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
    public static final boolean WARN = isLoggable(android.util.Log.WARN);
    public static final boolean ERROR = isLoggable(android.util.Log.ERROR);

    private Log() {}

    public static boolean isLoggable(int level) {
        return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
    }

    public static void d(String prefix, String format, Object... args) {
        if (DEBUG) {
            android.util.Log.d(TAG, buildMessage(prefix, format, args));
        }
    }

    public static void d(Object objectPrefix, String format, Object... args) {
        if (DEBUG) {
            android.util.Log.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
        }
    }

    public static void i(String prefix, String format, Object... args) {
        if (INFO) {
            android.util.Log.i(TAG, buildMessage(prefix, format, args));
        }
    }

    public static void i(Object objectPrefix, String format, Object... args) {
        if (INFO) {
            android.util.Log.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
        }
    }

    public static void v(String prefix, String format, Object... args) {
        if (VERBOSE) {
            android.util.Log.v(TAG, buildMessage(prefix, format, args));
        }
    }

    public static void v(Object objectPrefix, String format, Object... args) {
        if (VERBOSE) {
            android.util.Log.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
        }
    }

    public static void w(String prefix, String format, Object... args) {
        if (WARN) {
            android.util.Log.w(TAG, buildMessage(prefix, format, args));
        }
    }

    public static void w(Object objectPrefix, String format, Object... args) {
        if (WARN) {
            android.util.Log.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
        }
    }

    public static void e(String prefix, Throwable tr, String format, Object... args) {
        if (ERROR) {
            android.util.Log.e(TAG, buildMessage(prefix, format, args), tr);
        }
    }

    public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
        if (ERROR) {
            android.util.Log.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
                    tr);
        }
    }

    public static void wtf(String prefix, Throwable tr, String format, Object... args) {
        android.util.Log.wtf(TAG, buildMessage(prefix, format, args), tr);
    }

    public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
        android.util.Log.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
                tr);
    }

    public static void wtf(String prefix, String format, Object... args) {
        String msg = buildMessage(prefix, format, args);
        android.util.Log.wtf(TAG, msg, new IllegalStateException(msg));
    }

    public static void wtf(Object objectPrefix, String format, Object... args) {
        String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
        android.util.Log.wtf(TAG, msg, new IllegalStateException(msg));
    }

    /**
     * Redact personally identifiable information for production users.
     * If we are running in verbose mode, return the original string, otherwise
     * return a SHA-1 hash of the input string.
     */
    public static String pii(Object pii) {
        if (pii == null || VERBOSE) {
            return String.valueOf(pii);
        }
        return "[" + secureHash(String.valueOf(pii).getBytes()) + "]";
    }

    private static String secureHash(byte[] input) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
        messageDigest.update(input);
        byte[] result = messageDigest.digest();
        return encodeHex(result);
    }

    private static String encodeHex(byte[] bytes) {
        StringBuffer hex = new StringBuffer(bytes.length * 2);

        for (int i = 0; i < bytes.length; i++) {
            int byteIntValue = bytes[i] & 0xff;
            if (byteIntValue < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toString(byteIntValue, 16));
        }

        return hex.toString();
    }

    private static String getPrefixFromObject(Object obj) {
        return obj == null ? "<null>" : obj.getClass().getSimpleName();
    }

    private static String buildMessage(String prefix, String format, Object... args) {
        String msg;
        try {
            msg = (args == null || args.length == 0) ? format
                    : String.format(Locale.US, format, args);
        } catch (IllegalFormatException ife) {
            wtf("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format,
                    args.length);
            msg = format + " (An error occurred while formatting the message.)";
        }
        return String.format(Locale.US, "%s: %s", prefix, msg);
    }
}

以上的每個知識點都可以用於很多用途,不只是在log這裡!

參考連結:

http://blog.csdn.net/vk5176891/article/details/46537625