1 /*
2  *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 package org.webrtc;
12 
13 import android.support.annotation.Nullable;
14 import java.io.PrintWriter;
15 import java.io.StringWriter;
16 import java.util.EnumSet;
17 import java.util.logging.Level;
18 import java.util.logging.Logger;
19 import org.webrtc.Loggable;
20 
21 /**
22  * Java wrapper for WebRTC logging. Logging defaults to java.util.logging.Logger, but a custom
23  * logger implementing the Loggable interface can be injected along with a Severity. All subsequent
24  * log messages will then be redirected to the injected Loggable, except those with a severity lower
25  * than the specified severity, which will be discarded.
26  *
27  * It is also possible to switch to native logging (rtc::LogMessage) if one of the following static
28  * functions are called from the app:
29  * - Logging.enableLogThreads
30  * - Logging.enableLogTimeStamps
31  * - Logging.enableLogToDebugOutput
32  *
33  * The priority goes:
34  * 1. Injected loggable
35  * 2. Native logging
36  * 3. Fallback logging.
37  * Only one method will be used at a time.
38  *
39  * Injecting a Loggable or using any of the enable... methods requires that the native library is
40  * loaded, using PeerConnectionFactory.initialize.
41  */
42 public class Logging {
43   private static final Logger fallbackLogger = createFallbackLogger();
44   private static volatile boolean loggingEnabled;
45   @Nullable private static Loggable loggable;
46   private static Severity loggableSeverity;
47 
createFallbackLogger()48   private static Logger createFallbackLogger() {
49     final Logger fallbackLogger = Logger.getLogger("org.webrtc.Logging");
50     fallbackLogger.setLevel(Level.ALL);
51     return fallbackLogger;
52   }
53 
injectLoggable(Loggable injectedLoggable, Severity severity)54   static void injectLoggable(Loggable injectedLoggable, Severity severity) {
55     if (injectedLoggable != null) {
56       loggable = injectedLoggable;
57       loggableSeverity = severity;
58     }
59   }
60 
deleteInjectedLoggable()61   static void deleteInjectedLoggable() {
62     loggable = null;
63   }
64 
65   // TODO(solenberg): Remove once dependent projects updated.
66   @Deprecated
67   public enum TraceLevel {
68     TRACE_NONE(0x0000),
69     TRACE_STATEINFO(0x0001),
70     TRACE_WARNING(0x0002),
71     TRACE_ERROR(0x0004),
72     TRACE_CRITICAL(0x0008),
73     TRACE_APICALL(0x0010),
74     TRACE_DEFAULT(0x00ff),
75     TRACE_MODULECALL(0x0020),
76     TRACE_MEMORY(0x0100),
77     TRACE_TIMER(0x0200),
78     TRACE_STREAM(0x0400),
79     TRACE_DEBUG(0x0800),
80     TRACE_INFO(0x1000),
81     TRACE_TERSEINFO(0x2000),
82     TRACE_ALL(0xffff);
83 
84     public final int level;
TraceLevel(int level)85     TraceLevel(int level) {
86       this.level = level;
87     }
88   }
89 
90   // Keep in sync with webrtc/rtc_base/logging.h:LoggingSeverity.
91   public enum Severity { LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR, LS_NONE }
92 
enableLogThreads()93   public static void enableLogThreads() {
94     nativeEnableLogThreads();
95   }
96 
enableLogTimeStamps()97   public static void enableLogTimeStamps() {
98     nativeEnableLogTimeStamps();
99   }
100 
101   // TODO(solenberg): Remove once dependent projects updated.
102   @Deprecated
enableTracing(String path, EnumSet<TraceLevel> levels)103   public static void enableTracing(String path, EnumSet<TraceLevel> levels) {}
104 
105   // Enable diagnostic logging for messages of |severity| to the platform debug
106   // output. On Android, the output will be directed to Logcat.
107   // Note: this function starts collecting the output of the RTC_LOG() macros.
108   // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
109   @SuppressWarnings("NoSynchronizedMethodCheck")
enableLogToDebugOutput(Severity severity)110   public static synchronized void enableLogToDebugOutput(Severity severity) {
111     if (loggable != null) {
112       throw new IllegalStateException(
113           "Logging to native debug output not supported while Loggable is injected. "
114           + "Delete the Loggable before calling this method.");
115     }
116     nativeEnableLogToDebugOutput(severity.ordinal());
117     loggingEnabled = true;
118   }
119 
log(Severity severity, String tag, String message)120   public static void log(Severity severity, String tag, String message) {
121     if (tag == null || message == null) {
122       throw new IllegalArgumentException("Logging tag or message may not be null.");
123     }
124     if (loggable != null) {
125       // Filter log messages below loggableSeverity.
126       if (severity.ordinal() < loggableSeverity.ordinal()) {
127         return;
128       }
129       loggable.onLogMessage(message, severity, tag);
130       return;
131     }
132 
133     // Try native logging if no loggable is injected.
134     if (loggingEnabled) {
135       nativeLog(severity.ordinal(), tag, message);
136       return;
137     }
138 
139     // Fallback to system log.
140     Level level;
141     switch (severity) {
142       case LS_ERROR:
143         level = Level.SEVERE;
144         break;
145       case LS_WARNING:
146         level = Level.WARNING;
147         break;
148       case LS_INFO:
149         level = Level.INFO;
150         break;
151       default:
152         level = Level.FINE;
153         break;
154     }
155     fallbackLogger.log(level, tag + ": " + message);
156   }
157 
d(String tag, String message)158   public static void d(String tag, String message) {
159     log(Severity.LS_INFO, tag, message);
160   }
161 
e(String tag, String message)162   public static void e(String tag, String message) {
163     log(Severity.LS_ERROR, tag, message);
164   }
165 
w(String tag, String message)166   public static void w(String tag, String message) {
167     log(Severity.LS_WARNING, tag, message);
168   }
169 
e(String tag, String message, Throwable e)170   public static void e(String tag, String message, Throwable e) {
171     log(Severity.LS_ERROR, tag, message);
172     log(Severity.LS_ERROR, tag, e.toString());
173     log(Severity.LS_ERROR, tag, getStackTraceString(e));
174   }
175 
w(String tag, String message, Throwable e)176   public static void w(String tag, String message, Throwable e) {
177     log(Severity.LS_WARNING, tag, message);
178     log(Severity.LS_WARNING, tag, e.toString());
179     log(Severity.LS_WARNING, tag, getStackTraceString(e));
180   }
181 
v(String tag, String message)182   public static void v(String tag, String message) {
183     log(Severity.LS_VERBOSE, tag, message);
184   }
185 
getStackTraceString(Throwable e)186   private static String getStackTraceString(Throwable e) {
187     if (e == null) {
188       return "";
189     }
190 
191     StringWriter sw = new StringWriter();
192     PrintWriter pw = new PrintWriter(sw);
193     e.printStackTrace(pw);
194     return sw.toString();
195   }
196 
nativeEnableLogToDebugOutput(int nativeSeverity)197   private static native void nativeEnableLogToDebugOutput(int nativeSeverity);
nativeEnableLogThreads()198   private static native void nativeEnableLogThreads();
nativeEnableLogTimeStamps()199   private static native void nativeEnableLogTimeStamps();
nativeLog(int severity, String tag, String message)200   private static native void nativeLog(int severity, String tag, String message);
201 }
202