/*
* Tencent is pleased to support the open source community by making
* Tencent GT (Version 2.4 and subsequent versions) available.
*
* Notwithstanding anything to the contrary herein, any previous version
* of Tencent GT shall not be subject to the license hereunder.
* All right, title, and interest, including all intellectual property rights,
* in and to the previous version of Tencent GT (including any and all copies thereof)
* shall be owned and retained by Tencent and subject to the license under the
* Tencent GT End User License Agreement (http://gt.qq.com/wp-content/EULA_EN.html).
*
* Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the MIT License (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License at
*
* http://opensource.org/licenses/MIT
*
* 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.tencent.wstt.gt.log;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.tencent.wstt.gt.api.utils.Env;
import com.tencent.wstt.gt.dao.GTPref;
import com.tencent.wstt.gt.utils.FileUtil;
import com.tencent.wstt.gt.utils.StringUtil;
public class LogController extends AbsLogController {
private File curFile;
private String curLogFolderName = Env.S_ROOT_LOG_FOLDER;
private File curLogFolder = new File(curLogFolderName);
// 用于保存过滤下拉列表的状态
private int curSelectedLevel;
private String sCurSelectedTag = "";
private String sCurSelectedMsg = "";
private LinkedList<String> msgHistory = new LinkedList<String>();
private LinkedList<String> curShowDownMsgList = new LinkedList<String>();
private Map<File, FileWriter> tempLogFileWriterMap;
private Set<String> tempLogStartingSet;
private boolean allowAutoSave; // 是否自动保存日志
private boolean saveDefaultSeg; // 保存日志的时候是否保存GT日志前缀
public LogController()
{
super();
// TODO 用并发的数据结果更保险一些,待测试观察
// tempLogFileWriterMap = new ConcurrentHashMap<File, FileWriter>();
tempLogFileWriterMap = Collections.synchronizedMap(new HashMap<File, FileWriter>());
tempLogStartingSet = Collections.synchronizedSet(new HashSet<String>());
/*
* 初始化curFile
*/
setLastestLogFileAsCurFile();
allowAutoSave = GTPref.getGTPref().getBoolean(GTPref.LOG_AUTOSAVE_SWITCH, false);
try {
if (allowAutoSave)
{
// 要将自动保存目录加到目录容器中,参考setAutoSave的逻辑
addTempLog(curFile.getAbsolutePath(), true);
}
} catch (IOException e) {
e.printStackTrace();
}
/*
* 从2.2.1版本开始,强制要求日志符合logcat格式
*/
// saveDefaultSeg = GTPref.getGTPref().getBoolean(GTPref.LOG_SAVE_DEFAULT_SEG, true);
saveDefaultSeg = true;
}
/*
* ===================过滤下拉状态保存相关getter和setter=====================
*/
public int getCurSelectedLevel() {
return curSelectedLevel;
}
public void setCurSelectedLevel(int curSelectedLevel) {
this.curSelectedLevel = curSelectedLevel;
}
public String getsCurSelectedTag() {
return sCurSelectedTag;
}
public void setsCurSelectedTag(String sCurSelectedTag) {
this.sCurSelectedTag = sCurSelectedTag;
}
public String getsCurSelectedMsg() {
return sCurSelectedMsg;
}
public void setsCurSelectedMsg(String sCurSelectedMsg) {
this.sCurSelectedMsg = sCurSelectedMsg;
}
public LinkedList<String> getMsgHistory() {
return msgHistory;
}
public void setMsgHistory(LinkedList<String> msgHistory) {
this.msgHistory = msgHistory;
}
public LinkedList<String> getCurShowDownMsgList() {
return curShowDownMsgList;
}
public void setCurShowDownMsgList(LinkedList<String> curShowDownMsgList) {
this.curShowDownMsgList = curShowDownMsgList;
}
/*
* =============================保存日志相关方法=============================
*/
public void changeCurrentLogFolder(String appName)
{
curLogFolderName = Env.S_ROOT_LOG_FOLDER + appName + "/";
curLogFolder = new File(curLogFolderName);
if (! curLogFolder.exists())
{
curLogFolder.mkdirs();
}
/*
* 初始化curFile
*/
removeTempLog(curFile.getAbsolutePath());
setLastestLogFileAsCurFile();
if(allowAutoSave)
{
try {
addTempLog(curFile.getAbsolutePath(), true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void beforeAutoSave(StringBuffer bf)
{
if (! allowAutoSave)
{
return;
}
// 如果当前文件被用户从文件系统删掉了等情况,重建当前文件
if(!curFile.exists())
{
removeTempLog(curFile.getAbsolutePath());
try {
addTempLog(curFile.getAbsolutePath(), true);
} catch (IOException e) {
e.printStackTrace();
}
}
// 如果当前文件写满了,就按文件名去取下一个文件为当前文件
else if (curFile.length() >= LogUtils.CAPACITY)
{
removeTempLog(curFile.getAbsolutePath());
switch2NextLogFile();
try {
addTempLog(curFile.getAbsolutePath(), true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void setLastestLogFileAsCurFile()
{
// 遍历日志文件夹中的日志文件,取最新的为当前写目标
if (!curLogFolder.exists())
{
curLogFolder.mkdirs();
}
// 过滤出合法的日志文件
File[] files = curLogFolder.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.matches(LogUtils.LOG_FILE_MATCHE);
}
});
// 按修改时间排序
if (null != files && files.length > 0)
{
Arrays.sort(files, 0, files.length, new Comparator<File>() {
@Override
public int compare(File lhs, File rhs) {
return lhs.lastModified() <= rhs.lastModified() ? 1 : -1;
}
});
// 取最新的文件为当前文件
curFile = files[0];
}
else
{
curFile = new File(curLogFolder, 00 + LogUtils.LOG_POSFIX);
}
}
private void switch2NextLogFile()
{
File newFile = null;
int newFileSeq = 0;
int curFileNameSeq = 0;
String curFileName = curFile.getName();
if (curFileName.length() > 2
&& StringUtil.isNumeric(curFileName.substring(0, 2)))
{
curFileNameSeq = Integer.parseInt(curFile.getName().substring(0, 2));
}
else
{
curFileNameSeq = Integer.parseInt(curFile.getName().substring(0, 1));
}
if (curFileNameSeq < 99)
{
newFileSeq = curFileNameSeq + 1;
}
if (newFileSeq < 10)
{
newFile = new File(curLogFolder, "0" + newFileSeq + LogUtils.LOG_POSFIX);
}
else
{
newFile = new File(curLogFolder, newFileSeq + LogUtils.LOG_POSFIX);
}
if (newFile.exists())
{
newFile.delete();
}
try {
newFile.createNewFile();
curFile = newFile;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 只由TempLogConsumer调用,考虑移过去
* @param path
* @param append 此日志是否追加
*/
public void addTempLog(String path, boolean append) throws IOException
{
if (null == path)
{
return;
}
File f = null;
if (FileUtil.isPathStringValid(path))
{
String validPath = FileUtil.convertValidFilePath(path, LogUtils.LOG_POSFIX);
if (FileUtil.isPath(validPath))
{
f = new File(validPath);
}
else
{
f = new File(Env.ROOT_LOG_FOLDER, validPath);
}
try
{
if (! f.exists())
{
f.createNewFile();
}
FileWriter fw = new FileWriter(f, append);
tempLogFileWriterMap.put(f, fw);
}
catch(IOException e)
{
throw new IOException();
}
}
}
/**
* 只由TempLogConsumer调用,考虑移过去
* @param path
*/
public void removeTempLog(String path)
{
if (path == null)
{
return;
}
File f = null;
if (FileUtil.isPathStringValid(path))
{
String validPath = FileUtil.convertValidFilePath(path, LogUtils.LOG_POSFIX);
if (FileUtil.isPath(validPath))
{
f = new File(validPath);
}
else
{
f = new File(Env.ROOT_LOG_FOLDER, validPath);
}
FileWriter fr = tempLogFileWriterMap.remove(f);
FileUtil.closeWriter(fr);
}
}
/**
* 对path代表的文件执行该方法前,需要保证已执行过removeTempLog(path)操作关闭输出流,
* 否则可能文件删除失败。
* @param path
* @throws CleanTempLogException
*/
public void cleanTempLog(String path) throws CleanTempLogException
{
File f = null;
if (FileUtil.isPathStringValid(path))
{
String validPath = FileUtil.convertValidFilePath(path, LogUtils.LOG_POSFIX);
if (FileUtil.isPath(validPath))
{
f = new File(validPath);
}
else
{
f = new File(Env.ROOT_LOG_FOLDER, validPath);
}
if (f.exists())
{
// 抛出自定义异常才合理
boolean result = f.delete();
if (! result)
{
throw new CleanTempLogException();
}
}
}
}
public void setAutoSave(boolean flag)
{
allowAutoSave = flag;
GTPref.getGTPref().edit().putBoolean(GTPref.LOG_AUTOSAVE_SWITCH, flag).commit();
if (flag)
{
try {
addTempLog(curFile.getAbsolutePath(), true);
} catch (IOException e) {
e.printStackTrace();
}
}
else
{
removeTempLog(curFile.getAbsolutePath());
}
}
public boolean getAutoSave()
{
return allowAutoSave;
}
public void setSaveDefaultSeg(boolean flag)
{
saveDefaultSeg = flag;
GTPref.getGTPref().edit().putBoolean(GTPref.LOG_SAVE_DEFAULT_SEG, flag).commit();
}
public boolean getSaveDefaultSeg()
{
return saveDefaultSeg;
}
public boolean hasTempLog()
{
return tempLogStartingSet.size() > 0;
}
public void setTempLogStarting(String fileName)
{
tempLogStartingSet.add(fileName);
}
public void reudceTempLogStarting(String fileName)
{
tempLogStartingSet.remove(fileName);
}
/**
* 日志的消费者就是单个线程,所以目前本方法可以不针对LogBuff加锁
* @param sb
*/
public void add2Save(StringBuffer sb)
{
beforeAutoSave(sb);
synchronized (tempLogFileWriterMap) {
for (Entry<File, FileWriter> entry : tempLogFileWriterMap.entrySet())
{
LogUtils.writeBuff(sb, entry.getKey(), entry.getValue());
}
}
}
/**
* 当出现文件被用户手动删除的情况,需要更新
*/
public void endAllLog()
{
synchronized (tempLogFileWriterMap) {
for (FileWriter writer : tempLogFileWriterMap.values())
{
FileUtil.closeWriter(writer);
}
}
}
}