View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
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    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import java.io.IOException;
18  import java.io.ObjectInputStream;
19  import java.io.Serializable;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.htmlunit.corejs.javascript.Context;
24  import org.htmlunit.corejs.javascript.NativeConsole;
25  import org.htmlunit.corejs.javascript.NativeConsole.ConsolePrinter;
26  import org.htmlunit.corejs.javascript.NativeConsole.Level;
27  import org.htmlunit.corejs.javascript.ScriptStackElement;
28  import org.htmlunit.corejs.javascript.VarScope;
29  
30  /**
31   * This class can be used to print messages to the logger. The first parameter
32   * can be a message-object containing format specifiers such as ("%o", "%s",
33   * "%d", "%i", "%f"). The logging methods are null-safe, so if the number of
34   * format specifiers and the numbers of parameters don't match, no exception is thrown.
35   * <p>
36   * The default logger uses Apache Commons Logging.
37   * </p>
38   *
39   * @author Andrea Martino
40   * @author Ronald Brill
41   */
42  public class WebConsole implements ConsolePrinter, Serializable {
43  
44      private static final Log LOG = LogFactory.getLog(WebConsole.class);
45  
46      private transient Logger logger_ = new DefaultLogger(LOG);
47  
48      /**
49       * A simple logging interface abstracting logging APIs.
50       */
51      public interface Logger {
52  
53          /**
54           * Is trace logging currently enabled?.
55           * <p>
56           * Call this method to prevent having to perform expensive operations
57           * (for example, <code>String</code> concatenation)
58           * when the log level is more than trace.
59           * </p>
60           *
61           * @return true if trace is enabled in the underlying logger.
62           */
63          boolean isTraceEnabled();
64  
65          /**
66           * Logs a message with trace log level.
67           *
68           * @param message log this message
69           */
70          void trace(Object message);
71  
72          /**
73           * Is debug logging currently enabled?.
74           * <p>
75           * Call this method to prevent having to perform expensive operations
76           * (for example, <code>String</code> concatenation)
77           * when the log level is more than debug.
78           * </p>
79           *
80           * @return true if debug is enabled in the underlying logger.
81           */
82          boolean isDebugEnabled();
83  
84          /**
85           * Logs a message with debug log level.
86           *
87           * @param message log this message
88           */
89          void debug(Object message);
90  
91          /**
92           * Is info logging currently enabled?.
93           * <p>
94           * Call this method to prevent having to perform expensive operations
95           * (for example, <code>String</code> concatenation)
96           * when the log level is more than info.
97           * </p>
98           *
99           * @return true if info is enabled in the underlying logger.
100          */
101         boolean isInfoEnabled();
102 
103         /**
104          * Logs a message with info log level.
105          *
106          * @param message log this message
107          */
108         void info(Object message);
109 
110         /**
111          * Is warn logging currently enabled?.
112          * <p>
113          * Call this method to prevent having to perform expensive operations
114          * (for example, <code>String</code> concatenation)
115          * when the log level is more than warn.
116          * </p>
117          *
118          * @return true if warn is enabled in the underlying logger.
119          */
120         boolean isWarnEnabled();
121 
122         /**
123          * Logs a message with warn log level.
124          *
125          * @param message log this message
126          */
127         void warn(Object message);
128 
129         /**
130          * Is error logging currently enabled?.
131          * <p>
132          * Call this method to prevent having to perform expensive operations
133          * (for example, <code>String</code> concatenation)
134          * when the log level is more than error.
135          * </p>
136          *
137          * @return true if error is enabled in the underlying logger.
138          */
139         boolean isErrorEnabled();
140 
141         /**
142          * Logs a message with error log level.
143          *
144          * @param message log this message
145          */
146         void error(Object message);
147     }
148 
149     /**
150      * Sets the Logger_.
151      * @param logger the logger
152      */
153     public void setLogger(final Logger logger) {
154         logger_ = logger;
155     }
156 
157     /**
158      * Returns the current Logger.
159      * @return the logger
160      */
161     public Logger getLogger() {
162         return logger_;
163     }
164 
165     @Override
166     public void print(final Context cx, final VarScope scope, final Level level,
167             final Object[] args, final ScriptStackElement[] stack) {
168         switch (level) {
169             case TRACE -> {
170                 if (logger_.isInfoEnabled()) {
171                     String msg = format(cx, scope, args);
172                     if (stack != null) {
173                         final StringBuilder scriptStack = new StringBuilder();
174                         scriptStack.append(msg);
175 
176                         for (final ScriptStackElement scriptStackElement : stack) {
177                             if (scriptStack.length() > 0) {
178                                 scriptStack.append('\n');
179                             }
180                             scriptStack.append(scriptStackElement);
181                         }
182 
183                         msg = scriptStack.toString();
184                     }
185                     logger_.info(msg);
186                 }
187             }
188             case DEBUG -> {
189                 if (logger_.isDebugEnabled()) {
190                     logger_.debug(format(cx, scope, args));
191                 }
192             }
193             case INFO -> {
194                 if (logger_.isInfoEnabled()) {
195                     logger_.info(format(cx, scope, args));
196                 }
197             }
198             case WARN -> {
199                 if (logger_.isWarnEnabled()) {
200                     logger_.warn(format(cx, scope, args));
201                 }
202             }
203             case ERROR -> {
204                 if (logger_.isErrorEnabled()) {
205                     logger_.error(format(cx, scope, args));
206                 }
207             }
208             default -> { /* skip */ }
209         }
210     }
211 
212     private static String format(final Context cx, final VarScope scope, final Object[] args) {
213         final String msg = NativeConsole.format(cx, scope, args);
214         return msg.replaceAll("\\r?\\n", "\n");
215     }
216 
217     private void readObject(final ObjectInputStream ois) throws ClassNotFoundException, IOException {
218         ois.defaultReadObject();
219         setLogger(new DefaultLogger(LOG));
220     }
221 
222     /**
223      * This class is the default logger used by WebConsole.
224      */
225     private static class DefaultLogger implements Logger {
226 
227         private final Log webConsoleLogger_;
228 
229         /**
230          * Ctor.
231          */
232         DefaultLogger(final Log logger) {
233             webConsoleLogger_ = logger;
234         }
235 
236         @Override
237         public boolean isTraceEnabled() {
238             return webConsoleLogger_.isTraceEnabled();
239         }
240 
241         @Override
242         public void trace(final Object message) {
243             webConsoleLogger_.trace(message);
244         }
245 
246         @Override
247         public boolean isDebugEnabled() {
248             return webConsoleLogger_.isDebugEnabled();
249         }
250 
251         @Override
252         public void debug(final Object message) {
253             webConsoleLogger_.debug(message);
254         }
255 
256         @Override
257         public boolean isInfoEnabled() {
258             return webConsoleLogger_.isInfoEnabled();
259         }
260 
261         @Override
262         public void info(final Object message) {
263             webConsoleLogger_.info(message);
264         }
265 
266         @Override
267         public boolean isWarnEnabled() {
268             return webConsoleLogger_.isWarnEnabled();
269         }
270 
271         @Override
272         public void warn(final Object message) {
273             webConsoleLogger_.warn(message);
274         }
275 
276         @Override
277         public boolean isErrorEnabled() {
278             return webConsoleLogger_.isErrorEnabled();
279         }
280 
281         @Override
282         public void error(final Object message) {
283             webConsoleLogger_.error(message);
284         }
285     }
286 }