001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.main; 029 030import java.io.File; 031import java.io.IOException; 032import java.io.InterruptedIOException; 033import java.io.LineNumberReader; 034import java.io.PrintWriter; 035import java.io.StringReader; 036import java.io.StringWriter; 037import java.net.URL; 038import java.net.URLDecoder; 039import java.nio.charset.Charset; 040import java.util.ArrayList; 041 042import org.apache.commons.logging.Log; 043import org.apache.commons.logging.LogFactory; 044import org.apache.logging.log4j.core.config.ConfigurationSource; 045import org.apache.logging.log4j.core.config.Configurator; 046import org.apache.logging.log4j.core.util.Loader; 047 048/** 049 * Provides the OpenCms logging mechanism.<p> 050 * 051 * The OpenCms logging mechanism is based on Apache Commons Logging. 052 * However, log4j is shipped with OpenCms and assumed to be used as default logging mechanism. 053 * Since apparently Commons Logging may cause issues in more complex classloader scenarios, 054 * we may switch the logging interface to log4j <code>UGLI</code> once the final release is available.<p> 055 * 056 * The log4j configuration file shipped with OpenCms is located 057 * in <code>${opencms.WEB-INF}/classes/log4j2.xml</code>. OpenCms will auto-configure itself 058 * to write it's log file to <code>${opencms.WEB-INF}/logs/opencms.log</code>. This default behaviour 059 * can be supressed by either using a log4j configuration file from another location, or by setting the 060 * special property <code>${opencms.set.logfile}</code> in the log4j configuration file to <code>false</code>. 061 * 062 * @since 6.0.0 063 */ 064public final class CmsLog { 065 066 /** The name of the opencms.log file. */ 067 public static final String FILE_LOG = "opencms.log"; 068 069 /** Path to the "logs" folder relative to the "WEB-INF" directory of the application. */ 070 public static final String FOLDER_LOGS = "logs" + File.separatorChar; 071 072 /** Log for initialization messages. */ 073 public static Log INIT; 074 075 /** The absolute path to the folder of the main OpenCms log file (in the "real" file system). */ 076 private static String m_logFileRfsFolder; 077 078 /** The absolute path to the OpenCms log file (in the "real" file system). */ 079 private static String m_logFileRfsPath; 080 081 /** 082 * Initializes the OpenCms logger configuration.<p> 083 */ 084 static { 085 // 086 // DO NOT USE ANY OPENCMS CLASSES THAT USE STATIC LOGGER INSTANCES IN THIS STATIC BLOCK 087 // OTHERWISE THEIR LOGGER WOULD NOT BE INITIALIZED PROPERLY 088 // 089 try { 090 // look for the log4j2.xml that shipped with OpenCms 091 URL url = Loader.getResource("log4j2.xml", null); 092 if (url != null) { 093 // found some log4j properties, let's see if these are the ones used by OpenCms 094 File log4jProps = new File(URLDecoder.decode(url.getPath(), Charset.defaultCharset().name())); 095 String path = log4jProps.getAbsolutePath(); 096 // in a default OpenCms configuration, the following path would point to the OpenCms "WEB-INF" folder 097 String webInfPath = log4jProps.getParent(); 098 webInfPath = webInfPath.substring(0, webInfPath.lastIndexOf(File.separatorChar) + 1); 099 100 // check for the OpenCms configuration file 101 // check for the OpenCms tld file 102 String tldFilePath = webInfPath + CmsSystemInfo.FILE_TLD; 103 File tldFile = new File(tldFilePath); 104 if (tldFile.exists()) { 105 // check if OpenCms should set the log file environment variable 106 boolean setLogFile = Boolean.parseBoolean(System.getProperty("opencms.set.logfile", "true")); 107 if (setLogFile) { 108 // set "opencms.log" variable 109 String logFilePath = webInfPath + FOLDER_LOGS + FILE_LOG; 110 File logFile = new File(logFilePath); 111 m_logFileRfsPath = logFile.getAbsolutePath(); 112 m_logFileRfsFolder = logFile.getParent() + File.separatorChar; 113 System.setProperty("opencms.logfile", m_logFileRfsPath); 114 System.setProperty("opencms.logfolder", m_logFileRfsFolder); 115 // re-read the configuration with the new environment variable available 116 ConfigurationSource source = ConfigurationSource.fromUri( 117 Loader.getResource("log4j2.xml", null).toURI()); 118 Configurator.initialize(null, source); 119 } 120 } 121 // can't localize this message since this would end in an endless logger init loop 122 INIT = LogFactory.getLog("org.opencms.init"); 123 INIT.info(". Log4j config file : " + path); 124 } 125 } catch (SecurityException e) { 126 // may be caused if environment can't be written 127 e.printStackTrace(System.err); 128 } catch (Exception e) { 129 // unexpected but nothing we can do about it, print stack trace and continue 130 e.printStackTrace(System.err); 131 } 132 } 133 134 /** 135 * Hides the public constructor.<p> 136 */ 137 private CmsLog() { 138 139 // hides the public constructor 140 } 141 142 /** 143 * Returns the log for the selected object.<p> 144 * 145 * If the provided object is a String, this String will 146 * be used as channel name. Otherwise the objects 147 * class name will be used as channel name.<p> 148 * 149 * @param obj the object channel to use 150 * @return the log for the selected object channel 151 */ 152 public static Log getLog(Object obj) { 153 154 if (obj instanceof String) { 155 return LogFactory.getLog((String)obj); 156 } else if (obj instanceof Class<?>) { 157 return LogFactory.getLog((Class<?>)obj); 158 } else { 159 return LogFactory.getLog(obj.getClass()); 160 } 161 } 162 163 /** 164 * Render throwable using Throwable.printStackTrace. 165 * <p>This code copy from "org.apache.log4j.DefaultThrowableRenderer.render(Throwable throwable)"</p> 166 * @param throwable throwable, may not be null. 167 * @return string representation. 168 */ 169 public static String[] render(final Throwable throwable) { 170 171 StringWriter sw = new StringWriter(); 172 PrintWriter pw = new PrintWriter(sw); 173 try { 174 throwable.printStackTrace(pw); 175 } catch (RuntimeException ex) { 176 // nothing to do 177 } 178 pw.flush(); 179 LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString())); 180 ArrayList<String> lines = new ArrayList<>(); 181 try { 182 String line = reader.readLine(); 183 while (line != null) { 184 lines.add(line); 185 line = reader.readLine(); 186 } 187 } catch (IOException ex) { 188 if (ex instanceof InterruptedIOException) { 189 Thread.currentThread().interrupt(); 190 } 191 lines.add(ex.toString()); 192 } 193 //String[] tempRep = new String[lines.size()]; 194 return lines.toArray(new String[0]); 195 } 196 197 /** 198 * Returns the filename of the log file (in the "real" file system).<p> 199 * 200 * If the method returns <code>null</code>, this means that the log 201 * file is not managed by OpenCms.<p> 202 * 203 * @return the filename of the log file (in the "real" file system) 204 */ 205 protected static String getLogFileRfsPath() { 206 207 return m_logFileRfsPath; 208 } 209}