001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.util.log4j;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
018    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019    import com.liferay.portal.kernel.log.LogFactory;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
022    import com.liferay.portal.kernel.util.PropsKeys;
023    import com.liferay.portal.kernel.util.PropsUtil;
024    import com.liferay.portal.kernel.util.ReflectionUtil;
025    import com.liferay.portal.kernel.util.ServerDetector;
026    import com.liferay.portal.kernel.util.StreamUtil;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    
030    import java.io.IOException;
031    import java.io.InputStream;
032    
033    import java.lang.reflect.Field;
034    
035    import java.net.URL;
036    
037    import java.util.Enumeration;
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.concurrent.ConcurrentHashMap;
042    
043    import org.apache.log4j.Level;
044    import org.apache.log4j.LogManager;
045    import org.apache.log4j.Logger;
046    import org.apache.log4j.xml.DOMConfigurator;
047    
048    import org.dom4j.Document;
049    import org.dom4j.Element;
050    import org.dom4j.io.SAXReader;
051    
052    import org.xml.sax.EntityResolver;
053    import org.xml.sax.InputSource;
054    
055    /**
056     * @author Brian Wing Shun Chan
057     * @author Tomas Polesovsky
058     */
059    public class Log4JUtil {
060    
061            public static void configureLog4J(ClassLoader classLoader) {
062                    configureLog4J(classLoader.getResource("META-INF/portal-log4j.xml"));
063    
064                    try {
065                            Enumeration<URL> enu = classLoader.getResources(
066                                    "META-INF/portal-log4j-ext.xml");
067    
068                            while (enu.hasMoreElements()) {
069                                    configureLog4J(enu.nextElement());
070                            }
071                    }
072                    catch (IOException ioe) {
073                            java.util.logging.Logger logger =
074                                    java.util.logging.Logger.getLogger(Log4JUtil.class.getName());
075    
076                            logger.log(
077                                    java.util.logging.Level.WARNING,
078                                    "Unable to load portal-log4j-ext.xml", ioe);
079                    }
080            }
081    
082            public static void configureLog4J(URL url) {
083                    if (url == null) {
084                            return;
085                    }
086    
087                    String urlContent = _getURLContent(url);
088    
089                    if (urlContent == null) {
090                            return;
091                    }
092    
093                    // See LPS-6029, LPS-8865, and LPS-24280
094    
095                    DOMConfigurator domConfigurator = new DOMConfigurator();
096    
097                    domConfigurator.doConfigure(
098                            new UnsyncStringReader(urlContent),
099                            LogManager.getLoggerRepository());
100    
101                    try {
102                            SAXReader saxReader = new SAXReader();
103    
104                            saxReader.setEntityResolver(
105                                    new EntityResolver() {
106    
107                                            @Override
108                                            public InputSource resolveEntity(
109                                                    String publicId, String systemId) {
110    
111                                                    if (systemId.endsWith("log4j.dtd")) {
112                                                            return new InputSource(
113                                                                    DOMConfigurator.class.getResourceAsStream(
114                                                                            "log4j.dtd"));
115                                                    }
116    
117                                                    return null;
118                                            }
119    
120                                    });
121    
122                            Document document = saxReader.read(
123                                    new UnsyncStringReader(urlContent), url.toExternalForm());
124    
125                            Element rootElement = document.getRootElement();
126    
127                            List<Element> categoryElements = rootElement.elements("category");
128    
129                            for (Element categoryElement : categoryElements) {
130                                    String name = categoryElement.attributeValue("name");
131    
132                                    Element priorityElement = categoryElement.element("priority");
133    
134                                    String priority = priorityElement.attributeValue("value");
135    
136                                    java.util.logging.Logger jdkLogger =
137                                            java.util.logging.Logger.getLogger(name);
138    
139                                    jdkLogger.setLevel(_getJdkLevel(priority));
140                            }
141                    }
142                    catch (Exception e) {
143                            _logger.error(e, e);
144                    }
145            }
146    
147            public static Map<String, String> getCustomLogSettings() {
148                    return new HashMap<>(_getCustomLogSettings());
149            }
150    
151            public static String getOriginalLevel(String className) {
152                    Level level = Level.ALL;
153    
154                    Enumeration<Logger> enu = LogManager.getCurrentLoggers();
155    
156                    while (enu.hasMoreElements()) {
157                            Logger logger = enu.nextElement();
158    
159                            if (className.equals(logger.getName())) {
160                                    level = logger.getLevel();
161    
162                                    break;
163                            }
164                    }
165    
166                    return level.toString();
167            }
168    
169            public static void initLog4J(
170                    String serverId, String liferayHome, ClassLoader classLoader,
171                    LogFactory logFactory, Map<String, String> customLogSettings) {
172    
173                    ServerDetector.init(serverId);
174    
175                    _liferayHome = liferayHome;
176    
177                    configureLog4J(classLoader);
178    
179                    try {
180                            LogFactoryUtil.setLogFactory(logFactory);
181                    }
182                    catch (Exception e) {
183                            _logger.error(e, e);
184                    }
185    
186                    for (String name : customLogSettings.keySet()) {
187                            String priority = customLogSettings.get(name);
188    
189                            setLevel(name, priority, false);
190                    }
191            }
192    
193            public static void setLevel(String name, String priority, boolean custom) {
194                    Logger logger = Logger.getLogger(name);
195    
196                    logger.setLevel(Level.toLevel(priority));
197    
198                    java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger(
199                            name);
200    
201                    jdkLogger.setLevel(_getJdkLevel(priority));
202    
203                    if (custom) {
204                            Map<String, String> customLogSettings = _getCustomLogSettings();
205    
206                            customLogSettings.put(name, priority);
207                    }
208            }
209    
210            /**
211             * @see com.liferay.portal.util.FileImpl#getBytes(InputStream, int, boolean)
212             */
213            private static byte[] _getBytes(InputStream inputStream)
214                    throws IOException {
215    
216                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
217                            new UnsyncByteArrayOutputStream();
218    
219                    StreamUtil.transfer(inputStream, unsyncByteArrayOutputStream, -1, true);
220    
221                    return unsyncByteArrayOutputStream.toByteArray();
222            }
223    
224            private static Map<String, String> _getCustomLogSettings() {
225                    ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
226    
227                    if (Log4JUtil.class.getClassLoader() == classLoader) {
228                            return _customLogSettings;
229                    }
230    
231                    try {
232                            Class<?> clazz = classLoader.loadClass(Log4JUtil.class.getName());
233    
234                            Field field = ReflectionUtil.getDeclaredField(
235                                    clazz, "_customLogSettings");
236    
237                            return (Map<String, String>)field.get(null);
238                    }
239                    catch (Exception e) {
240                            return ReflectionUtil.throwException(e);
241                    }
242            }
243    
244            private static java.util.logging.Level _getJdkLevel(String priority) {
245                    if (StringUtil.equalsIgnoreCase(priority, Level.DEBUG.toString())) {
246                            return java.util.logging.Level.FINE;
247                    }
248                    else if (StringUtil.equalsIgnoreCase(
249                                            priority, Level.ERROR.toString())) {
250    
251                            return java.util.logging.Level.SEVERE;
252                    }
253                    else if (StringUtil.equalsIgnoreCase(priority, Level.WARN.toString())) {
254                            return java.util.logging.Level.WARNING;
255                    }
256                    else {
257                            return java.util.logging.Level.INFO;
258                    }
259            }
260    
261            private static String _getLiferayHome() {
262                    if (_liferayHome == null) {
263                            _liferayHome = PropsUtil.get(PropsKeys.LIFERAY_HOME);
264                    }
265    
266                    return _liferayHome;
267            }
268    
269            private static String _getURLContent(URL url) {
270                    Map<String, String> variables = new HashMap<>();
271    
272                    variables.put("@liferay.home@", _getLiferayHome());
273    
274                    String spiId = System.getProperty("spi.id");
275    
276                    if (spiId == null) {
277                            spiId = StringPool.BLANK;
278                    }
279    
280                    variables.put("@spi.id@", spiId);
281    
282                    String urlContent = null;
283    
284                    InputStream inputStream = null;
285    
286                    try {
287                            inputStream = url.openStream();
288    
289                            byte[] bytes = _getBytes(inputStream);
290    
291                            urlContent = new String(bytes, StringPool.UTF8);
292                    }
293                    catch (Exception e) {
294                            _logger.error(e, e);
295    
296                            return null;
297                    }
298                    finally {
299                            StreamUtil.cleanUp(inputStream);
300                    }
301    
302                    for (Map.Entry<String, String> variable : variables.entrySet()) {
303                            urlContent = StringUtil.replace(
304                                    urlContent, variable.getKey(), variable.getValue());
305                    }
306    
307                    if (ServerDetector.getServerId() != null) {
308                            return urlContent;
309                    }
310    
311                    urlContent = _removeAppender(urlContent, "TEXT_FILE");
312    
313                    return _removeAppender(urlContent, "XML_FILE");
314            }
315    
316            private static String _removeAppender(String content, String appenderName) {
317                    int x = content.indexOf("<appender name=\"" + appenderName + "\"");
318    
319                    int y = content.indexOf("</appender>", x);
320    
321                    if (y != -1) {
322                            y = content.indexOf("<", y + 1);
323                    }
324    
325                    if ((x != -1) && (y != -1)) {
326                            content = content.substring(0, x) + content.substring(y);
327                    }
328    
329                    return StringUtil.replace(
330                            content, "<appender-ref ref=\"" + appenderName + "\" />",
331                            StringPool.BLANK);
332            }
333    
334            private static final Logger _logger = Logger.getRootLogger();
335    
336            private static final Map<String, String> _customLogSettings =
337                    new ConcurrentHashMap<>();
338            private static String _liferayHome;
339    
340    }