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
17package com.android.internal.logging;
18
19import android.util.Log;
20import com.android.internal.util.FastPrintWriter;
21import dalvik.system.DalvikLogging;
22import dalvik.system.DalvikLogHandler;
23
24import java.io.PrintWriter;
25import java.io.StringWriter;
26import java.util.logging.Formatter;
27import java.util.logging.Handler;
28import java.util.logging.Level;
29import java.util.logging.LogRecord;
30import java.util.logging.Logger;
31
32/**
33 * Implements a {@link java.util.logging.Logger} handler that writes to the Android log. The
34 * implementation is rather straightforward. The name of the logger serves as
35 * the log tag. Only the log levels need to be converted appropriately. For
36 * this purpose, the following mapping is being used:
37 *
38 * <table>
39 *   <tr>
40 *     <th>logger level</th>
41 *     <th>Android level</th>
42 *   </tr>
43 *   <tr>
44 *     <td>
45 *       SEVERE
46 *     </td>
47 *     <td>
48 *       ERROR
49 *     </td>
50 *   </tr>
51 *   <tr>
52 *     <td>
53 *       WARNING
54 *     </td>
55 *     <td>
56 *       WARN
57 *     </td>
58 *   </tr>
59 *   <tr>
60 *     <td>
61 *       INFO
62 *     </td>
63 *     <td>
64 *       INFO
65 *     </td>
66 *   </tr>
67 *   <tr>
68 *     <td>
69 *       CONFIG
70 *     </td>
71 *     <td>
72 *       DEBUG
73 *     </td>
74 *   </tr>
75 *   <tr>
76 *     <td>
77 *       FINE, FINER, FINEST
78 *     </td>
79 *     <td>
80 *       VERBOSE
81 *     </td>
82 *   </tr>
83 * </table>
84 */
85public class AndroidHandler extends Handler implements DalvikLogHandler {
86    /**
87     * Holds the formatter for all Android log handlers.
88     */
89    private static final Formatter THE_FORMATTER = new Formatter() {
90        @Override
91        public String format(LogRecord r) {
92            Throwable thrown = r.getThrown();
93            if (thrown != null) {
94                StringWriter sw = new StringWriter();
95                PrintWriter pw = new FastPrintWriter(sw, false, 256);
96                sw.write(r.getMessage());
97                sw.write("\n");
98                thrown.printStackTrace(pw);
99                pw.flush();
100                return sw.toString();
101            } else {
102                return r.getMessage();
103            }
104        }
105    };
106
107    /**
108     * Constructs a new instance of the Android log handler.
109     */
110    public AndroidHandler() {
111        setFormatter(THE_FORMATTER);
112    }
113
114    @Override
115    public void close() {
116        // No need to close, but must implement abstract method.
117    }
118
119    @Override
120    public void flush() {
121        // No need to flush, but must implement abstract method.
122    }
123
124    @Override
125    public void publish(LogRecord record) {
126        int level = getAndroidLevel(record.getLevel());
127        String tag = DalvikLogging.loggerNameToTag(record.getLoggerName());
128        if (!Log.isLoggable(tag, level)) {
129            return;
130        }
131
132        try {
133            String message = getFormatter().format(record);
134            Log.println(level, tag, message);
135        } catch (RuntimeException e) {
136            Log.e("AndroidHandler", "Error logging message.", e);
137        }
138    }
139
140    public void publish(Logger source, String tag, Level level, String message) {
141        // TODO: avoid ducking into native 2x; we aren't saving any formatter calls
142        int priority = getAndroidLevel(level);
143        if (!Log.isLoggable(tag, priority)) {
144            return;
145        }
146
147        try {
148            Log.println(priority, tag, message);
149        } catch (RuntimeException e) {
150            Log.e("AndroidHandler", "Error logging message.", e);
151        }
152    }
153
154    /**
155     * Converts a {@link java.util.logging.Logger} logging level into an Android one.
156     *
157     * @param level The {@link java.util.logging.Logger} logging level.
158     *
159     * @return The resulting Android logging level.
160     */
161    static int getAndroidLevel(Level level) {
162        int value = level.intValue();
163        if (value >= 1000) { // SEVERE
164            return Log.ERROR;
165        } else if (value >= 900) { // WARNING
166            return Log.WARN;
167        } else if (value >= 800) { // INFO
168            return Log.INFO;
169        } else {
170            return Log.DEBUG;
171        }
172    }
173}
174