Skip to content

Instantly share code, notes, and snippets.

@JunqiangYang
Last active June 30, 2017 08:50
Show Gist options
  • Save JunqiangYang/a9503577e20dd2995ba12cbb2be79135 to your computer and use it in GitHub Desktop.
Save JunqiangYang/a9503577e20dd2995ba12cbb2be79135 to your computer and use it in GitHub Desktop.
log 本地实现 单线程打印输出日志
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
/**
* 专为产生抛出对象的堆栈的封装
*
*/
final class ArrayWriter extends PrintWriter
{
private ArrayList<String> alist;
public ArrayWriter()
{
super(new NullWriter());
alist = new ArrayList<String>();
}
public void print(Object o)
{
alist.add(o.toString());
}
public void print(char[] chars)
{
alist.add(new String(chars));
}
public void print(String s)
{
alist.add(s);
}
public void println(Object o)
{
alist.add(o.toString());
}
public void println(char[] chars)
{
alist.add(new String(chars));
}
public void println(String s)
{
alist.add(s);
}
public void write(char[] chars)
{
alist.add(new String(chars));
}
public void write(char[] chars, int off, int len)
{
alist.add(new String(chars, off, len));
}
public void write(String s, int off, int len)
{
alist.add(s.substring(off, off+len));
}
public void write(String s)
{
alist.add(s);
}
public String[] toStringArray()
{
int len = alist.size();
String[] result = new String[len];
for(int i=0; i< len; i++)
{
result[i] = (String) alist.get(i);
}
return result;
}
}
class NullWriter extends Writer
{
public void close()
{
// blank
}
public void flush()
{
// blank
}
public void write(char[] cbuf, int off, int len)
{
// blank
}
}
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public final class Configure
{
private static final String LOG_CONF = "j4log.property";
private static boolean testFile(String filePath)
{
if(filePath == null || filePath.trim().equals("")){
return false;
}
FileOutputStream fout = null;
try
{
fout = new FileOutputStream(filePath + "." + Util.getDateSimpleInfo(System.currentTimeMillis()), true);
}
catch(FileNotFoundException e)
{
return false;
}
finally
{
try
{
fout.close();
}catch(Exception e){}
}
return true;
}
/**
* 读配置文件初始化j4log
*
*/
public static void init()
{
try
{
Properties prop = new Properties();
//prop.load(new FileInputStream("/usr/local/tomcat/webapps/waptest/WEB-INF/classes/j4log.property"));
//InputStream in = Configure.class.getResourceAsStream("/j4log.property");
InputStream in = Configure.class.getResourceAsStream("/"+LOG_CONF);
prop.load(in);
Set<Map.Entry<Object, Object>> entrySet = prop.entrySet();
Iterator<Map.Entry<Object, Object>> it = entrySet.iterator();
while(it.hasNext())
{
try{
Map.Entry<Object, Object> entry = it.next();
String loggerName = ((String)entry.getKey()).trim();
String valueStr = ((String)entry.getValue()).trim();
String[] arr = valueStr.split(",");
int level = Util.getLevelByStr(arr[0].trim());
if(arr.length == 2){ // 本地log
String filePath = arr[1].trim();
//先测试一下该文件是否配置有错,若有错,则打warnning,并不加进logger列表中
if(!testFile(filePath)){
System.err.println("!! the configure of " + loggerName + " is wrong! log path=["+filePath+"]");
continue;
}
Logger logger = Logger.getLogger(loggerName);
logger.initFileLog(level, filePath);
}else if(arr.length > 3){ // 远程log
/**
String type = arr[1].trim();
String[] s = arr[2].split(":");
InetAddress addr = null;
if( addr == null ){
System.err.println("!! the configure of " + loggerName + " is wrong! remote ip=["+s[0]+"]");
continue;
}
int port = StringUtil.convertInt(s.length > 1 ? s[1] : null, 60021);
String failLog = arr[3].trim();
if(failLog == null || failLog.equals("")) {
failLog = null;
System.err.println("!! the configure of " + loggerName + " warning: failLog is empty.");
} else if(!testFile(failLog)){
System.err.println("!! the configure of " + loggerName + " is wrong! log path=["+failLog+"]");
continue;
}
String localLog = arr.length > 4 ? arr[4].trim() : null;
if( localLog!=null && !testFile(localLog)){
System.err.println("!! the configure of " + loggerName + " is wrong! log path=["+localLog+"]");
continue;
}
Logger logger = Logger.getLogger(loggerName);
logger.initLogClient(level, type, addr, port, localLog, failLog);
**/
}else{
System.err.println("!! the configure of " + loggerName + " is wrong: " + valueStr);
}
}catch (Exception e){
e.printStackTrace();
}
}
Thread th = new Thread(new LogWorkThread(), "logger_thread");
th.start();
// Thread thRemote = new Thread(new LogWorkThread(true), "logger_thread");
// thRemote.start();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
public class Logger
{
//静态变量
private static final String DEFAULT_LOG = "_j4log";
private static ConcurrentHashMap<String, Logger> loggerMap = new ConcurrentHashMap<String, Logger>();//所有的Logger对象
static{
Configure.init();//读取j4log.property文件
}
//本地日志和远程日志共有的属性
private String name = null; //log的名字
private String filePath = null; //log的路径
private int level = 0; //log的level,其中0-debug,1-info,2-warn,3-error,4-fatal
private LinkedBlockingQueue<LogItem> logQueue = new LinkedBlockingQueue<LogItem>(50000);
//远程日志特有的属性
private String type = null;
private String failLogPath = null; // 失败路径
//构造方法没有public,只能通过getLogger方法生成实例
private Logger(String name)
{
this.name = name;
}
private Logger(String name, String filePath, int level)
{
this.name = name;
this.filePath = filePath;
this.level = level;
}
/**
* 工厂方法,根据名字来获取得一个Logger,同样的名字返回同一个实例
* @param name Logger的名字
* @return Logger
*/
public static Logger getLogger(String name){
Logger logger = loggerMap.get(name);
if(logger == null){
synchronized( Logger.class ){
logger = loggerMap.get(name);
if( logger==null ){
logger = new Logger(name);
loggerMap.put(name,logger);
}
}
}
return logger;
}
public static Map<String, Logger> getLoggerMap(){
return loggerMap;
}
//更改本地日志属性,Configure.init()时候用
void initFileLog(int level, String filePath)
{
this.level = level;
this.filePath = filePath;
this.failLogPath = null;
this.type = null;
}
//更改远程日志属性,Configure.init()时候用
void initLogClient(int level, String type, InetAddress addr, int port, String localLogPath, String failLogPath)
{
this.level = level;
this.type = type;
this.filePath = localLogPath;
this.failLogPath = failLogPath;
}
//判断是否远程日志
boolean isRemoteLog()
{
return false;
}
//日志内容放入队列,如果发现有本地日志没有配置本地文件的,放入DEFAULT_LOG的队列
private void log(int level, String str, Throwable th)
{
if(str == null)
str="";
if(level < this.level) //只打比配置的level要高的log
{
return;
}
if( (!isRemoteLog()) && (this.filePath == null) && (!DEFAULT_LOG.equals(this.name)) )
{
Logger.getLogger(DEFAULT_LOG).log(level,this.name+"\t"+str,th);
return;
}
boolean result = logQueue.offer(new LogItem(level, str, th));
if( !result ){//队列满
if( !DEFAULT_LOG.equals(this.name) ){
Logger.getLogger(DEFAULT_LOG).log(level,this.name+"\t"+str,th);
return;
}else{//DEFAULT_LOG队列也满了,直接丢弃了。
return;
}
}
}
public boolean isInfoEnabled()
{
return (this.level <= Util.LEVEL_INFO);
}
public boolean isDebugEnabled()
{
return (this.level <= Util.LEVEL_DEBUG);
}
/**
* 打level为debug的log
* @param str 要打的log
*/
public void debug(String str)
{
log(Util.LEVEL_DEBUG, str, null);
}
/**
* 打level为debug的log,并将异常的堆栈也打出来
* @param str 要打的log
* @param th 异常的堆栈
*/
public void debug(String str, Throwable th)
{
log(Util.LEVEL_DEBUG, str, th);
}
/**
* 打level为info的log
* @param str 要打的log
*/
public void info(String str)
{
log(Util.LEVEL_INFO, str, null);
}
/**
* 打level为info的log,并将异常的堆栈也打出来
* @param str 要打的log
* @param th 异常的堆栈
*/
public void info(String str, Throwable th)
{
log(Util.LEVEL_INFO, str, th);
}
/**
* 打level为warn的log
* @param str 要打的log
*/
public void warn(String str)
{
log(Util.LEVEL_WARN, str, null);
}
/**
* 打level为warn的log,并将异常的堆栈也打出来
* @param str 要打的log
* @param th 异常的堆栈
*/
public void warn(String str, Throwable th)
{
log(Util.LEVEL_WARN, str, th);
}
/**
* 打level为error的log
* @param str 要打的log
*/
public void error(String str)
{
log(Util.LEVEL_ERROR, str, null);
}
/**
* 打level为error的堆栈
* @param str 要打的log
* @param th 异常的堆栈
*/
public void error(String str, Throwable th)
{
log(Util.LEVEL_ERROR, str, th);
}
/**
* 打level为fatal的log
* @param str 要打的log
*/
public void fatal(String str)
{
log(Util.LEVEL_FATAL, str, null);
}
/**
* 打level为fatal的log,并将异常的堆栈也打出来
* @param str 要打的log
* @param th 异常的堆栈
*/
public void fatal(String str, Throwable th)
{
log(Util.LEVEL_FATAL, str, th);
}
//打日志线程的回调方法
void doWriteLog() throws UnsupportedEncodingException,
FileNotFoundException, IOException{
writeAllToLocal(filePath);
}
private void writeAllToLocal(String file) throws UnsupportedEncodingException,
FileNotFoundException, IOException {
// local log
boolean succ = false;
if( file != null ){
BufferedWriter bw = null;
try{
int endindex = file.lastIndexOf(File.separator);
if(endindex>0){
String path = file.substring(0,endindex);
File dirname = new File(path);
if(!dirname.exists()){ //目录不存在
dirname.mkdir(); //创建目录
}
}
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file + "." + Util.getDateSimpleInfo(System.currentTimeMillis()), true), "utf-8"));
LogItem item = null;
while( (item = logQueue.poll()) != null ){
bw.write(item.toString());
}
succ = true;
}finally{
if(bw != null)
bw.close();
}
}
if( !succ ){
LogItem item = null;
while( (item = logQueue.poll()) != null ){
System.out.print("\t"+ item.toString());
}
}
}
/**
//打本地日志,如果失败,丢弃了
private void writeToLocal(ArrayList<String> content, String file) throws IOException
{
Writer bw = null;
try{
OutputStream os = null;
try{
os = new FileOutputStream(file + "." + Util.getDateSimpleInfo(System.currentTimeMillis()), true);
bw = new BufferedWriter(new OutputStreamWriter(os, "gbk"));
for(String s : content){
bw.write(s);
bw.write("\n");
}
bw.flush();
return;
}catch(Exception e){
}
//如果输出file有问题,丢弃算了
}finally{
if(bw != null)
bw.close();
}
}
**/
//下面全部是getter/setter方法
public String getName()
{
return name;
}
public String getFilePath()
{
return filePath;
}
public int getLevel()
{
return level;
}
public void setLevel(int level)
{
this.level = level;
}
public Queue<LogItem> getQueue()
{
return logQueue;
}
public void setRemoteAddr(InetAddress addr, int port){
return;
}
public void setRemoteType(String type){
this.type = type;
}
@Override
public String toString(){
String s = "";
s += this.name + "|";
s += this.logQueue.size() + "|";
s += this.level + "|";
s += this.type + "|";
s += this.filePath + "|";
s += this.failLogPath + "|";
return s;
}
public static void main(String[] argv) throws InterruptedException
{
Logger l = Logger.getLogger("testRemoteLog");
for(int i = 0; i < 1000; ++i){
l.debug("远程log测试: " + i);
//Thread.sleep(100);
}
System.out.println("---finish---");
// testSytemProperty();
}
}
import com.nyonline.util.MilliSecClock;
/**
* 每行log的一个封装
*
*/
final class LogItem
{
private long time; //该log发生的时间
private int level; //该log的level
private String str; //该log的描述
private ThrowableInfo throwInfo = null; //该log的throwInfo
public LogItem(int level, String str, Throwable th)
{
this.time = MilliSecClock.currentTimeMillis();
this.level = level;
this.str = str;
if(th != null)
{
this.throwInfo = new ThrowableInfo(th);
}
}
public long getTime()
{
return this.time;
}
public int getLevel()
{
return this.level;
}
public String getStr()
{
return this.str;
}
public ThrowableInfo getThrowInfo()
{
return this.throwInfo;
}
String toStringNoEndReturn()
{
StringBuilder strBuf = new StringBuilder();
strBuf.append(Util.getDateAllInfo(this.time)).append("\t");
strBuf.append(Util.levelStrArr[this.level]).append("\t");
strBuf.append(this.str);
if(this.throwInfo != null)
{
strBuf.append(this.throwInfo.getThrowableStr());
}
return strBuf.toString();
}
public String toString() //获取每个log的描述
{
StringBuilder strBuf = new StringBuilder();
strBuf.append(Util.getDateAllInfo(this.time)).append("\t");
strBuf.append(Util.levelStrArr[this.level]).append("\t");
strBuf.append(this.str).append("\n");
if(this.throwInfo != null)
{
strBuf.append(this.throwInfo.getThrowableStr());
}
return strBuf.toString();
}
}
import java.util.Collection;
final class LogWorkThread implements Runnable
{
private boolean handleRemoteLog = false;
public LogWorkThread()
{
}
public LogWorkThread(boolean handleRemoteLog)
{
this.handleRemoteLog = handleRemoteLog;
}
public void run()
{
while(true)
{
try {
Collection<Logger> coll = Logger.getLoggerMap().values();
for( Logger logger : coll )
{
if((logger.isRemoteLog() && handleRemoteLog)
||(!logger.isRemoteLog() && !handleRemoteLog))
{
try
{
logger.doWriteLog();
// Thread.sleep(50); //每写完一个logger,sleep 50毫秒
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Thread.sleep(1500); //每次sleep 1.5秒
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import com.nyonline.nyutil.StringUtil;
/**
* 统计日志
*
* @author jake
* @date 2017年5月10日
*/
public class StatLog {
// 日志级别及路径
private static final String statlog_file_level = "INFO"; // 日志级别
private static final String statlog_file_path = "d:"+File.separator+"data"+File.separator+"logcenter"; // 日志路径
private static final Map<String, Logger> logMap = new HashMap<String, Logger>();
private static Logger log = Logger.getLogger("stat_error"); // 正常输出失败时,输出到这里
/**
* 统计
* @param logname 统计日志名称(stat,app_acccess,report)
* @param objs 日志参数
*/
public static void printLog(String logname, Object ...objs){
Logger logger = getLogMap(logname);
if(logger!=null){
logger.info(StringUtil.join(objs));
}else{
log.info(StringUtil.join(objs));
}
}
/** 获取对应文件名Logger */
private static Logger getLogMap(String logname){
synchronized(StatLog.class){
if(logMap.get(logname)!=null){
return logMap.get(logname);
}else{
if(logMap.get(logname)==null){
Logger logger = Logger.getLogger(logname);
String path = statlog_file_path+File.separator+getAppId()+File.separator;
File dirname = new File(path);
if(!dirname.isDirectory()){ //目录不存在
dirname.mkdir(); //创建目录
}
logger.initFileLog(Util.getLevelByStr(statlog_file_level), path+logname+".log");
logMap.put(logname, logger);
}
return logMap.get(logname);
}
}
}
private static String getAppId(){
URL path = Logger.class.getClassLoader().getResource("");
if(path==null){
path = ClassLoader.getSystemClassLoader().getResource("");
}
return "abc";
// return path.getPath().replace("/", "_").replaceFirst("(\\d+\\.)+\\d+", "V");
}
}
import java.io.File;
public class Test {
public static void main(String ...args) throws Throwable {
File dirname = new File("d:/data/logcenter");
System.out.println(dirname.isDirectory());
if(!dirname.isDirectory()){ //目录不存在
System.out.println(dirname.mkdir()); //创建目录
}
int endindex = ("abd"+File.separator+"cdbd"+File.separator).lastIndexOf(File.separator);
System.out.println(endindex);
// StatLog.printLog("report", 1,2,3);
// Logger log = Logger.getLogger("test");
// log.info("test");
Thread.sleep(100);
System.exit(0);
}
}
/**
* 对于抛出对象的封装
*
*/
final class ThrowableInfo
{
private transient Throwable throwable;
private String[] rep;
public ThrowableInfo(Throwable throwable)
{
this.throwable = throwable;
}
public Throwable getThrowable()
{
return throwable;
}
public String[] getThrowableStrArr()
{
if(throwable == null)
{
return null;
}
if(rep != null)
{
return (String[]) rep.clone();
}
else
{
ArrayWriter aw = new ArrayWriter();
throwable.printStackTrace(aw);
rep = aw.toStringArray();
return rep;
}
}
public String getThrowableStr()
{
String[] arr = getThrowableStrArr();
if(arr == null)
{
return "";
}
StringBuilder strBuf = new StringBuilder();
for(int i=0; i<arr.length; i++)
{
strBuf.append(arr[i]).append("\n");
}
return strBuf.toString();
}
}
import java.util.Calendar;
import java.util.Date;
final class Util
{
public static final int LEVEL_DEBUG = 0;
public static final int LEVEL_INFO = 1;
public static final int LEVEL_WARN = 2;
public static final int LEVEL_ERROR = 3;
public static final int LEVEL_FATAL = 4;
public static String[] levelStrArr = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
/**
* 根据level的名字,如DEBUG等,获取其内部的index
* @param levelStr
* @return
*/
public static final int getLevelByStr(String levelStr)
{
if(levelStr == null)
{
return -1;
}
for(int i=0; i<levelStrArr.length; i++)
{
if(levelStr.equalsIgnoreCase(levelStrArr[i]))
{
return i;
}
}
return -1;
}
/**
* 根据时间,获取现时的日期的字符串,如2006-02-21
* @param time
* @return
*/
public static final String getDateSimpleInfo(long time)
{
Date date = new Date(time);
Calendar ca = Calendar.getInstance();
ca.setTime(date);
int month = 1 + ca.get(Calendar.MONTH);
String monthStr = String.valueOf(month);
if(month < 10)
{
monthStr = "0" + monthStr;
}
int day = ca.get(Calendar.DAY_OF_MONTH);
String dayStr = String.valueOf(day);
if(day < 10)
{
dayStr = "0" + dayStr;
}
String result = ca.get(Calendar.YEAR) + "-" + monthStr + "-" + dayStr;
return result;
}
/**
* 根据时间,获取现时的时间的字符串,如2006-02-21 17:49:43.156
* @param time
* @return
*/
public static final String getDateAllInfo(long time)
{
Date date = new Date(time);
Calendar ca = Calendar.getInstance();
ca.setTime(date);
int month = 1 + ca.get(Calendar.MONTH);
String monthStr = String.valueOf(month);
if(month < 10)
{
monthStr = "0" + monthStr;
}
int day = ca.get(Calendar.DAY_OF_MONTH);
String dayStr = String.valueOf(day);
if(day < 10)
{
dayStr = "0" + dayStr;
}
int hour = ca.get(Calendar.HOUR_OF_DAY);
String hourStr = String.valueOf(hour);
if(hour < 10)
{
hourStr = "0" + hourStr;
}
int minute = ca.get(Calendar.MINUTE);
String minuteStr = String.valueOf(minute);
if(minute < 10)
{
minuteStr = "0" + minuteStr;
}
int second = ca.get(Calendar.SECOND);
String secondStr = String.valueOf(second);
if(second < 10)
{
secondStr = "0" + second;
}
StringBuilder strBuf = new StringBuilder();
strBuf.append(ca.get(Calendar.YEAR)).append("-").append(monthStr).append("-").append(dayStr);
strBuf.append(" ").append(hourStr).append(":").append(minuteStr).append(":").append(secondStr).append(".").append(ca.get(Calendar.MILLISECOND));
return strBuf.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment