View Javadoc
1   /**
2    * Copyright (c) Glodon Co. Ltd.
3    */
4   package gboat2.base.logging.action;
5   
6   import gboat2.base.core.annotation.Domain;
7   import gboat2.base.core.annotation.Module;
8   import gboat2.base.core.annotation.Preference;
9   import gboat2.base.core.logging.BusinessLogEntry;
10  import gboat2.base.core.web.BaseActionSupport;
11  import gboat2.base.core.web.JsonResultSupport;
12  
13  import java.io.BufferedReader;
14  import java.io.File;
15  import java.io.FileInputStream;
16  import java.io.FileNotFoundException;
17  import java.io.FileOutputStream;
18  import java.io.FileReader;
19  import java.io.IOException;
20  import java.io.OutputStream;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.util.Properties;
24  
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.log4j.LogManager;
27  import org.apache.log4j.PropertyConfigurator;
28  import org.apache.log4j.helpers.Loader;
29  import org.apache.log4j.helpers.LogLog;
30  import org.apache.log4j.spi.LoggerRepository;
31  import org.apache.struts2.convention.annotation.ParentPackage;
32  import org.apache.struts2.convention.annotation.Result;
33  import org.apache.struts2.convention.annotation.ResultPath;
34  import org.apache.struts2.convention.annotation.Results;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * @author zhangxj-a
40   *
41   */
42  @Preference("/系统配置/日志配置")
43  @Results(value = { @Result(name = "success", location = "/content/index.vm"), @Result(name = "list", location = "/content/index-list.vm") })
44  @ParentPackage(value = "log")
45  @Domain(value = BusinessLogEntry.class)
46  @ResultPath("/content")
47  @Module(name = "日志配置管理", desc = "配置日志的记录级别")
48  public class LogConfigAction extends BaseActionSupport {
49  
50  	/** */
51  	private static final long serialVersionUID = 1L;
52  
53  	private static Logger logger = LoggerFactory.getLogger(LogConfigAction.class.getName());
54  
55  	private static final String BR = "\r\n";
56  
57  	/**
58  	 * 获得WEB_APP_ROOT
59  	 */
60  	public final static String WEB_APP_ROOT = System.getProperty("webapp.gboat2.root");
61  
62  	/**
63  	 * 业务日志地址LOG_CONFIG_LOCATION
64  	 */
65  	public final static String BUSI_LOG_CONFIG_LOCATION = "WEB-INF/config/businessLog.properties";
66  
67  	/**
68  	 * 系统日志地址LOG_CONFIG_LOCATION
69  	 */
70  	public final static String SYS_LOG_CONFIG_LOCATION = "WEB-INF/config/log4j.properties";
71  
72  	/**
73  	 * 系统日志地址DIRECTORY
74  	 */
75  	public final static String SYS_LOG_DIRECTORY = "WEB-INF/config";
76  
77  	/**
78  	 * 文件名称
79  	 */
80  	public final static String LOG4J_FILENAME = "log4j.properties";
81  
82  	/**业务日志级别key */
83  	public final static String BUSINESS_LEVEL = "business.level";
84  
85  	/**系统日志级别key */
86  	public final static String LOG4J_ROOTLOGGER = "log4j.rootLogger";
87  
88  	private String businessLevel;
89  
90  	private String systemLogRootLevel;
91  
92  	private String properties;
93  
94  	public String getProperties() {
95  		return properties;
96  	}
97  
98  	public void setProperties(String properties) {
99  		this.properties = properties;
100 	}
101 
102 	public String getBusinessLevel() {
103 		return businessLevel;
104 	}
105 
106 	public void setBusinessLevel(String businessLevel) {
107 		this.businessLevel = businessLevel;
108 	}
109 
110 	public String getSystemLogRootLevel() {
111 		return systemLogRootLevel;
112 	}
113 
114 	public void setSystemLogRootLevel(String systemLogRootLevel) {
115 		this.systemLogRootLevel = systemLogRootLevel;
116 	}
117 
118 	/* (non-Javadoc)
119 	 * @see gboat2.base.action.IBaseAction#edit()
120 	 */
121 	public String execute() {
122 		//准备默认数据
123 		setDefaultRootLevel();
124 		//把默认值设置到页面
125 		setDefaultValueToPage();
126 
127 		return null;
128 	}
129 
130 	/**
131 	 * 业务,系统日志配置页面
132 	 * @return null
133 	 * @throws IOException  对response中的writer操作抛出检查异常IOException
134 	 */
135 	public String properties() throws IOException {
136 		setProperties(readFromFile(getSystemLogRealPath()));
137 
138 		//		JSONArray data = new JSONArray();
139 		//		JSONObject obj = new JSONObject();
140 		//		obj.accumulate("properties", getProperties());
141 		//		data.add(obj);
142 
143 		//		JSONObject wrap = JsonResultSupport.wrap(true);
144 		//		wrap.accumulate("data", data);
145 		//		JsonResultSupport.output(wrap);
146 
147 		return null;
148 	}
149 
150 	/**
151 	 * 更新log4j.properties
152 	 * @return null
153 	 * @throws IOException 对response中的writer操作抛出检查异常IOException
154 	 */
155 	public String saveLog4jConfig() throws IOException {
156 		//写入配置文件
157 		writeToFile(getProperties());
158 
159 		JsonResultSupport.output(JsonResultSupport.wrap(true));
160 
161 		return null;
162 	}
163 
164 	/**
165 	 * 为页面设置默认值
166 	 * @throws IOException 对response中的writer操作抛出检查异常IOException
167 	 */
168 	private void setDefaultValueToPage() {
169 		//		JSONArray data = new JSONArray();
170 		//		JSONObject obj = new JSONObject();
171 		//		obj.accumulate("businessLevel", getBusinessLevel());
172 		//		obj.accumulate("systemLogRootLevel", getSystemLogRootLevel());
173 		//		data.add(obj);
174 		//		
175 		//		JSONObject wrap = JsonResultSupport.wrap(true);
176 		//		wrap.accumulate("data", data);
177 		//		JsonResultSupport.output(wrap, response);
178 	}
179 
180 	private String getBusinessLogRealPath() {
181 		checkWebAppRootKeyExist();
182 		return WEB_APP_ROOT + BUSI_LOG_CONFIG_LOCATION;
183 	}
184 
185 	private void checkWebAppRootKeyExist() {
186 		if (WEB_APP_ROOT == null) {
187 			LogLog.warn("can't get web app root,please add context param into web.xml like below:");
188 			LogLog.warn("<context-param><param-name>webAppRootKey</param-name><param-value>webapp.gboat2.root</param-value></context-param>");
189 		}
190 	}
191 
192 	private String getSystemLogRealPath() {
193 		checkWebAppRootKeyExist();
194 		return WEB_APP_ROOT + SYS_LOG_CONFIG_LOCATION;
195 	}
196 
197 	/**
198 	 * 使修改的系统日志级别生效
199 	 * @return null
200 	 * @throws IOException   io操作异常 
201 	 * @throws IllegalAccessException 非法访问
202 	 * @throws InstantiationException 实例创建失败
203 	 * @throws InvocationTargetException 反射异常
204 	 * @throws NoSuchMethodException   发射
205 	 */
206 	public String apply() throws IOException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
207 
208 		try {
209 			Properties busiProps = loadProperties(getBusinessLogRealPath());
210 			Properties sysProps = loadProperties(getSystemLogRealPath());
211 
212 			//读取系统配置文件的内容
213 			setProperties(readFromFile(getSystemLogRealPath()));
214 
215 			//业务日志采用properties.store方式处理
216 			busiProps.put(BUSINESS_LEVEL, getBusinessLevel());
217 			storeProperties(busiProps, getBusinessLogRealPath());
218 			//系统日志采用流的方式处理,防止格式会乱
219 			writeToFile(getProperties());
220 
221 			//重新配置日志(bundle内部)
222 			reBundleConfigLog4jProperies(sysProps);
223 
224 			//重新配置外部环境日志
225 			reRuntimeEnvConfigLog4jProperties();
226 
227 		} catch (FileNotFoundException e) {
228 			// TODO Auto-generated catch block
229 			LogLog.warn("  log properties don't not exist ,please check ");
230 			JsonResultSupport.output(JsonResultSupport.wrap(false));
231 			return null;
232 		} catch (IOException e) {
233 			// TODO Auto-generated catch block
234 			JsonResultSupport.output(JsonResultSupport.wrap(false));
235 			LogLog.warn("  log properties don't not exist ,please check ");
236 			return null;
237 		} catch (ClassNotFoundException e) {
238 			LogLog.warn("  log properties don't not exist ,please check ");
239 			// TODO Auto-generated catch block
240 		} catch (Exception e) {
241 			LogLog.warn("  log properties don't not exist ,please check ");
242 			// TODO Auto-generated catch block
243 		}
244 
245 		JsonResultSupport.output(JsonResultSupport.wrap(true));
246 
247 		return null;
248 	}
249 
250 	/**
251 	 * 重新加载外部环境的日志,使外部容器的日志生效
252 	 * @throws ClassNotFoundException 异常
253 	 * @throws NoSuchMethodException  异常
254 	 * @throws IllegalAccessException 异常
255 	 * @throws InvocationTargetException 异常
256 	 * @throws InstantiationException 异常
257 	 */
258 	@SuppressWarnings("unchecked")
259 	private void reRuntimeEnvConfigLog4jProperties() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
260 			InvocationTargetException, InstantiationException {
261 		Class<?> propertyConfigurator = Loader.loadClass(PropertyConfigurator.class.getName());
262 		Class<?> loggerRepository = Loader.loadClass(LoggerRepository.class.getName());
263 		Class<?> logManager = Loader.loadClass(LogManager.class.getName());
264 		Method getLoggerRepository = logManager.getMethod("getLoggerRepository");
265 		Class[] paramTypes = new Class[] { String.class, loggerRepository };
266 		Object[] params = new Object[] { getSystemLogRealPath(), getLoggerRepository.invoke(null) };
267 		Method doConfigure = propertyConfigurator.getMethod("doConfigure", paramTypes);
268 		doConfigure.invoke(propertyConfigurator.newInstance(), params);
269 	}
270 
271 	/**
272 	 * 应用bundle之间的日志配置
273 	 * @param props 文件内容
274 	 */
275 	private void reBundleConfigLog4jProperies(Properties props) {
276 		//把当前线程的加载器换成系统加载器,不然会导致日志类加载不一致
277 		LogManager.resetConfiguration();
278 		LogManager.shutdown();
279 		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
280 		Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
281 		PropertyConfigurator.configure(props);
282 		Thread.currentThread().setContextClassLoader(contextClassLoader);
283 		logger.debug("gboat2 bundle log apply  successful.");
284 
285 	}
286 
287 	/**
288 	 * 为页面设置默认级别
289 	 * TODO
290 	 */
291 	private void setDefaultRootLevel() {
292 		//业务日志
293 		try {
294 			Properties busiProps = loadProperties(getBusinessLogRealPath());
295 			String level = (String) busiProps.get(BUSINESS_LEVEL);
296 			if (StringUtils.isEmpty(level)) {
297 				setBusinessLevel("DEBUG");
298 			} else {
299 				setBusinessLevel(level.toUpperCase());
300 			}
301 		} catch (FileNotFoundException e) {
302 			LogLog.warn("  no business log level could be found for Gboat2. ");
303 			LogLog.warn("  businessLog.properties not found ,Gboat2 use default level DEBUG . ");
304 			LogLog.warn("  adding businessLog.properties in directory of WEB-INF/config  can change business log behavior . ");
305 			LogLog.warn("  the Content as follows:business.level=DEBUG;business.level=INFO;business.level=WARN;business.level=ERROR . ");
306 		} catch (IOException e) {
307 			LogLog.warn("  gboat2 use default level DEBUG  ");
308 		}
309 
310 		//系统日志
311 		try {
312 			Properties sysProps = loadProperties(getSystemLogRealPath());
313 			String level = (String) sysProps.get(LOG4J_ROOTLOGGER);
314 			if (StringUtils.isEmpty(level)) {
315 				setBusinessLevel("DEBUG");
316 			} else {
317 				String[] split = level.split(",");
318 				setSystemLogRootLevel(split[0].toUpperCase());
319 			}
320 		} catch (FileNotFoundException e) {
321 			LogLog.warn("  No appenders could be found for Gboat2. ");
322 			LogLog.warn("  Please initialize the log4j system properly. ");
323 			LogLog.warn("  Please add log4j.properties in the directory: WEB-INF/config. ");
324 		} catch (IOException e) {
325 			LogLog.warn("  log4j.properties  don't load. ");
326 		}
327 
328 	}
329 
330 	/**
331 	 * load屬性文件
332 	 * @param path 路径
333 	 * @return 读取文件内容
334 	 * @throws IOException io异常
335 	 */
336 	private Properties loadProperties(String path) throws IOException {
337 		Properties props = new Properties();
338 		FileInputStream logStream;
339 		logStream = new FileInputStream(path);
340 		props.load(logStream);
341 
342 		logStream.close();
343 		return props;
344 	}
345 
346 	/**
347 	 * load属性文件
348 	 * @param props 属性
349 	 * @param path  文件路径
350 	 * @return  文件内容
351 	 * @throws IOException store文件抛出异常
352 	 */
353 	private Properties storeProperties(Properties props, String path) throws IOException {
354 		FileOutputStream logOutStream;
355 		logOutStream = new FileOutputStream(path);
356 		props.store(logOutStream, "存储成功");
357 		logOutStream.close();
358 		return props;
359 	}
360 
361 	/**
362 	 * 读取文件
363 	 * @param fileName 文件名称
364 	 * @return 文件内容
365 	 */
366 	private String readFromFile(String fileName) {
367 		try {
368 			File file = new File(fileName);
369 			BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
370 			StringBuilder stringBuilder = new StringBuilder();
371 			int content;
372 			while ((content = bufferedReader.read()) != -1) {
373 				stringBuilder.append((char) content);
374 			}
375 
376 			bufferedReader.close();
377 
378 			return stringBuilder.toString();
379 		} catch (FileNotFoundException e) {
380 			// TODO Auto-generated catch block
381 			e.printStackTrace();
382 			return null;
383 		} catch (IOException e) {
384 			// TODO Auto-generated catch block
385 			e.printStackTrace();
386 			return null;
387 		}
388 	}
389 
390 	/**
391 	 * 写文件
392 	 * @param content 内容
393 	 * @throws IOException io异常
394 	 */
395 	private void writeToFile(String content) throws IOException {
396 		if (StringUtils.isEmpty(content))
397 			return;
398 		String path = WEB_APP_ROOT + SYS_LOG_DIRECTORY;
399 		File fileDir = new File(path);
400 		if (!fileDir.exists()) {
401 			boolean mkdirs = fileDir.mkdirs();
402 			if (!mkdirs) {
403 				throw new RuntimeException(" create directory  {" + path + "}  fail ");
404 			}
405 		} else if (!fileDir.isDirectory()) {
406 			throw new RuntimeException(path + " should be directory");
407 		}
408 		//创建日志文件
409 		File f = new File(fileDir, LOG4J_FILENAME);
410 
411 		//用FileOutputSteam包装文件,并设置文件可追加
412 		OutputStream out = null;
413 		try {
414 			out = new FileOutputStream(f);
415 			//这个地方在不同的系统下可能有问题
416 			//由于单独的修改根级别,所有智能按照行来操作
417 			String[] split = content.split(BR);
418 			if (split.length == 1) {
419 				String[] temp = content.split("\n");
420 				if (temp.length >= 1) {
421 					split = temp;
422 				}
423 			}
424 			if (split == null) {
425 				out.close();
426 				return;
427 			}
428 			for (int i = 0; i < split.length; i++) {
429 				String lineContent = split[i];
430 				if (!StringUtils.isEmpty(lineContent)) {
431 					if (lineContent.indexOf("log4j.rootLogger") >= 0) {
432 						if (!StringUtils.isEmpty(getSystemLogRootLevel())) {
433 							String afterEqual = lineContent.substring(lineContent.indexOf("=") + 1);
434 							String[] appenders = afterEqual.split(",");
435 							StringBuffer appenderStrings = new StringBuffer();
436 							if (appenders.length >= 2) {
437 								for (int j = 1; j < appenders.length; j++) {
438 									appenderStrings = appenderStrings.append(appenders[j]);
439 									if (j < appenders.length - 1) {
440 										appenderStrings = appenderStrings.append(",");
441 									}
442 								}
443 							}
444 							if ("".equals(appenderStrings.toString())) {
445 								lineContent = "log4j.rootLogger = " + getSystemLogRootLevel();
446 							} else
447 								lineContent = "log4j.rootLogger = " + getSystemLogRootLevel() + "," + appenderStrings;
448 						}
449 					}
450 				}
451 				out.write(lineContent.getBytes());
452 				out.write('\r'); // \r\n表示换行
453 				out.write('\n');
454 			}
455 
456 			out.close(); //关闭输出流
457 		} catch (FileNotFoundException e) {
458 			// TODO Auto-generated catch block
459 			e.printStackTrace();
460 		} catch (IOException e) {
461 			// TODO Auto-generated catch block
462 			e.printStackTrace();
463 		}
464 	}
465 
466 }