Skip to content

Commit

Permalink
HBASE-21048 Get LogLevel is not working from console in secure enviro…
Browse files Browse the repository at this point in the history
…nment

Signed-off-by: Reid Chan <reidchan@apache.org>
Amend author: Reid Chan <reidchan@apache.org>
  • Loading branch information
jojochuang authored and Reidddddd committed Apr 13, 2019
1 parent 9c13dde commit 16146a1
Show file tree
Hide file tree
Showing 3 changed files with 495 additions and 73 deletions.
5 changes: 5 additions & 0 deletions hbase-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minikdc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<!-- Needs to make the profile in apache parent pom -->
Expand Down
211 changes: 190 additions & 21 deletions hbase-http/src/main/java/org/apache/hadoop/hbase/http/log/LogLevel.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,61 +26,230 @@
import java.net.URLConnection;
import java.util.Objects;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.impl.Jdk14Logger;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.http.HttpServer;
import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
import org.apache.hadoop.security.authentication.client.KerberosAuthenticator;
import org.apache.hadoop.util.ServletUtil;
import org.apache.hadoop.util.Tool;
import org.apache.log4j.LogManager;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.Log4jLoggerAdapter;

import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.apache.hbase.thirdparty.com.google.common.base.Charsets;

/**
* Change log level in runtime.
*/
@InterfaceAudience.Private
public final class LogLevel {
private static final String USAGES = "\nUsage: General options are:\n"
+ "\t[-getlevel <host:httpPort> <name>]\n"
+ "\t[-setlevel <host:httpPort> <name> <level>]\n";
+ "\t[-getlevel <host:port> <classname>\n"
+ "\t[-setlevel <host:port> <classname> <level> ";

public static final String PROTOCOL_HTTP = "http";
/**
* A command line implementation
*/
public static void main(String[] args) {
if (args.length == 3 && "-getlevel".equals(args[0])) {
process("http://" + args[1] + "/logLevel?log=" + args[2]);
return;
}
else if (args.length == 4 && "-setlevel".equals(args[0])) {
process("http://" + args[1] + "/logLevel?log=" + args[2]
+ "&level=" + args[3]);
return;
}
public static void main(String[] args) throws Exception {
CLI cli = new CLI(new Configuration());
System.exit(cli.run(args));
}

/**
* Valid command line options.
*/
private enum Operations {
GETLEVEL,
SETLEVEL,
UNKNOWN
}

private static void printUsage() {
System.err.println(USAGES);
System.exit(-1);
}

private static void process(String urlstring) {
try {
URL url = new URL(urlstring);
System.out.println("Connecting to " + url);
URLConnection connection = url.openConnection();
@VisibleForTesting
static class CLI extends Configured implements Tool {
private Operations operation = Operations.UNKNOWN;
private String hostName;
private String className;
private String level;

CLI(Configuration conf) {
setConf(conf);
}

@Override
public int run(String[] args) throws Exception {
try {
parseArguments(args);
sendLogLevelRequest();
} catch (HadoopIllegalArgumentException e) {
printUsage();
}
return 0;
}

/**
* Send HTTP request to the daemon.
* @throws HadoopIllegalArgumentException if arguments are invalid.
* @throws Exception if unable to connect
*/
private void sendLogLevelRequest()
throws HadoopIllegalArgumentException, Exception {
switch (operation) {
case GETLEVEL:
doGetLevel();
break;
case SETLEVEL:
doSetLevel();
break;
default:
throw new HadoopIllegalArgumentException(
"Expect either -getlevel or -setlevel");
}
}

public void parseArguments(String[] args) throws
HadoopIllegalArgumentException {
if (args.length == 0) {
throw new HadoopIllegalArgumentException("No arguments specified");
}
int nextArgIndex = 0;
while (nextArgIndex < args.length) {
switch (args[nextArgIndex]) {
case "-getlevel":
nextArgIndex = parseGetLevelArgs(args, nextArgIndex);
break;
case "-setlevel":
nextArgIndex = parseSetLevelArgs(args, nextArgIndex);
break;
default:
throw new HadoopIllegalArgumentException(
"Unexpected argument " + args[nextArgIndex]);
}
}

// if operation is never specified in the arguments
if (operation == Operations.UNKNOWN) {
throw new HadoopIllegalArgumentException(
"Must specify either -getlevel or -setlevel");
}
}

private int parseGetLevelArgs(String[] args, int index) throws
HadoopIllegalArgumentException {
// fail if multiple operations are specified in the arguments
if (operation != Operations.UNKNOWN) {
throw new HadoopIllegalArgumentException("Redundant -getlevel command");
}
// check number of arguments is sufficient
if (index + 2 >= args.length) {
throw new HadoopIllegalArgumentException("-getlevel needs two parameters");
}
operation = Operations.GETLEVEL;
hostName = args[index + 1];
className = args[index + 2];
return index + 3;
}

private int parseSetLevelArgs(String[] args, int index) throws
HadoopIllegalArgumentException {
// fail if multiple operations are specified in the arguments
if (operation != Operations.UNKNOWN) {
throw new HadoopIllegalArgumentException("Redundant -setlevel command");
}
// check number of arguments is sufficient
if (index + 3 >= args.length) {
throw new HadoopIllegalArgumentException("-setlevel needs three parameters");
}
operation = Operations.SETLEVEL;
hostName = args[index + 1];
className = args[index + 2];
level = args[index + 3];
return index + 4;
}

/**
* Send HTTP request to get log level.
*
* @throws HadoopIllegalArgumentException if arguments are invalid.
* @throws Exception if unable to connect
*/
private void doGetLevel() throws Exception {
process(PROTOCOL_HTTP + "://" + hostName + "/logLevel?log=" + className);
}

/**
* Send HTTP request to set log level.
*
* @throws HadoopIllegalArgumentException if arguments are invalid.
* @throws Exception if unable to connect
*/
private void doSetLevel() throws Exception {
process(PROTOCOL_HTTP + "://" + hostName + "/logLevel?log=" + className
+ "&level=" + level);
}

/**
* Connect to the URL. Supports HTTP and supports SPNEGO
* authentication. It falls back to simple authentication if it fails to
* initiate SPNEGO.
*
* @param url the URL address of the daemon servlet
* @return a connected connection
* @throws Exception if it can not establish a connection.
*/
private URLConnection connect(URL url) throws Exception {
AuthenticatedURL.Token token = new AuthenticatedURL.Token();
AuthenticatedURL aUrl;
URLConnection connection;

aUrl = new AuthenticatedURL(new KerberosAuthenticator());
connection = aUrl.openConnection(url, token);
connection.connect();
try (InputStreamReader streamReader = new InputStreamReader(connection.getInputStream());
return connection;
}

/**
* Configures the client to send HTTP request to the URL.
* Supports SPENGO for authentication.
* @param urlString URL and query string to the daemon's web UI
* @throws Exception if unable to connect
*/
private void process(String urlString) throws Exception {
URL url = new URL(urlString);
System.out.println("Connecting to " + url);

URLConnection connection = connect(url);

// read from the servlet

try (InputStreamReader streamReader =
new InputStreamReader(connection.getInputStream(), Charsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(streamReader)) {
bufferedReader.lines().filter(Objects::nonNull).filter(line -> line.startsWith(MARKER))
.forEach(line -> System.out.println(TAG.matcher(line).replaceAll("")));
.forEach(line -> System.out.println(TAG.matcher(line).replaceAll("")));
} catch (IOException ioe) {
System.err.println("" + ioe);
}
} catch (IOException ioe) {
System.err.println("" + ioe);
}
}

Expand Down
Loading

0 comments on commit 16146a1

Please sign in to comment.