背景:不同情况下,我们有不同的需求,下面我们总结一下所有的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这里!
参考链接: