logging最佳实践和想法

我即将做一些重构我的应用程序,我来思考这个简单而复杂的主题,日志logging,如何做到干净,有效和信息丰富的日志logging是如此困难…

当你阅读关于日志的文档时,你经常会看到这个片段

if (BuildConfig.DEBUG) { Log.d(TAG + "message"); } 

这使我想知道它的目的是什么? 根据Android Developer-Log文档,debugging日志消息被编译进去,但是在运行时剥离,所以你不需要在if语句中有日志调用。 还是我误解了什么?

然后我也想知道除了debugging以外,使用任何其他Log.x()调用的真正好处是什么,因为日志条目不会被用户看到或login到某个错误文件,所以它们将被编译并执行在没有任何目的的生产环境中? 这可能是if语句的用例吗?

我在前面提到,日志条目没有login到文件中。 为什么这不是Android的内置function? 是因为性能问题,不必要的权限使用或其他? 我已经在自己的日志类中实现了这个function,但是现在我想知道这是不是很糟糕的做法? 但是有重要的日志条目的日志也不错?

因此,要在开发和生产过程中实施清洁,有效和信息丰富的测井。 什么是最佳实践?

Solutions Collecting From Web of "logging最佳实践和想法"

日志只需要在开发过程中debugging应用程序,以确保该function按预期工作,并产生预期的结果。 我相信最好的做法是以最方便的方式进行日志logging,并尽可能快速有效地发现和解决问题

我在前面提到,日志条目没有login到文件中。 为什么这不是Android的内置function?

谁(除了在开发阶段的程序员)会希望应用程序浪费有限的存储空间在无用数据的设备上? 用户看不到,不看,不用日志。 他们不需要这个垃圾。 在生产中的应用程序不能产生任何日志,当然,也不能将它们保存到文件中。

在发布的应用程序中应该实现的唯一日志logging是未处理的exception日志logging。 此外,如果应用程序build议发送崩溃报告,则应负责处理这些日志,并在发送报告后将其删除。

日志不应该由发布的应用程序创build的另一个原因是它们可能包含敏感数据和输出,这需要用户授权,从而引入安全漏洞。

我相信最好的做法是在部署到生产之前,在模块或function完全实施并彻底testing之后立即移除所有日志。 介绍if (BuildConfig.DEBUG)条件有助于确保实现。

这将产生干净的debugging标签与这种格式的ClasssName[MethodName] - LineNumber与reflection。

