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     * @see com.liferay.petra.log4j.Log4JUtil
059     */
060    public class Log4JUtil {
061    
062            public static void configureLog4J(ClassLoader classLoader) {
063                    configureLog4J(classLoader.getResource("META-INF/portal-log4j.xml"));
064    
065                    try {
066                            Enumeration<URL> enu = classLoader.getResources(
067                                    "META-INF/portal-log4j-ext.xml");
068    
069                            while (enu.hasMoreElements()) {
070                                    configureLog4J(enu.nextElement());
071                            }
072                    }
073                    catch (IOException ioe) {
074                            java.util.logging.Logger logger =
075                                    java.util.logging.Logger.getLogger(Log4JUtil.class.getName());
076    
077                            logger.log(
078                                    java.util.logging.Level.WARNING,
079                                    "Unable to load portal-log4j-ext.xml", ioe);
080                    }
081            }
082    
083            public static void configureLog4J(URL url) {
084                    if (url == null) {
085                            return;
086                    }
087    
088                    String urlContent = _getURLContent(url);
089    
090                    if (urlContent == null) {
091                            return;
092                    }
093    
094                    // See LPS-6029, LPS-8865, and LPS-24280
095    
096                    DOMConfigurator domConfigurator = new DOMConfigurator();
097    
098                    domConfigurator.doConfigure(
099                            new UnsyncStringReader(urlContent),
100                            LogManager.getLoggerRepository());
101    
102                    try {
103                            SAXReader saxReader = new SAXReader();
104    
105                            saxReader.setEntityResolver(
106                                    new EntityResolver() {
107    
108                                            @Override
109                                            public InputSource resolveEntity(
110                                                    String publicId, String systemId) {
111    
112                                                    if (systemId.endsWith("log4j.dtd")) {
113                                                            return new InputSource(
114                                                                    DOMConfigurator.class.getResourceAsStream(
115                                                                            "log4j.dtd"));
116                                                    }
117    
118                                                    return null;
119                                            }
120    
121                                    });
122    
123                            Document document = saxReader.read(
124                                    new UnsyncStringReader(urlContent), url.toExternalForm());
125    
126                            Element rootElement = document.getRootElement();
127    
128                            List<Element> categoryElements = rootElement.elements("category");
129    
130                            for (Element categoryElement : categoryElements) {
131                                    String name = categoryElement.attributeValue("name");
132    
133                                    Element priorityElement = categoryElement.element("priority");
134    
135                                    String priority = priorityElement.attributeValue("value");
136    
137                                    java.util.logging.Logger jdkLogger =
138                                            java.util.logging.Logger.getLogger(name);
139    
140                                    jdkLogger.setLevel(_getJdkLevel(priority));
141                            }
142                    }
143                    catch (Exception e) {
144                            _logger.error(e, e);
145                    }
146            }
147    
148            public static Map<String, String> getCustomLogSettings() {
149                    return new HashMap<>(_getCustomLogSettings());
150            }
151    
152            public static String getOriginalLevel(String className) {
153                    Level level = Level.ALL;
154    
155                    Enumeration<Logger> enu = LogManager.getCurrentLoggers();
156    
157                    while (enu.hasMoreElements()) {
158                            Logger logger = enu.nextElement();
159    
160                            if (className.equals(logger.getName())) {
161                                    level = logger.getLevel();
162    
163                                    break;
164                            }
165                    }
166    
167                    return level.toString();
168            }
169    
170            public static void initLog4J(
171                    String serverId, String liferayHome, ClassLoader classLoader,
172                    LogFactory logFactory, Map<String, String> customLogSettings) {
173    
174                    ServerDetector.init(serverId);
175    
176                    _liferayHome = liferayHome;
177    
178                    configureLog4J(classLoader);
179    
180                    try {
181                            LogFactoryUtil.setLogFactory(logFactory);
182                    }
183                    catch (Exception e) {
184                            _logger.error(e, e);
185                    }
186    
187                    for (Map.Entry<String, String> entry : customLogSettings.entrySet()) {
188                            setLevel(entry.getKey(), entry.getValue(), false);
189                    }
190            }
191    
192            public static void setLevel(String name, String priority, boolean custom) {
193                    Logger logger = Logger.getLogger(name);
194    
195                    logger.setLevel(Level.toLevel(priority));
196    
197                    java.util.logging.Logger jdkLogger = java.util.logging.Logger.getLogger(
198                            name);
199    
200                    jdkLogger.setLevel(_getJdkLevel(priority));
201    
202                    if (custom) {
203                            Map<String, String> customLogSettings = _getCustomLogSettings();
204    
205                            customLogSettings.put(name, priority);
206                    }
207            }
208    
209            /**
210             * @see com.liferay.portal.util.FileImpl#getBytes(InputStream, int, boolean)
211             */
212            private static byte[] _getBytes(InputStream inputStream)
213                    throws IOException {
214    
215                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
216                            new UnsyncByteArrayOutputStream();
217    
218                    StreamUtil.transfer(inputStream, unsyncByteArrayOutputStream, -1, true);
219    
220                    return unsyncByteArrayOutputStream.toByteArray();
221            }
222    
223            private static Map<String, String> _getCustomLogSettings() {
224                    ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
225    
226                    if (Log4JUtil.class.getClassLoader() == classLoader) {
227                            return _customLogSettings;
228                    }
229    
230                    try {
231                            Class<?> clazz = classLoader.loadClass(Log4JUtil.class.getName());
232    
233                            Field field = ReflectionUtil.getDeclaredField(
234                                    clazz, "_customLogSettings");
235    
236                            return (Map<String, String>)field.get(null);
237                    }
238                    catch (Exception e) {
239                            return ReflectionUtil.throwException(e);
240                    }
241            }
242    
243            private static java.util.logging.Level _getJdkLevel(String priority) {
244                    if (StringUtil.equalsIgnoreCase(priority, Level.DEBUG.toString())) {
245                            return java.util.logging.Level.FINE;
246                    }
247                    else if (StringUtil.equalsIgnoreCase(
248                                            priority, Level.ERROR.toString())) {
249    
250                            return java.util.logging.Level.SEVERE;
251                    }
252                    else if (StringUtil.equalsIgnoreCase(priority, Level.WARN.toString())) {
253                            return java.util.logging.Level.WARNING;
254                    }
255                    else {
256                            return java.util.logging.Level.INFO;
257                    }
258            }
259    
260            private static String _getLiferayHome() {
261                    if (_liferayHome == null) {
262                            _liferayHome = PropsUtil.get(PropsKeys.LIFERAY_HOME);
263                    }
264    
265                    return _liferayHome;
266            }
267    
268            private static String _getURLContent(URL url) {
269                    Map<String, String> variables = new HashMap<>();
270    
271                    variables.put("@liferay.home@", _getLiferayHome());
272    
273                    String spiId = System.getProperty("spi.id");
274    
275                    if (spiId == null) {
276                            spiId = StringPool.BLANK;
277                    }
278    
279                    variables.put("@spi.id@", spiId);
280    
281                    String urlContent = null;
282    
283                    InputStream inputStream = null;
284    
285                    try {
286                            inputStream = url.openStream();
287    
288                            byte[] bytes = _getBytes(inputStream);
289    
290                            urlContent = new String(bytes, StringPool.UTF8);
291                    }
292                    catch (Exception e) {
293                            _logger.error(e, e);
294    
295                            return null;
296                    }
297                    finally {
298                            StreamUtil.cleanUp(inputStream);
299                    }
300    
301                    for (Map.Entry<String, String> variable : variables.entrySet()) {
302                            urlContent = StringUtil.replace(
303                                    urlContent, variable.getKey(), variable.getValue());
304                    }
305    
306                    if (ServerDetector.getServerId() != null) {
307                            return urlContent;
308                    }
309    
310                    urlContent = _removeAppender(urlContent, "TEXT_FILE");
311    
312                    return _removeAppender(urlContent, "XML_FILE");
313            }
314    
315            private static String _removeAppender(String content, String appenderName) {
316                    int x = content.indexOf("<appender name=\"" + appenderName + "\"");
317    
318                    int y = content.indexOf("</appender>", x);
319    
320                    if (y != -1) {
321                            y = content.indexOf("<", y + 1);
322                    }
323    
324                    if ((x != -1) && (y != -1)) {
325                            content = content.substring(0, x) + content.substring(y);
326                    }
327    
328                    return StringUtil.replace(
329                            content, "<appender-ref ref=\"" + appenderName + "\" />",
330                            StringPool.BLANK);
331            }
332    
333            private static final Logger _logger = Logger.getRootLogger();
334    
335            private static final Map<String, String> _customLogSettings =
336                    new ConcurrentHashMap<>();
337            private static String _liferayHome;
338    
339    }