在debug hadoop的问题的时候,经常需要临时打开hadoop的debug日志,可以通过更改环境变量:

1
export  HADOOP_ROOT_LOGGER=DEBUG,console

来开启debug log,这几天在看hadoop shell的时候发现有daemonlog这个参数,可以临时获取或更改当前日志基本,是通过org.apache.hadoop.log.LogLevel的main方法来实现的,比如操作datanode的日志级别:

1
2
hadoop daemonlog  -getlevel 127.0.0.1:50070 datanode
hadoop daemonlog  -setlevel 127.0.0.1:50070 datanode DEBUG  //level 的设置必须是大写的

来看下org.apache.hadoop.log.LogLevel的具体实现:

main方法在开始会解析输入的参数,并生成一个url,调用process方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
  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]);  // 由process实现
       return ;
     }
     System.err.println( USAGES);
     System. exit (-1);
   }

调用process方法,建立到url的连接,并获取读取数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   private static void process(String urlstring) {
     try {
       URL url = new URL(urlstring);
       System.out.println( "Connecting to "  + url);
       URLConnection connection = url.openConnection();
       connection.connect();
       BufferedReader  in  = new BufferedReader(new InputStreamReader(
           connection.getInputStream()));
       for (String line; (line =  in .readLine()) != null; )
         if  (line.startsWith(MARKER)) { 
           System.out.println(TAG.matcher(line).replaceAll( "" )); 
         }
       in .close();
     } catch (IOException ioe) {
       System.err.println( ""  + ioe);
     }
   }

LogLevel其内部实现了一个扩展了HttpServlet 的Servlet,并重写了一个doGet方法来处理GET请求,如果是/logLevel?log=xxxx的url是获取日志级别,/logLevel?log=xxx&level=xxxx是设置日志级别:

Servlet类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
   public  static  class  Servlet  extends  HttpServlet {
     private  static  final  long  serialVersionUID = 1L;
     public  void  doGet(HttpServletRequest request, HttpServletResponse response
         throws  ServletException, IOException {  //doGet方法用来处理请求
       // Do the authorization
       if  (!HttpServer.hasAdministratorAccess(getServletContext (), request,
           response)) {
         return ;
       }
       PrintWriter out = ServletUtil.initHTML(response,  "Log Level" );  //初始化响应的html头内容
       String logName = ServletUtil.getParameter(request,  "log" );  //从请求中获取log的设置
       String level = ServletUtil.getParameter(request,  "level" );  //从请求中获取level的设置
       if  (logName !=  null ) {
         out.println(  "<br /><hr /><h3>Results</h3>"  );
         out.println( MARKER
             "Submitted Log Name: <b>"  + logName +  "</b><br />" );
         Log log = LogFactory.getLog(logName);  //根据日志名获取Log对象  org.apache.commons.logging.LogFactory  org.apache.commons.logging.Log(Log是一个接口)
  
         out.println( MARKER
             "Log Class: <b>"  + log.getClass().getName() + "</b><br />"  );
         if  (level !=  null ) {
           out.println( MARKER +  "Submitted Level: <b>"  + level +  "</b><br />" );
         }
         if  (log  instanceof  Log4JLogger) {  //对不同的log类调用对用的process方法
           process(((Log4JLogger)log).getLogger(), level, out);
         }
         else  if  (log  instanceof  Jdk14Logger ) {
           process(((Jdk14Logger)log).getLogger(), level, out);
         }
         else  {
           out.println(  "Sorry, "  + log.getClass() +  " not supported.<br />"  );
         }
       }
       out.println( FORMS);
       out.println(ServletUtil.HTML_TAIL);
     }

再来看对应的process方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
     private  static  void  process(org.apache.log4j.Logger log, String level,
         PrintWriter out)  throws  IOException {  //以常用的log4j为例
       if  (level !=  null ) {  //如果请求中含有level=xxx
         if  (!level.equals(org.apache.log4j.Level.toLevel(level).toString())) {
           out.println( MARKER +  "Bad level : <b>"  + level +  "</b><br />" );
         else 
           log.setLevel( org.apache.log4j.Level.toLevel(level));  //并且level为正确的值的话,直接调用对应Log实现类的setlevel设置log的级别
           out.println( MARKER +  "Setting Level to "  + level +  " ...<br />" );
         }
       }
       out.println( MARKER
           "Effective level: <b>"  + log.getEffectiveLevel() +  "</b><br />"  );
     }