| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.fuseki.system; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.nio.file.Path; |
| |
| import org.apache.jena.atlas.io.IO; |
| import org.apache.jena.atlas.lib.Lib; |
| import org.apache.jena.atlas.logging.LogCtl; |
| import org.apache.jena.atlas.logging.LogCtlLog4j2; |
| import org.apache.jena.fuseki.Fuseki; |
| |
| /** |
| * FusekiLogging. |
| * <p> |
| * This applies to Fuseki run from the command line and embedded. |
| * <p> |
| * This does not apply to Fuseki running in Tomcat where it uses the |
| * servlet 3.0 mechanism described in |
| * <a href="https://logging.apache.org/log4j/2.x/manual/webapp.html">Log4j2 manual |
| * (webapp)</a>. See {@code FusekiServerEnvironmentInit}. |
| */ |
| public class FusekiLogging |
| { |
| // This class must not have static constants, or otherwise not "Fuseki.*" |
| // or any class else where that might kick off logging. Otherwise, the |
| // setLogging is pointless (it's already set). |
| |
| // Set logging. |
| // 1/ Use system property log4j2.configurationFile if defined. |
| // 2/ Use file:log4j2.properties if exists |
| // 3/ Use log4j2.properties on the classpath. |
| // 4/ Use org/apache/jena/fuseki/log4j2.properties on the classpath. |
| // 5/ Use built in string |
| |
| /** |
| * Places for the log4j properties file at (3). |
| * This is not the standard, fixed classpath names used by log4j. |
| * log4j2.properties, log4j2.yaml, log4j2.json, log4j2.xml |
| */ |
| private static final String[] resourcesForLog4jProperties = { |
| "log4j2.properties" |
| }; |
| |
| public static String envLogLoggingProperty = "FUSEKI_LOGLOGGING"; |
| public static String logLoggingProperty = "fuseki.logLogging"; |
| private static String logLoggingPropertyAlt = "fuseki.loglogging"; |
| |
| // This is also set every call of seLogging. |
| // That picks up any in-code settings of the logging properties. |
| private static boolean logLogging = getLogLogging(); |
| |
| private static final boolean getLogLogging() { |
| String x = System.getProperty(logLoggingPropertyAlt); |
| if ( x != null ) { |
| logLogging("Old system property used '%s'", logLoggingPropertyAlt); |
| return x.equalsIgnoreCase("true"); |
| } |
| x = Lib.getenv("FUSEKI_LOGLOGGING", logLoggingProperty); |
| return x != null && x.equalsIgnoreCase("true"); |
| } |
| |
| private static boolean loggingInitialized = false; |
| |
| /** |
| * Mark whether logging is considered "initialized". Some external factor (e.g. |
| * log4j2 webapp context-param "log4jConfiguration") may mean logging will be |
| * initialized some other way. |
| * <p> |
| * Call this with argument false if the code wants to re-initialize the logging |
| * otherwise calls of {@code setLogging} will be no-ops. |
| */ |
| public static synchronized void markInitialized(boolean isInitialized) { |
| logLogging("markInitialized("+isInitialized+")"); |
| loggingInitialized = isInitialized; |
| } |
| |
| /** Set up logging. */ |
| public static synchronized void setLogging() { |
| setLogging(null); |
| } |
| |
| public static final String log4j2_configurationFile = LogCtl.log4j2ConfigFileProperty; |
| private static final String log4j2_configurationFileLegacy = LogCtl.log4j2ConfigFilePropertyLegacy; |
| // Only used by the webapp. |
| public static final String log4j2_web_configuration = "log4jConfiguration"; |
| |
| public static synchronized boolean hasInitialized() { |
| return loggingInitialized; |
| } |
| |
| /** |
| * Set up logging. Allow an extra location. This may be null. |
| * |
| * @param extraDir |
| */ |
| public static synchronized void setLogging(Path extraDir) { |
| |
| // Cope with repeated calls so code can call this to ensure |
| if ( loggingInitialized ) |
| return; |
| loggingInitialized = true; |
| |
| logLogging = getLogLogging(); |
| logLogging("Set logging"); |
| |
| // Is there a log4j setup provided? |
| if ( checkSystemProperties(log4j2_configurationFile) || |
| checkSystemProperties(log4j2_configurationFileLegacy) || |
| System.getenv("LOG4J_CONFIGURATION_FILE") != null ) |
| { |
| logLogging("External log4j2 setup "); |
| return ; |
| } |
| |
| logLogging("Setup"); |
| // Look for a log4j2.properties file in the current working directory |
| // and a place (e.g. FUSEKI_BASE in the webapp/full server) for easy customization. |
| String fn1 = "log4j2.properties"; |
| String fn2 = null; |
| |
| if ( extraDir != null ) |
| fn2 = extraDir.resolve("log4j2.properties").toString(); |
| if ( attempt(fn1) ) return; |
| if ( attempt(fn2) ) return; |
| |
| // Try classpath |
| for ( String resourceName : resourcesForLog4jProperties ) { |
| // In log4j2, classpath initialization is fixed name : |
| // log4j2.properties, log4j2.yaml, log4j2.json, log4j2.xml |
| // Instead, we manually load a resource. |
| logLogging("Try classpath %s", resourceName); |
| URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName); |
| // if ( url != null ) { |
| // // Problem - test classes can be on the classpath (development mainly). |
| // if ( url.toString().contains("-tests.jar") || url.toString().contains("test-classes") ) |
| // url = null; |
| // } |
| |
| if ( url != null ) { |
| try ( InputStream inputStream = url.openStream() ) { |
| String x = IO.readWholeFileAsUTF8(inputStream); |
| } catch (IOException ex) { IO.exception(ex); } |
| |
| try ( InputStream inputStream = url.openStream() ) { |
| loadConfiguration(inputStream, resourceName); |
| } catch (IOException ex) { IO.exception(ex); } |
| logLogging("Found via classpath %s", url); |
| System.setProperty(log4j2_configurationFile, url.toString()); |
| return; |
| } |
| } |
| // Use built-in. |
| logLogging("Fallback built-in log4j2setup"); |
| String dftLog4j = log4j2setupFallback(); |
| resetLogging(dftLog4j); |
| // Stop anything attempting to do it again. |
| System.setProperty(log4j2_configurationFile, "set"); |
| } |
| |
| private static boolean checkSystemProperties(String... properties) { |
| String x = null; |
| for ( String propertyName : properties ) { |
| x = System.getProperty(propertyName, null); |
| if ( x != null ) { |
| if ( "set".equals(x) ) { |
| Fuseki.serverLog.warn("Fuseki logging: Unexpected: Log4j2 was setup by some other part of Jena"); |
| return true; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static void loadConfiguration(InputStream inputStream, String resourceName) throws IOException { |
| LogCtlLog4j2.resetLogging(inputStream, resourceName); |
| } |
| |
| private static boolean attempt(String fn) { |
| if ( fn == null ) |
| return false; |
| try { |
| logLogging("Try file:"+fn); |
| File f = new File(fn); |
| if ( f.exists() ) { |
| logLogging("Found via file "+fn); |
| try (InputStream input = new FileInputStream(fn) ) { |
| loadConfiguration(input, fn); |
| } catch (IOException ex) { IO.exception(ex); } |
| System.setProperty(log4j2_configurationFile, "file:" + fn); |
| return true; |
| } |
| } |
| catch (Throwable th) {} |
| return false; |
| } |
| |
| private static void logLogging(String fmt, Object ... args) { |
| if ( logLogging ) { |
| System.err.print("Fuseki Logging: "); |
| System.err.printf(fmt, args); |
| System.err.println(); |
| } |
| } |
| |
| private static String log4j2setupFallback() { |
| // The logging file for Fuseki in Tomcat webapp is in "log4j2.properties" in the webapp root directory. |
| // This is used by command line Fuseki (full and main) |
| |
| // filters = threshold |
| // filter.threshold.type = ThresholdFilter |
| // filter.threshold.level = ALL |
| |
| return """ |
| ## Plain output to stdout |
| status = error |
| name = FusekiLogging |
| appender.console.type = Console |
| appender.console.name = OUT |
| appender.console.target = SYSTEM_OUT |
| appender.console.layout.type = PatternLayout |
| appender.console.layout.pattern = [%d{yyyy-MM-dd HH:mm:ss}] %-10c{1} %-5p %m%n |
| |
| rootLogger.level = WARN |
| rootLogger.appenderRef.stdout.ref = OUT |
| |
| logger.jena.name = org.apache.jena |
| logger.jena.level = INFO |
| |
| logger.arq-exec.name = org.apache.jena.arq.exec |
| logger.arq-exec.level = INFO |
| |
| logger.riot.name = org.apache.jena.riot |
| logger.riot.level = INFO |
| |
| logger.fuseki.name = org.apache.jena.fuseki |
| logger.fuseki.level = INFO |
| |
| logger.fuseki-fuseki.name = org.apache.jena.fuseki.Fuseki |
| logger.fuseki-fuseki.level = INFO |
| |
| logger.fuseki-server.name = org.apache.jena.fuseki.Server |
| logger.fuseki-server.level = INFO |
| |
| logger.fuseki-config.name = org.apache.jena.fuseki.Config |
| logger.fuseki-config.level = INFO |
| |
| logger.fuseki-admin.name = org.apache.jena.fuseki.Admin |
| logger.fuseki-admin.level = INFO |
| |
| logger.jetty.name = org.eclipse.jetty |
| logger.jetty.level = WARN |
| |
| logger.apache-http.name = org.apache.http |
| logger.apache-http.level = WARN |
| logger.shiro.name = org.apache.shiro |
| logger.shiro.level = WARN |
| |
| # Hide bug in Shiro 1.5.0 |
| logger.shiro-realm.name = org.apache.shiro.realm.text.IniRealm |
| logger.shiro-realm.level = ERROR |
| |
| # This goes out in NCSA format |
| appender.plain.type = Console |
| appender.plain.name = PLAIN |
| appender.plain.layout.type = PatternLayout |
| appender.plain.layout.pattern = %m%n |
| |
| logger.fuseki-request.name = org.apache.jena.fuseki.Request |
| logger.fuseki-request.additivity = false |
| logger.fuseki-request.level = OFF |
| logger.fuseki-request.appenderRef.plain.ref = PLAIN |
| ); |
| """; |
| } |
| |
| public static void resetLogging(String configString) { |
| LogCtlLog4j2.resetLogging(configString); |
| } |
| } |
| |