Android一行代码实现将Log写入文件(可配置是否输出Log,测试包可直接用于发布)
2017-01-03 11:12
681 查看
需求:
1.在测试阶段,也可以留下Log,出问题方便查看。给qa测试或者晚上放着手机跑App等
2.测试包不需要改代码,可以直接发布,正常用户因为没有配置文件(配置文件下面的内容会介绍),所以看不到Log,保证了信息的安全性
因为笔者前不久在做一个项目,需求是在用户屏幕熄灭一个小时后,开始点亮屏幕做任务。
所以选在白天开发完后,晚上放着几台手机跑。第二天如果要想知道昨晚运行是否正常,最好的办法就是看Log,把Log写入文件,这是最直观的。
关于6.0或之后版本的手机休眠后不可唤醒,如何主动点亮屏幕,怎样才能保持后台运行不被杀死等内容,笔者会在之后的博客里面进行讲解。
不废话了,直接上代码
这个类就是一个工具类,可以使用其中的v d i w e 方法,输出log。
从上到下来看,先是定义了一些常量,为Log等级。然后是保存log的文件,log开关等。
再看static域,通过sd卡根目录下面的一个配置文件来判断是否打开log,配置文件为.properties类型,这种文件,我相信使用as做开发的朋友并不会陌生,新建一个工程,目录下面就有好几个.properties文件,比如gradle.properties。这种文件非常简单,一行一个单元,属性=值,此类文件的具体内容本篇不再介绍。
在static域里,我们根据配置文件,来判断是否打开log,LOG_LEVEL为log的等级,只有他打开的时候,才会输出log,包括控制台和文件的log。logFileEnable为log输出到文件的开关,如果只打开LOG_LEVEL而logFileEnable没打开,只会把log输出到控制台。总的来说,LOG_LEVEL为总开关,logFileEnable为子开关。
看看write方法,就是把log输出到文件里面,要注意,输出到文件里面的Log要添加前缀,因为此处不像控制台一样,系统和开发工具会帮你输出和显示一些相信信息,比如log等级,时间,类,方法,行数之类的信息。
gerPrefixName就是获取部分前缀信息,获取不到的话,可能被混淆了,就输出 [ minify ]
再回过头来看看LOG_FILE这个变量,其为log文件路径,由LogManager里面获取,所以看看LogManager的代码
就一个功能,通过init方法由外部传入log文件路径。如果开发者觉得麻烦不想用这个类,可以直接把log文件写到LogUtil类里面,做一下初始化,就行了。
最后看看使用
初始化一下log文件路径,就可以尽情的输出log了。
运行程序,结果如下:
并没有输出log。那是因为没有放入配置文件。我们来看看配置文件的内容,
是如此的简单。
把配置文件放到sd卡根目录,再运行看看
可以看到控制台和文件都有log输出。
当然了,有人会觉得使用配置文件非常麻烦,但我觉得一台手机只要放一次,能使用一辈子,并不是太麻烦。
你如果要想在文件里面保存log,除了放配置文件,就只能改代码了,改代码的话,相当于要apk包改变了,对于正规的测试发布流程来说,包修改后,就要重新测过一遍。因此,使用改代码的方式的话,你的最后一次完整测试,也就是发布前的那一次测试,是看不到log的。
源码下载点击打开链接
1.在测试阶段,也可以留下Log,出问题方便查看。给qa测试或者晚上放着手机跑App等
2.测试包不需要改代码,可以直接发布,正常用户因为没有配置文件(配置文件下面的内容会介绍),所以看不到Log,保证了信息的安全性
因为笔者前不久在做一个项目,需求是在用户屏幕熄灭一个小时后,开始点亮屏幕做任务。
所以选在白天开发完后,晚上放着几台手机跑。第二天如果要想知道昨晚运行是否正常,最好的办法就是看Log,把Log写入文件,这是最直观的。
关于6.0或之后版本的手机休眠后不可唤醒,如何主动点亮屏幕,怎样才能保持后台运行不被杀死等内容,笔者会在之后的博客里面进行讲解。
不废话了,直接上代码
package com.chenjian.logutil; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Properties; /** * Log使用类 */ public class LogUtil { private static final int VERBOSE = 0; private static final int DEBUG = 1; private static final int INFO = 2; private static final int WARNING = 3; private static final int ERROR = 4; private static final int NO_LOG = 5; private static final String LOG_FILE = LogManager.getInstance().getLogFilePath(); private static LogUtil mInstance = null; private static int LOG_LEVEL = NO_LOG; private static boolean logFileEnable = false; static { try { // 通过SD卡配置文件启用日志 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { // path为配置文件的路径 String path = Environment.getExternalStorageDirectory() + "/config.properties"; if ((new File(path).exists())) { InputStream inputStream = new FileInputStream(path); Properties properties = new Properties(); properties.load(inputStream); if ("true".equals(properties.getProperty("logcat"))) { LOG_LEVEL = VERBOSE; } if ("true".equals(properties.getProperty("filelog"))) { logFileEnable = true; } inputStream.close(); } } } catch (Exception e) { e.printStackTrace(); } } private static LogUtil getInstance() { if (mInstance == null) { synchronized (LogUtil.class) { if (mInstance == null) { mInstance = new LogUtil(); } } } return mInstance; } // verbose public static void v(String tag, String msg) { if (VERBOSE < LOG_LEVEL) return; if (msg == null) return; Log.v(tag, msg); write("VERBOSE", getInstance().getPrefixName(), msg); } public static void v(String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; v(getInstance().getPrefixName(), msg); } // debug public static void d(String tag, String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; Log.d(tag, msg); write("DEBUG", getInstance().getPrefixName(), msg); } public static void d(String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; d(getInstance().getPrefixName(), msg); } // info public static void i(String tag, String msg) { if (INFO < LOG_LEVEL) return; if (msg == null) return; Log.i(tag, msg); write("INFO", getInstance().getPrefixName(), msg); } public static void i(String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; i(getInstance().getPrefixName(), msg); } // warning public static void w(String tag, String msg) { if (WARNING < LOG_LEVEL) return; if (msg == null) return; Log.w(tag, msg); write("WARNING", getInstance().getPrefixName(), msg); } public static void w(String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; w(getInstance().getPrefixName(), msg); } public static void w(String tag, String msg, Throwable tr) { if (WARNING < LOG_LEVEL) return; if (msg == null) return; Log.w(tag, msg, tr); write("WARNING", getInstance().getPrefixName(), msg); } // error public static void e(String tag, String msg) { if (ERROR < LOG_LEVEL) return; if (msg == null) return; Log.e(tag, msg); write("ERROR", getInstance().getPrefixName(), msg); } public static void e(String msg) { if (DEBUG < LOG_LEVEL) return; if (msg == null) return; e(getInstance().getPrefixName(), msg); } /** * 写到文件中的log的前缀,如果因为混淆之类的原因而取不到,就返回"[ minify ]" * * @return prefix */ private String getPrefixName() { StackTraceElement[] sts = Thread.currentThread().getStackTrace(); if (sts == null || sts.length == 0) { return "[ minify ]"; } try { for (StackTraceElement st : sts) { if (st.isNativeMethod()) { continue; } if (st.getClassName().equals(Thread.class.getName())) { continue; } if (st.getClassName().equals(this.getClass().getName())) { continue; } if (st.getFileName() != null) { return "[ " + Thread.currentThread().getName() + ": " + st.getFileName() + ":" + st.getLineNumber() + " " + st.getMethodName() + " ]"; } } } catch (Exception e) { e.printStackTrace(); } return "[ minify ]"; } /** * 追加文件:使用FileWriter * * @param level 等级 * @param prefix 前缀 * @param content 内容 */ private static void write(String level, String prefix, String content) { if (!logFileEnable) return; try { // 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件 FileWriter writer = new FileWriter(LOG_FILE, true); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); String time = sdf.format(new Date()); writer.write(time + ": " + level + "/" + prefix + ": " + content + "\n"); writer.close(); } catch (Exception e) { e.printStackTrace(); } } }
这个类就是一个工具类,可以使用其中的v d i w e 方法,输出log。
从上到下来看,先是定义了一些常量,为Log等级。然后是保存log的文件,log开关等。
再看static域,通过sd卡根目录下面的一个配置文件来判断是否打开log,配置文件为.properties类型,这种文件,我相信使用as做开发的朋友并不会陌生,新建一个工程,目录下面就有好几个.properties文件,比如gradle.properties。这种文件非常简单,一行一个单元,属性=值,此类文件的具体内容本篇不再介绍。
在static域里,我们根据配置文件,来判断是否打开log,LOG_LEVEL为log的等级,只有他打开的时候,才会输出log,包括控制台和文件的log。logFileEnable为log输出到文件的开关,如果只打开LOG_LEVEL而logFileEnable没打开,只会把log输出到控制台。总的来说,LOG_LEVEL为总开关,logFileEnable为子开关。
看看write方法,就是把log输出到文件里面,要注意,输出到文件里面的Log要添加前缀,因为此处不像控制台一样,系统和开发工具会帮你输出和显示一些相信信息,比如log等级,时间,类,方法,行数之类的信息。
gerPrefixName就是获取部分前缀信息,获取不到的话,可能被混淆了,就输出 [ minify ]
再回过头来看看LOG_FILE这个变量,其为log文件路径,由LogManager里面获取,所以看看LogManager的代码
package com.chenjian.logutil; import java.io.File; /** * Log逻辑管理类 * <p> * Created by ChenJian * 2016.10.8 14:22:22. */ public class LogManager { private static volatile LogManager mInstance; private String logFilePath; private LogManager() { } public static LogManager getInstance() { if (mInstance == null) { synchronized (LogManager.class) { if (mInstance == null) { mInstance = new LogManager(); } } } return mInstance; } /** * 初始化log配置 * * @param logFilePath log文件路径,必须是完整的全路径 */ public void init(String logFilePath) { this.logFilePath = logFilePath; createLogFile(); } private boolean createLogFile() { boolean ret = false; File file = new File(logFilePath); try { if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } ret = file.createNewFile(); } catch (Exception e) { e.printStackTrace(); } return ret; } String getLogFilePath() { return logFilePath; } }
就一个功能,通过init方法由外部传入log文件路径。如果开发者觉得麻烦不想用这个类,可以直接把log文件写到LogUtil类里面,做一下初始化,就行了。
最后看看使用
package com.chenjian.logutil; import android.app.Activity; import android.os.Bundle; import android.os.Environment; public class MainActivity extends Activity { /** * 主路径 */ private static final String MAIN_DIR = Environment.getExternalStorageDirectory() + "/ChenJian/CsdnBlog/LogUtil"; /** * 保存Log的文件 */ private static final String LOG_FILE = MAIN_DIR + "/log.txt"; private final static String LOG_FOR_QA = "[log4qa] "; private final static String LOG_FOR_DADIAN = "[log4dadian] "; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LogManager.getInstance().init(LOG_FILE); LogUtil.d(LOG_FOR_QA, "发发恭子的博客"); LogUtil.e(LOG_FOR_DADIAN, "http://blog.csdn.net/qq_296216078"); } }
初始化一下log文件路径,就可以尽情的输出log了。
运行程序,结果如下:
并没有输出log。那是因为没有放入配置文件。我们来看看配置文件的内容,
是如此的简单。
把配置文件放到sd卡根目录,再运行看看
可以看到控制台和文件都有log输出。
当然了,有人会觉得使用配置文件非常麻烦,但我觉得一台手机只要放一次,能使用一辈子,并不是太麻烦。
你如果要想在文件里面保存log,除了放配置文件,就只能改代码了,改代码的话,相当于要apk包改变了,对于正规的测试发布流程来说,包修改后,就要重新测过一遍。因此,使用改代码的方式的话,你的最后一次完整测试,也就是发布前的那一次测试,是看不到log的。
源码下载点击打开链接
相关文章推荐
- android代码混淆发布版本时关闭log输出
- 尝试用android-logging-log4j去实现log输出内容到sd卡中的文件的功能
- Android 代码实现logcat输出到文件
- 一行代码实现Android文件下载
- 用android-logging-log4j去实现log输出内容到sd卡中的文件的功能
- 【记录】尝试用android-logging-log4j去实现log输出内容到sd卡中的文件的功能
- java和Android文件下载断点续传和图片下载代码实现,可直接复制简单实现
- 尝试用android-logging-log4j去实现log输出内容到sd卡中的文件的功能
- 一行代码实现android文件下载
- 关于iis发布网站未能写入输出文件的错误的解决办法
- android直接把打印信息输出到文件
- ASP实现文件直接下载的代码
- ANDROID代码实现APK文件的安装与卸载
- 用于验证用户名是否可用的php ajax实现代码
- 不用xib文件,直接用代码实现xib。实现一个toolbar,tableview,具体如效果图
- Android代码实现APK文件的安装与卸载
- sharepoint中在blog中,发布post可以直接打开 word 发布!(Launch blog program to post用代码实现)
- JavaScript 直接操作本地文件的实现代码
- Android中Log.d和Log.v如何实现在release版本不输出
- Android代码实现APK文件的安装与卸载