游戏后台报错邮件通知
没人生来杰出 --奥格瑞姆·毁灭之锤
游戏开发过程中或游戏上线后,我们都不可能每时每刻都盯着游戏后台监控后台运行情况。这时我们可以利用Logger4j自动发送报警邮件的功能,通知我们后台报错的地方,通过邮件统一收集这些报错,并一一修复,这样不仅可以知道平时编码过程中哪些地方容易犯错,而且更有利于打造一个稳定的游戏服务端。但是,即便有了邮件通知,作为后端程序员还是要养成工作中时常监控游戏后台运行的良好习惯。
Logger4j的自动邮件报警,关键在于配置,参照如下:
log4j.properties
log4j.rootCategory=info,stdout,file,mail
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC%-d{yyyy-MM-dd HH:mm:ss}] %p [%t] %C.%M(%L) | %m%n
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.Append=true
log4j.appender.file.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.file.File=logs/log
log4j.appender.file.Threshold=INFO
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[QC%-d{yyyy-MM-dd HH:mm:ss}] %p [%t] %C.%M(%L) | %m%n
log4j.appender.mail=warningMail.SMTPAppender
log4j.appender.mail.SMTPPort=587
log4j.appender.mail.Subject=Log4J Message
log4j.appender.mail.To=252878xxxx@qq.com
log4j.appender.mail.From=19072xxxx@qq.com
log4j.appender.mail.SMTPUsername=19072xxxx@qq.com
log4j.appender.mail.SMTPPassword=xxxxxxxxxx
log4j.appender.mail.SMTPHost=smtp.qq.com
log4j.appender.mail.Threshold=ERROR
log4j.appender.mail.layout=warningMail.HTMLLayout
注意上面的配置关键点:
1.在log4j.rootCategory=info,stdout,file,mail后面需加mail,注意需和后面的log4j.appender.mail里的mail大小写保持一致;
2.需要添加SMTPAppender(appender:向目的地发送格式化的输出)和Layout(布局:根据用户的愿望格式化日志记录请求),其中SMTPAppender是发送邮件一个重要的类,用于支持SMTP认证,其中SMTPUsername和SMTPPassword是登录SMTP服务器发送认证的用户名和密码,很多人认为SMTPPassword是邮箱密码,其实不是,而是开启邮箱SMTP服务的授权码,比如QQ邮箱的这个授权码获得方式如下图所示,如此操作后便能得到授权码,此外端口为587,SMTPHost为smtp.qq.com,公司的企业邮箱SMTPHost为smtp.exmail.qq.com:
QQ邮箱开启SMTP服务.png
3.SMTPAppender和Layout可以分别用org.apache.log4j.net.SMTPAppender和org.apache.log4j.HTMLLayout替换,也可以自己实现,比如上述例子的warningMail.SMTPAppender和warningMail.HTMLLayout就是项目包warningMail下自己实现的,自己实现的目的是因为完全可以添加更详尽的报错信息在邮件中展示,以及在游戏服的配置文件中用开关控制是否发送邮件。
自己实现SMTPAppender和Layout时,其实就是把org.apache.log4j下的相应文件拷过来,然后修改其中部分内容,让其更适合项目要求即可,比如我们可以用开关控制是否发送邮件,以及在邮件中展示报错堆栈的核心代码如下:
public void append(LoggingEvent event) {//即修改org.apache.log4j下的SMTPAppender文件此方法
if (!GameConfig.isErrorMailNotify) //用开关控制是否发送邮件
return;
if (!checkEntryConditions()) {
return;
}
event.getThreadName();
event.getNDC();
event.getMDCCopy();
if (locationInfo) {
event.getLocationInformation();
}
event.getRenderedMessage();
String[] throwables = event.getThrowableStrRep(); //报错堆栈
if (throwables == null)
return;
cb.add(event);
if (evaluator.isTriggeringEvent(event)) {
sendBuffer();
}
}
此外,还可以修改邮件标题,可以从标题中一眼就看出哪里报错了,核心代码如下:
protected String formatBody() {//也是修改org.apache.log4j下的SMTPAppender文件相应方法
// Note: this code already owns the monitor for this
// appender. This frees us from needing to synchronize on 'cb'.
StringBuffer sbuf = new StringBuffer();
String t = layout.getHeader();
if (t != null)
sbuf.append(t);
int len = cb.length();
for (int i = 0; i < len; i++) {
// sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
LoggingEvent event = cb.get();
int serverId = GameConfig.serverId;
String renderMsg = Transform.escapeTags(event.getRenderedMessage());
LocationInfo locas = event.getLocationInformation();
String localInfo = Transform.escapeTags(locas.getFileName())+":"+locas.getMethodName()+":"+locas.getLineNumber();
try {
msg.setSubject(serverId + " - " + localInfo + " - " + renderMsg );
} catch (MessagingException e) {
e.printStackTrace();
}
sbuf.append(layout.format(event));
if (layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
for (int j = 0; j < s.length; j++) {
sbuf.append(s[j]);
sbuf.append(Layout.LINE_SEP);
}
}
}
}
t = layout.getFooter();
if (t != null) {
sbuf.append(t);
}
return sbuf.toString();
}
这样,就可以向指定邮箱发送报警邮件了,测试代码如下:
public class Test {
private static final Logger logger = Logger.getLogger(Test.class);
public static void main(String[] args){
// 装入log4j配置信息
FileInputStream in;
try {
in = new FileInputStream("E:/ServerProject/ErrorWarningMail/src/main/resources/log4j.properties");
PropertyConfigurator.configure(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Map<Integer, Object> map = new HashMap<Integer, Object>();
map.put(1, new Object());
Object obj = map.get(2);
try{
obj.hashCode();
}catch(Exception e){
logger.error("obj null:", e);
}
}
}
最终的报警邮件展示如下图:
游戏后台报错邮件通知.png
截止文章发布时间2019/9/6时,以上配置是还可以正常工作的。
maven依赖如下:<properties> <log4j.version>1.2.17</log4j.version> <mail.version>1.4.7</mail.version> </properties> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>${mail.version}</version> </dependency> </dependencies>