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
;
11 import java
.io
.FileInputStream
;
12 import java
.io
.IOException
;
13 import java
.io
.InputStream
;
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
;
22 * Manager for a web application's logging configurations.
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
{
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();
36 * Reads and remember the logging configuration for a single server. This
37 * reads up to two files.
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.
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}.
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.
60 * <li> As an absolute file name.
61 * <li> As a relative file name qualified by {@link #warDir}.
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
);
91 Properties
getReadProperties() {
92 Properties result
= new Properties();
93 result
.putAll(readProperties
);
98 * Updates the JVM's logging configuration based on the combined already read
99 * logging configurations.
101 void updateLoggingConfiguration() {
102 ByteArrayOutputStream out
= new ByteArrayOutputStream();
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
)) {
130 String key
= (String
) entry
.getKey();
131 if (!(entry
.getValue() instanceof String
)) {
134 String newValue
= (String
) entry
.getValue();
135 String oldValue
= readProperties
.getProperty(key
);
137 || !key
.endsWith(".level")
138 || compareLevels(newValue
, oldValue
) > 0) {
139 readProperties
.setProperty(key
, newValue
);
145 int compareLevels(String levelName1
, String levelName2
) {
146 if (levelName1
.equals(levelName2
)) {
151 level1
= Integer
.valueOf(Level
.parse(levelName1
).intValue());
152 } catch (IllegalArgumentException iae
) {
157 level2
= Integer
.valueOf(Level
.parse(levelName2
).intValue());
158 } catch (IllegalArgumentException iae
) {
161 return level2
.compareTo(level1
);
165 private static Properties
loadPropertiesFile(String file
, File appDir
, boolean logWarning
) {
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;
176 inputStream
= new BufferedInputStream(new FileInputStream(f
));
177 Properties props
= new Properties();
178 props
.load(inputStream
);
180 } catch (IOException e
) {
182 LOGGER
.log(Level
.WARNING
, "Unable to load properties file, " + f
.getAbsolutePath(), e
);
186 if (inputStream
!= null) {
189 } catch (IOException e
) {
190 LOGGER
.log(Level
.WARNING
, "Stream close failure", e
);