Version 1.7.4
[gae.git] / java / src / main / com / google / appengine / tools / development / LoggingConfigurationManager.java
blobbe33e2c5e70b04884e67e26d5157afd9e1719183
1 package com.google.appengine.tools.development;
3 import com.google.appengine.api.log.dev.LocalLogService;
4 import com.google.apphosting.api.ApiProxy;
5 import com.google.common.annotations.VisibleForTesting;
7 import java.io.BufferedInputStream;
8 import java.io.ByteArrayInputStream;
9 import java.io.ByteArrayOutputStream;
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.util.Map;
15 import java.util.Properties;
16 import java.util.logging.Handler;
17 import java.util.logging.Level;
18 import java.util.logging.LogManager;
19 import java.util.logging.Logger;
21 /**
22 * Manager for a web application's logging configurations.
23 * <p>
24 * When the web application is read from an EAR directory this supports
25 * combining the logging configurations from each contained WAR directory. In
26 * the case two configurations define the logging level for the same logger the
27 * more verbose level applies.
29 class LoggingConfigurationManager {
30 @VisibleForTesting
31 static final Logger LOGGER = Logger.getLogger(LoggingConfigurationManager.class.getName());
32 public static final String LOGGING_CONFIG_FILE = "java.util.logging.config.file";
33 private final Properties readProperties = new Properties();
35 /**
36 * Reads and remember the logging configuration for a single server. This
37 * reads up to two files.
38 * <ul>
40 * <li>
41 * The file specified by the {@link #LOGGING_CONFIG_FILE}
42 * property value from {@link #userSystemProperties} (from appengine-web.xml).
43 * This interprets the file name in up to 3 ways looking for a valid
44 * logging.properties file.
45 * <ol>
46 * <li> As an absolute file name.
47 * <li> As a relative file name qualified by {@link #warDir}.
48 * <li> As a relative file name qualified by {@link #externalResourceDir}.
49 * </ol>
50 * </li>
52 * <li>
53 * The file specified by the {@link #LOGGING_CONFIG_FILE} property value
54 * from {@link SystemProperties} (from {@link System#getProperties}.
55 * <li>The file specified by the {@link #LOGGING_CONFIG_FILE}
56 * property value from {@link #userSystemProperties} (from appengine-web.xml).
57 * This interprets the file name in up to 2 ways looking for a valid
58 * logging.properties file.
59 * <ol>
60 * <li> As an absolute file name.
61 * <li> As a relative file name qualified by {@link #warDir}.
62 * </ol>
63 * </li>
65 * </ul>
66 * When the same multiple configurations specify the level for the same
67 * logger, the more verbose level wins.
68 * @param systemProperties
69 * @param userSystemProperties
71 void read(Properties systemProperties, Map<String, String> userSystemProperties,
72 File warDir, File externalResourceDir) {
73 String userConfigFile = userSystemProperties.get(LOGGING_CONFIG_FILE);
75 boolean shouldLogWarning = (externalResourceDir == null);
76 Properties userProperties = loadPropertiesFile(userConfigFile, warDir, shouldLogWarning);
77 if (userProperties == null && externalResourceDir != null) {
78 userProperties = loadPropertiesFile(userConfigFile, externalResourceDir, true);
80 String sdkConfigFile = systemProperties.getProperty(LOGGING_CONFIG_FILE);
81 Properties sdkProperties = loadPropertiesFile(sdkConfigFile, warDir, true);
82 if (sdkProperties != null) {
83 mergeProperties(sdkProperties);
85 if (userProperties != null) {
86 mergeProperties(userProperties);
90 @VisibleForTesting
91 Properties getReadProperties() {
92 Properties result = new Properties();
93 result.putAll(readProperties);
94 return result;
97 /**
98 * Updates the JVM's logging configuration based on the combined already read
99 * logging configurations.
101 void updateLoggingConfiguration() {
102 ByteArrayOutputStream out = new ByteArrayOutputStream();
104 try {
105 readProperties.store(out, null);
106 LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray()));
108 Logger root = Logger.getLogger("");
110 ApiProxyLocal proxy = (ApiProxyLocal) ApiProxy.getDelegate();
111 LocalLogService logService = (LocalLogService) proxy.getService(LocalLogService.PACKAGE);
112 root.addHandler(logService.getLogHandler());
114 Handler[] handlers = root.getHandlers();
115 if (handlers != null) {
116 for (Handler handler : handlers) {
117 handler.setLevel(Level.FINEST);
120 } catch (IOException e) {
121 LOGGER.log(Level.WARNING, "Unable to configure logging properties.", e);
125 private void mergeProperties(Properties additional) {
126 for (Map.Entry<Object, Object> entry : additional.entrySet()){
127 if (!(entry.getKey() instanceof String)) {
128 continue;
130 String key = (String) entry.getKey();
131 if (!(entry.getValue() instanceof String)) {
132 continue;
134 String newValue = (String) entry.getValue();
135 String oldValue = readProperties.getProperty(key);
136 if (oldValue == null
137 || !key.endsWith(".level")
138 || compareLevels(newValue, oldValue) > 0) {
139 readProperties.setProperty(key, newValue);
144 @VisibleForTesting
145 int compareLevels(String levelName1, String levelName2) {
146 if (levelName1.equals(levelName2)) {
147 return 0;
149 Integer level1;
150 try {
151 level1 = Integer.valueOf(Level.parse(levelName1).intValue());
152 } catch (IllegalArgumentException iae) {
153 return -1;
155 Integer level2;
156 try {
157 level2 = Integer.valueOf(Level.parse(levelName2).intValue());
158 } catch (IllegalArgumentException iae) {
159 return 1;
161 return level2.compareTo(level1);
165 private static Properties loadPropertiesFile(String file, File appDir, boolean logWarning) {
166 if (file == null) {
167 return null;
169 file = file.replace('/', File.separatorChar);
170 File f = new File(file);
171 if (!f.isAbsolute()) {
172 f = new File(appDir + File.separator + f.getPath());
174 InputStream inputStream = null;
175 try {
176 inputStream = new BufferedInputStream(new FileInputStream(f));
177 Properties props = new Properties();
178 props.load(inputStream);
179 return props;
180 } catch (IOException e) {
181 if (logWarning) {
182 LOGGER.log(Level.WARNING, "Unable to load properties file, " + f.getAbsolutePath(), e);
184 return null;
185 } finally {
186 if (inputStream != null) {
187 try {
188 inputStream.close();
189 } catch (IOException e) {
190 LOGGER.log(Level.WARNING, "Stream close failure", e);