前言
现在软件开发门槛越来越低,随便一个大学生培训几个月就可以开始开发软件,但是事实上,软件开发是有较高门槛的,写出高质量的代码并不容易。按28定律,只有20%程序员能写出合格的代码,其他80%代码质量都是有问题的。按从业多年接触的经验上看,竞然高达有80%的人不注重日志实践,甚至连日志实践都很肤浅。不夸张地说,面试程序员时,如果连接日志实践都无法说清楚的,几乎可以肯定是没有经验的或水平很一般的。
有经验的人在构建应用程序基础框架时,一般会前置构建好日志模块。笔者从事的物联网设备开发领域有很多使用Android
开发的设备,水平不一的Android程序员经常开发出崩溃、闪退、ANR等问题设备的,在Android
开始时,日志实践更是重要,一般我们应该结合自身的应用特点定义和配置好日志模块。
常见的Android
日志框架包括:
- 原生logcat
Android
原生内置的日志模块,与Android Studio
结合紧密,相当于Chrome
中的console
,算是比较基础的日志模块。
零配置,直接log.d
,log.i
、log.w
,log.e
就可以在控制台输出日志。 一般,我们就会release
版本中关闭日志输出功能以减少开销。 - Logger
这是一个应用比较广泛的Android
日志库,一款简单优秀强大的Android日志记录器Logger,有相当多的人在使用。优点是能提供线程/类/方法信息,支持JSON/XML
格式化打印,输出格式简洁易看、能直接跳转到源文件。
但是Logger
只是专门针对Android开发的,对一个完整的日志框架而言,并不完善。官方只包含一个DiskLogAdapter
,如果要将日志输出到Socket
、HTTP
、syslog
等,需要自己开发Adapter
。
- android-logback
在Java领域,SLF4J
是一个著名的JAVA日志门面框架,[logback](http://www.logback.cn/)
是其实现。logback
继承自 log4j,它建立在有十年工业经验的日志系统之上。它比其它所有的日志系统更快并且更小,包含了许多独特并且有用的特性。android-logback
则是其Android
封装。 - 其他Android日志框架
还有不少第三方开源的日志框架,如ViseLog
,log4j
,Hutool/logUntil
等等。
日志需求
日志模块实践和配置应与需求相关联,笔者需要开发一套基于Android的物联网设备,不同于一般的Android App,其对日志的需求如下:
- 支持输出到logcat,并且能自己输出格式,最重要的是要能跳转到源码文件。
- 支持输出到文件,能按日期、日志文件大小、数量切割滚动输出日志。
- 能输出到Sentry日志收集平台
- 能输出到Syslog服务器
- 能灵活配置日志参数,并可以在运行中修改日志级别、启用/禁用网络日志等。
基于上述需求,最终选型使用android-logback
.
基本使用方法
安装
在build.gradle
中添加
implementation 'org.slf4j:slf4j-api:1.7.25'
implementation 'com.github.tony19:logback-android:2.0.0'
logback-android提供了多种Appender,用来实现将日志输出到不同的目标,如下:
FileAppender
RollingFileAppender
SMTPAppender
SocketAppender
,SyslogAppender
SQLiteAppender
AsyncAppender
XML配置
在app/assets
下创建一个logback.xml
文件。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 日志保存位置 -->
<property name="LOG_DIR" value="/data/data/包名/files/logs" />
<!-- Logcat -->
<appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
<tagEncoder>
<pattern>%logger{12}</pattern>
</tagEncoder>
<encoder>
<pattern>[%-5level] - %msg\, %caller%\,L%line\,%thread%, %line%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- Syslog -->
<appender name="syslog" class="ch.qos.logback.classic.net.SyslogAppender">
<lazy>true</lazy>
<syslogHost>192.168.118.129</syslogHost>
<port>514</port>
<facility>LOCAL1</facility>
<reconnectionDelay>10000</reconnectionDelay>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
<charset>UTF-8</charset>
</appender>
<appender name="SYSLOG" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="syslog" />
</appender>
<!-- 滚动文件 -->
<appender name="rollingfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %class{16} - %msg, L%line, %thread%n</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>20MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="rollingfile" />
</appender>
<root level="DEBUG">
<appender-ref ref="LOGCAT" />
<appender-ref ref="FILE" />
<appender-ref ref="SYSLOG" />
</root>
</configuration>
关于android-logback
的很多配置参数均可以参考logback文档。
需要注意的是针对文件、网络等输出目标,一般应配置AsyncAppender
。AsyncAppender
的作用简单也就仅仅是内置一个队列,然后批量进行日志输出。logcarAppender不需要。
使用
接下来在类中使用即可。
public class MyClass {
private final Logger log = LoggerFactory.getLogger(MyClass.class);
...
public void method(){
log.debug(...);
log.info(...);
log.warn(...);
log.error(...);
}
}
我们需要使用LoggerFactory.getLogger(类名)
构建一个日志记录器,一般会使用当前类的类名作为logger
的名称。
事实上,你完全也可以使用LoggerFactory.getLogger(任意字符串)
构建,但使用类名可以得用类名形成层级关系,可以进行更加灵活的日志输出和控制。
至此,基本使用就算完成了。
但是,为了使用log,我们不得不写上大量的样板代码private final Logger log = LoggerFactory.getLogger(MyClass.class);
,明显是很不爽的。
一般推荐使用lombok
注解来简化此操作。如上例:
@Slf4j
public class MyClass {
....
public void method(){
log.debug(...);
log.info(...);
log.warn(...);
log.error(...);
}
}
只需要在所有类前添加一个@Slf4j的注解即可达到同样的效果,很时显清爽多了。
进阶开发和配置
基本配置并不能满足我们的需求,我们还需要解决:
- 在logcat中输出
- 输出到Syslog
- 输出到Sentry
输出到logcat
android-logback
已经实现了logcatAppender
,我们只需要配置相应的参数即可。
<appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
<tagEncoder>
<pattern>%logger{12}</pattern>
</tagEncoder>
<encoder>
<pattern>[%-5level] - %msg\, %caller%\,L%line\,%thread%, %line%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
只需要在按照logback
的[PatternLayout](http://www.logback.cn/06%E7%AC%AC%E5%85%AD%E7%AB%A0Layouts.html)
规则配置pattern
即可输出丰富的日志信息。
接下来我们还要解决一个问题,在logcat
日志输出窗口直接跳转到输出日志所在的源文件。
方法很简单,只需要在pattern
中添加.(%file:%line\)
即可。
<appender name="LOGCAT" class="ch.qos.logback.classic.android.LogcatAppender">
...
<encoder>
<pattern>[%-5level]", "%msg, .(%file:%line\\) , %thread </pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
这样,每一条输出到logcat控制台的日志均会包含一个超链接,点击就可以直接跳转到打印日志时的源文件所在行。
输出至Syslog
syslog
是Linux内置的标准日志功能,一般linux
均至少会内置syslog client
,syslog server
需要额外开启。
只所以在Android
开发中启用syslog
的原因是,公司的物联网设备即有Android
的,也有嵌入式linux
的,而在网络收集日志时,嵌入式Linux
设备内置了syslog
输出,我们希望使用统一的、现成的日志服务器。因此就有了这样的网络日志收集方案:
- 使用一台
linux
服务器,配置启用syslog
服务器。或者windows上安装Kiwi Syslog Server
嵌入式Linux
设备直接使用内置现成的syslog
进行网络日志输出,即可以输出应用日志,还可以输出系统日志。Android
使用android-logback
,但是配置启用syslogAppender
.
以上就是我们需要启用syslog
的原因。
<appender name="syslog" class="ch.qos.logback.classic.net.SyslogAppender">
<lazy>true</lazy>
<syslogHost>192.168.118.129</syslogHost>
<port>514</port>
<facility>LOCAL1</facility>
<reconnectionDelay>10000</reconnectionDelay>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
<charset>UTF-8</charset>
</appender>
<appender name="SYSLOG" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="syslog" />
</appender>
SyslogAppender的配置方式如上,suffixPattern
就是用来指定输出日志的信息格式的。配置格式参照[PatternLayout](http://www.logback.cn/06%E7%AC%AC%E5%85%AD%E7%AB%A0Layouts.html)
即可。
输出至sentry
sentry
是用python/django
写的一个通用开源的日志收集服务器,可以说功能相当强大,最关键的是各种语言的日志输出客户端都有,一般可以部署来进行日志收集。
使用sentry相当复杂,部署也比较重,一般使用官方的docker-compose进行部署,部署完毕后容器高达20几个,可以是一个相当重量级的应用,优点是可以进行大规模集群部署。
可以说,小型应用不推荐。
sentry
官方就提供了sentry-logback
库,直接引入就
implementation('io.sentry:sentry-logback:4.0.0-alpha.2') {
exclude group: 'ch.qos.logback'
}
sentry-logback
与android-logback
有引用冲突,需要配置一下exclude group。
小结
至此,使用android-logback的日志实践已基本可以工作。
但是还有几个问题需要解决:
- 静态的logback.xml配置文件不够灵活,我们需要能让用户或应用可以进行少量的配置,如是否启用网络日志等。
- 大型的Android应用可能涉及多个Library,如何实现日志的集中分发。
未完待续
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。