1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.logging;
18 
19 import android.util.Log;
20 
21 import com.android.internal.util.FastPrintWriter;
22 
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25 import java.util.logging.Formatter;
26 import java.util.logging.Handler;
27 import java.util.logging.Level;
28 import java.util.logging.LogRecord;
29 import java.util.logging.Logger;
30 
31 /**
32  * Implements a {@link java.util.logging.Logger} handler that writes to the Android log. The
33  * implementation is rather straightforward. The name of the logger serves as
34  * the log tag. Only the log levels need to be converted appropriately. For
35  * this purpose, the following mapping is being used:
36  *
37  * <table>
38  *   <tr>
39  *     <th>logger level</th>
40  *     <th>Android level</th>
41  *   </tr>
42  *   <tr>
43  *     <td>
44  *       SEVERE
45  *     </td>
46  *     <td>
47  *       ERROR
48  *     </td>
49  *   </tr>
50  *   <tr>
51  *     <td>
52  *       WARNING
53  *     </td>
54  *     <td>
55  *       WARN
56  *     </td>
57  *   </tr>
58  *   <tr>
59  *     <td>
60  *       INFO
61  *     </td>
62  *     <td>
63  *       INFO
64  *     </td>
65  *   </tr>
66  *   <tr>
67  *     <td>
68  *       CONFIG
69  *     </td>
70  *     <td>
71  *       DEBUG
72  *     </td>
73  *   </tr>
74  *   <tr>
75  *     <td>
76  *       FINE, FINER, FINEST
77  *     </td>
78  *     <td>
79  *       VERBOSE
80  *     </td>
81  *   </tr>
82  * </table>
83  */
84 public class AndroidHandler extends Handler {
85     /**
86      * Holds the formatter for all Android log handlers.
87      */
88     private static final Formatter THE_FORMATTER = new Formatter() {
89         @Override
90         public String format(LogRecord r) {
91             Throwable thrown = r.getThrown();
92             if (thrown != null) {
93                 StringWriter sw = new StringWriter();
94                 PrintWriter pw = new FastPrintWriter(sw, false, 256);
95                 sw.write(r.getMessage());
96                 sw.write("\n");
97                 thrown.printStackTrace(pw);
98                 pw.flush();
99                 return sw.toString();
100             } else {
101                 return r.getMessage();
102             }
103         }
104     };
105 
106     /**
107      * Constructs a new instance of the Android log handler.
108      */
AndroidHandler()109     public AndroidHandler() {
110         setFormatter(THE_FORMATTER);
111     }
112 
113     @Override
close()114     public void close() {
115         // No need to close, but must implement abstract method.
116     }
117 
118     @Override
flush()119     public void flush() {
120         // No need to flush, but must implement abstract method.
121     }
122 
123     /**
124      * Returns the short logger tag (up to 23 chars) for the given logger name.
125      * Traditionally loggers are named by fully-qualified Java classes; this
126      * method attempts to return a concise identifying part of such names.
127      */
loggerNameToTag(String loggerName)128     private static String loggerNameToTag(String loggerName) {
129         // Anonymous logger.
130         if (loggerName == null) {
131             return "null";
132         }
133 
134         int length = loggerName.length();
135         if (length <= 23) {
136             return loggerName;
137         }
138 
139         int lastPeriod = loggerName.lastIndexOf(".");
140         return length - (lastPeriod + 1) <= 23
141                 ? loggerName.substring(lastPeriod + 1)
142                 : loggerName.substring(loggerName.length() - 23);
143     }
144 
145     @Override
publish(LogRecord record)146     public void publish(LogRecord record) {
147         int level = getAndroidLevel(record.getLevel());
148         String tag = loggerNameToTag(record.getLoggerName());
149         if (!Log.isLoggable(tag, level)) {
150             return;
151         }
152 
153         try {
154             String message = getFormatter().format(record);
155             Log.println(level, tag, message);
156         } catch (RuntimeException e) {
157             Log.e("AndroidHandler", "Error logging message.", e);
158         }
159     }
160 
publish(Logger source, String tag, Level level, String message)161     public void publish(Logger source, String tag, Level level, String message) {
162         // TODO: avoid ducking into native 2x; we aren't saving any formatter calls
163         int priority = getAndroidLevel(level);
164         if (!Log.isLoggable(tag, priority)) {
165             return;
166         }
167 
168         try {
169             Log.println(priority, tag, message);
170         } catch (RuntimeException e) {
171             Log.e("AndroidHandler", "Error logging message.", e);
172         }
173     }
174 
175     /**
176      * Converts a {@link java.util.logging.Logger} logging level into an Android one.
177      *
178      * @param level The {@link java.util.logging.Logger} logging level.
179      *
180      * @return The resulting Android logging level.
181      */
getAndroidLevel(Level level)182     static int getAndroidLevel(Level level) {
183         int value = level.intValue();
184         if (value >= 1000) { // SEVERE
185             return Log.ERROR;
186         } else if (value >= 900) { // WARNING
187             return Log.WARN;
188         } else if (value >= 800) { // INFO
189             return Log.INFO;
190         } else {
191             return Log.DEBUG;
192         }
193     }
194 }
195