内嵌评论的完整代码可以在这里作为要点 。

 import android.util.Log; public class Logger { public enum LOGGER_DEPTH { ACTUAL_METHOD(4), LOGGER_METHOD(3), STACK_TRACE_METHOD(1), JVM_METHOD(0); private final int value; private LOGGER_DEPTH(final int newValue) { value = newValue; } public int getValue() { return value; } } private static final String personalTAG = "Logger"; private StringBuilder sb; private Logger() { if (LoggerLoader.instance != null) { Log.e(personalTAG, "Error: Logger already instantiated"); throw new IllegalStateException("Already Instantiated"); } else { this.sb = new StringBuilder(255); } } public static Logger getLogger() { return LoggerLoader.instance; } private String getTag(LOGGER_DEPTH depth) { try { String className = Thread.currentThread().getStackTrace()[depth.getValue()].getClassName(); sb.append(className.substring(className.lastIndexOf(".") + 1)); sb.append("["); sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getMethodName()); sb.append("] - "); sb.append(Thread.currentThread().getStackTrace()[depth.getValue()].getLineNumber()); return sb.toString(); } catch (Exception ex) { ex.printStackTrace(); Log.d(personalTAG, ex.getMessage()); } finally { sb.setLength(0); } return null; } public void d(String msg) { try { Log.d(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void d(String msg, LOGGER_DEPTH depth) { try { Log.d(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void d(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.d(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void e(String msg) { try { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void e(String msg, LOGGER_DEPTH depth) { try { Log.e(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void e(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.e(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void w(String msg) { try { Log.w(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void w(String msg, LOGGER_DEPTH depth) { try { Log.w(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void w(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.w(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void v(String msg) { try { Log.v(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void v(String msg, LOGGER_DEPTH depth) { try { Log.v(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void v(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.v(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void i(String msg) { try { Log.i(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void i(String msg, LOGGER_DEPTH depth) { try { Log.i(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void i(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.i(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void wtf(String msg) { try { Log.wtf(getTag(LOGGER_DEPTH.ACTUAL_METHOD), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void wtf(String msg, LOGGER_DEPTH depth) { try { Log.wtf(getTag(depth), msg); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } public void wtf(String msg, Throwable t, LOGGER_DEPTH depth) { try { Log.wtf(getTag(depth), msg, t); } catch (Exception exception) { Log.e(getTag(LOGGER_DEPTH.ACTUAL_METHOD), "Logger failed, exception: " + exception.getMessage()); } } private static class LoggerLoader { private static final Logger instance = new Logger(); } } 

自从我开始在Android中工作以来,我遇到了同样的问题,我创build了这个开源项目( Android Studio Macros ),允许您通过使用“// <#DEBUG_AREA>和/或<#/ DEBUG_AREA>标签,基本的想法是,当你改变你的构build变体时,这些标签中的任何东西都会被注释掉,例如,如果你在for循环中有类似的东西:

  //=========This piece of code is only for logging purposes...========= Log.e("LogUserInfo", "Name: " + name); Log.e("LogUserInfo", "Id: " + user.getId()); Log.e("LogUserInfo", "Id: " + user.getDistance()); //==================================================================== 

而不是这样做:

 if(DEBUG){ Log.e("LogginUserInfo", "Name: " + name); Log.e("LogginUserInfo", "Id: " + user.getId()); Log.e("LogginUserInfo", "Id: " + user.getDistance()); } 

有了这个macros,你可以做到这一点(完整的方法):

 private List<String> getNamesOfUsersNearMe(String zipCode){ List<User> users = mBusinessLogic.getUsersByZipcode(zipCode); if(users == null || users.size() < 1){ return null; } List<String> names = new ArrayList<String>(); int totalUsers = users.size(); for(int i = 0; i < totalUsers; i++){ User user = users.get(i); String name = user.getName(); names.add(name); //<#DEBUG_AREA> Log.e("LogginUserInfo", "Name: " + name); Log.e("LogginUserInfo", "Id: " + user.getId()); Log.e("LogginUserInfo", "Id: " + user.getDistance()); //</#DEBUG_AREA> } return names; } 

而当你改变你的构build变种释放它会变成这样的:

 private List<String> getNamesOfUsersNearMe(String zipCode){ List<User> users = mBusinessLogic.getUsersByZipcode(zipCode); if(users == null || users.size() < 1){ return null; } List<String> names = new ArrayList<String>(); int totalUsers = users.size(); for(int i = 0; i < totalUsers; i++){ User user = users.get(i); String name = user.getName(); names.add(name); /*<#DEBUG_OFF> Log.e("LogginUserInfo", "Name: " + name); Log.e("LogginUserInfo", "Id: " + user.getId()); Log.e("LogginUserInfo", "Id: " + user.getDistance()); </#DEBUG_OFF>*/ } return names; } 

长循环的性能会更好,并且在“释放”模式下通过清除不必要的代码来清理代码,当然如果你回到“debugging”它将取消注释该区域,并保持原来的样子与“<#DEBUG_AREA>”标签…

也试图适应最常见的情况,似乎有些时候你不需要一个完整的区域来摆脱,而是只有一个单一的日志,所以在这种情况下,该项目也有一个日志包装类,您可以使用如下:

 if(users == null || users.size() < 1){ ASDebuggerMacroLog.e("LogUserInfo", "There's no users available near me..."); return null; } 

类“ASDebuggerMacroLog”使用的代码行将在Android Studio中更改为“发布”模式后进行评论。

希望能帮助到你!

问候!