blob: 2eb33f3a6d866bb0ebece484970677d2e0af043a [file] [log] [blame]
// 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.impala.util;
import java.util.Properties;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.impala.common.InternalException;
import org.apache.impala.common.ImpalaException;
import org.apache.impala.common.JniUtil;
import org.apache.impala.service.BackendConfig;
import org.apache.impala.thrift.TGetJavaLogLevelParams;
import org.apache.impala.thrift.TSetJavaLogLevelParams;
import org.apache.impala.thrift.TLogLevel;
import org.apache.thrift.protocol.TBinaryProtocol;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
/**
* log4j appender which calls into C++ code to log messages at their correct severities
* via glog.
*/
public class GlogAppender extends AppenderSkeleton {
private final static TBinaryProtocol.Factory protocolFactory_ =
new TBinaryProtocol.Factory();
// GLOG takes care of formatting, so we don't require a layout
public boolean requiresLayout() { return false; }
// Required implementation by superclass.
public void ActivateOptions() { }
// Required implementation by superclass
public void close() { }
private TLogLevel levelToSeverity(Level level) {
Preconditions.checkState(!level.equals(Level.OFF));
// TODO: Level does not work well in a HashMap or switch statement due to some
// strangeness with equality testing.
if (level.equals(Level.TRACE)) return TLogLevel.VLOG_3;
if (level.equals(Level.ALL)) return TLogLevel.VLOG_3;
if (level.equals(Level.DEBUG)) return TLogLevel.VLOG;
if (level.equals(Level.ERROR)) return TLogLevel.ERROR;
if (level.equals(Level.FATAL)) return TLogLevel.FATAL;
if (level.equals(Level.INFO)) return TLogLevel.INFO;
if (level.equals(Level.WARN)) return TLogLevel.WARN;
throw new IllegalStateException("Unknown log level: " + level.toString());
}
@Override
public void append(LoggingEvent event) {
Level level = event.getLevel();
if (level.equals(Level.OFF)) return;
String msg = event.getRenderedMessage();
if (event.getThrowableInformation() != null) {
msg = msg + "\nJava exception follows:\n" +
Joiner.on("\n").join(event.getThrowableStrRep());
}
int lineNumber = Integer.parseInt(event.getLocationInformation().getLineNumber());
String fileName = event.getLocationInformation().getFileName();
NativeLogger.LogToGlog(
levelToSeverity(level).getValue(), msg, fileName, lineNumber);
}
/**
* Returns a log4j level string corresponding to the Glog log level
*/
private static String log4jLevelForTLogLevel(TLogLevel logLevel)
throws InternalException {
switch (logLevel) {
case INFO: return "INFO";
case WARN: return "WARN";
case ERROR: return "ERROR";
case FATAL: return "FATAL";
case VLOG:
case VLOG_2: return "DEBUG";
case VLOG_3: return "TRACE";
default: throw new InternalException("Unknown log level:" + logLevel);
}
}
/**
* Manually override Log4j root logger configuration. Any values in log4j.properties
* not overridden (that is, anything but the root logger and its default level) will
* continue to have effect.
* - impalaLogLevel - the maximum log level for org.apache.impala.* classes
* - otherLogLevel - the maximum log level for all other classes
*/
public static void Install(TLogLevel impalaLogLevel, TLogLevel otherLogLevel)
throws InternalException {
Properties properties = new Properties();
properties.setProperty("log4j.appender.glog", GlogAppender.class.getName());
// These settings are relatively subtle. log4j provides many ways to filter log
// messages, and configuring them in the right order is a bit of black magic.
//
// The 'Threshold' property supercedes everything, so must be set to its most
// permissive and applies to any message sent to the glog appender.
//
// The 'rootLogger' property controls the default maximum logging level (where more
// verbose->larger logging level) for the entire space of classes. This will apply to
// all non-Impala classes, so is set to otherLogLevel.
//
// Finally we can configure per-package logging which overrides the rootLogger
// setting. In order to control Impala's logging independently of the rest of the
// world, we set the log level for org.apache.impala.
properties.setProperty("log4j.rootLogger",
log4jLevelForTLogLevel(otherLogLevel) + ",glog");
properties.setProperty("log4j.appender.glog.Threshold", "TRACE");
properties.setProperty("log4j.logger.org.apache.impala",
log4jLevelForTLogLevel(impalaLogLevel));
PropertyConfigurator.configure(properties);
Logger.getLogger(GlogAppender.class).info(String.format("Logging (re)initialized. " +
"Impala: %s, All other: %s", impalaLogLevel, otherLogLevel));
}
/**
* Get the log4j log level corresponding to a serialized TGetJavaLogLevelParams.
*/
public static String getLogLevel(byte[] serializedParams) throws ImpalaException {
TGetJavaLogLevelParams thriftParams = new TGetJavaLogLevelParams();
JniUtil.deserializeThrift(protocolFactory_, thriftParams, serializedParams);
String className = thriftParams.getClass_name();
if (Strings.isNullOrEmpty(className)) return null;
return Logger.getLogger(className).getEffectiveLevel().toString();
}
/**
* Sets the logging level of a class as per serialized TSetJavaLogLevelParams.
*/
public static String setLogLevel(byte[] serializedParams) throws ImpalaException {
TSetJavaLogLevelParams thriftParams = new TSetJavaLogLevelParams();
JniUtil.deserializeThrift(protocolFactory_, thriftParams, serializedParams);
String className = thriftParams.getClass_name();
String logLevel = thriftParams.getLog_level();
if (Strings.isNullOrEmpty(className) || Strings.isNullOrEmpty(logLevel)) return null;
// Level.toLevel() returns DEBUG for an incorrect logLevel input.
Logger.getLogger(className).setLevel(Level.toLevel(logLevel));
return Logger.getLogger(className).getEffectiveLevel().toString();
}
/**
* Re-initializes the Java log4j logging levels.
*/
public static void resetLogLevels() throws ImpalaException {
LogManager.resetConfiguration();
Install(TLogLevel.values()[BackendConfig.INSTANCE.getImpalaLogLevel()],
TLogLevel.values()[BackendConfig.INSTANCE.getNonImpalaJavaVlogLevel()]);
}
};