Add a simple remote log handler, and update the sample to use it
Review at http://gwt-code-reviews.appspot.com/626802
Review by: fredsa@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8328 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.java b/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.java
index 9c812b4..60ced41 100644
--- a/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.java
+++ b/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.java
@@ -23,6 +23,7 @@
import com.google.gwt.logging.client.DevelopmentModeLogHandler;
import com.google.gwt.logging.client.FirebugLogHandler;
import com.google.gwt.logging.client.HasWidgetsLogHandler;
+import com.google.gwt.logging.client.SimpleRemoteLogHandler;
import com.google.gwt.logging.client.SystemLogHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
@@ -66,6 +67,7 @@
@UiField CheckBox firebugCheckbox;
@UiField CheckBox popupCheckbox;
@UiField CheckBox systemCheckbox;
+ @UiField CheckBox remoteCheckbox;
private Map<String, Handler> handlers;
private Logger logger;
private Panel panel;
@@ -86,6 +88,7 @@
setupHandler(DevelopmentModeLogHandler.class.getName(), devmodeCheckbox);
setupHandler(FirebugLogHandler.class.getName(), firebugCheckbox);
setupHandler(HasWidgetsLogHandler.class.getName(), popupCheckbox);
+ setupHandler(SimpleRemoteLogHandler.class.getName(), remoteCheckbox);
}
public Panel getPanel() {
diff --git a/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.ui.xml b/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.ui.xml
index 08e790e..9dc9421 100644
--- a/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.ui.xml
+++ b/samples/logexample/src/com/google/gwt/sample/logexample/client/HandlerController.ui.xml
@@ -13,5 +13,6 @@
<br/>
<g:CheckBox ui:field='firebugCheckbox'> Firebug Handler </g:CheckBox>
<g:CheckBox ui:field='popupCheckbox'> Popup Handler </g:CheckBox>
+ <g:CheckBox ui:field='remoteCheckbox'> Remote Handler </g:CheckBox>
</g:HTMLPanel>
</ui:UiBinder>
\ No newline at end of file
diff --git a/samples/logexample/war/WEB-INF/web.xml b/samples/logexample/war/WEB-INF/web.xml
new file mode 100644
index 0000000..6ff5449
--- /dev/null
+++ b/samples/logexample/war/WEB-INF/web.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app>
+
+ <!-- Servlets -->
+ <servlet>
+ <servlet-name>logServlet</servlet-name>
+ <servlet-class>com.google.gwt.sample.logexample.server.LoggingServiceImpl</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>logServlet</servlet-name>
+ <url-pattern>/logexample/log</url-pattern>
+ </servlet-mapping>
+
+ <servlet>
+ <servlet-name>remoteLoggingServlet</servlet-name>
+ <servlet-class>com.google.gwt.logging.server.RemoteLoggingServiceImpl</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>remoteLoggingServlet</servlet-name>
+ <url-pattern>logexample/remote_logging</url-pattern>
+ </servlet-mapping>
+
+
+ <!-- Default page to serve -->
+ <welcome-file-list>
+ <welcome-file>LogExample.html</welcome-file>
+ </welcome-file-list>
+
+</web-app>
diff --git a/user/src/com/google/gwt/logging/Logging.gwt.xml b/user/src/com/google/gwt/logging/Logging.gwt.xml
index 48adb38..c22ccc4 100644
--- a/user/src/com/google/gwt/logging/Logging.gwt.xml
+++ b/user/src/com/google/gwt/logging/Logging.gwt.xml
@@ -12,9 +12,10 @@
<!-- implied. License for the specific language governing permissions and -->
<!-- limitations under the License. -->
-<!-- regular expressions support. -->
<module>
<inherits name="com.google.gwt.logging.LogImpl"/>
+ <source path="client" />
+ <source path="shared" />
<replace-with class="com.google.gwt.logging.client.LogConfiguration.LogConfigurationImplRegular">
<when-type-is class="com.google.gwt.logging.client.LogConfiguration.LogConfigurationImplNull"/>
@@ -79,6 +80,11 @@
<when-type-is class="com.google.gwt.logging.client.SystemLogHandler" />
<when-property-is name="gwt.logging.systemHandler" value="DISABLED" />
</replace-with>
+ <define-property name="gwt.logging.simpleRemoteHandler" values="ENABLED, DISABLED" />
+ <replace-with class="com.google.gwt.logging.client.NullLogHandler">
+ <when-type-is class="com.google.gwt.logging.client.SimpleRemoteLogHandler" />
+ <when-property-is name="gwt.logging.simpleRemoteHandler" value="DISABLED" />
+ </replace-with>
<define-property name="gwt.logging.popupHandler" values="ENABLED, DISABLED" />
<replace-with class="com.google.gwt.logging.client.NullLoggingPopup">
<when-type-is class="com.google.gwt.logging.client.LoggingPopup" />
@@ -93,6 +99,7 @@
<set-property name="gwt.logging.firebugHandler" value="ENABLED" />
<set-property name="gwt.logging.popupHandler" value="ENABLED" />
<set-property name="gwt.logging.systemHandler" value="ENABLED" />
+ <set-property name="gwt.logging.simpleRemoteHandler" value="ENABLED" />
<entry-point class="com.google.gwt.logging.client.LogConfiguration"/>
</module>
\ No newline at end of file
diff --git a/user/src/com/google/gwt/logging/client/LogConfiguration.java b/user/src/com/google/gwt/logging/client/LogConfiguration.java
index e08b93a..7007a89 100644
--- a/user/src/com/google/gwt/logging/client/LogConfiguration.java
+++ b/user/src/com/google/gwt/logging/client/LogConfiguration.java
@@ -98,6 +98,8 @@
addHandlerIfNotNull(l, firebug);
Handler system = GWT.create(SystemLogHandler.class);
addHandlerIfNotNull(l, system);
+ Handler remote = GWT.create(SimpleRemoteLogHandler.class);
+ addHandlerIfNotNull(l, remote);
HasWidgets loggingWidget = GWT.create(LoggingPopup.class);
if (!(loggingWidget instanceof NullLoggingPopup)) {
addHandlerIfNotNull(l, new HasWidgetsLogHandler(loggingWidget));
diff --git a/user/src/com/google/gwt/logging/client/SimpleRemoteLogHandler.java b/user/src/com/google/gwt/logging/client/SimpleRemoteLogHandler.java
new file mode 100644
index 0000000..3970e19
--- /dev/null
+++ b/user/src/com/google/gwt/logging/client/SimpleRemoteLogHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.logging.shared.RemoteLoggingService;
+import com.google.gwt.logging.shared.RemoteLoggingServiceAsync;
+import com.google.gwt.logging.shared.SerializableLogRecord;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * A very simple handler which sends messages to the server via GWT RPC to be
+ * logged. Note that this logger should not be used in production. It does not
+ * do any intelligent batching of RPC's, nor does it disable when the RPC
+ * calls fail repeatedly.
+ */
+public final class SimpleRemoteLogHandler extends Handler {
+ private static Logger logger =
+ Logger.getLogger(SimpleRemoteLogHandler.class.getName());
+
+ class DefaultCallback implements AsyncCallback<String> {
+ public void onFailure(Throwable caught) {
+ logger.severe("Remote logging failed: " + caught.toString());
+ }
+ public void onSuccess(String result) {
+ if (!result.isEmpty()) {
+ logger.severe("Remote logging failed: " + result);
+ } else {
+ logger.finest("Remote logging message acknowledged");
+ }
+ }
+ }
+
+ private RemoteLoggingServiceAsync service;
+ private AsyncCallback<String> callback;
+
+ public SimpleRemoteLogHandler() {
+ service = (RemoteLoggingServiceAsync) GWT.create(RemoteLoggingService.class);
+ this.callback = new DefaultCallback();
+ }
+
+ @Override
+ public void close() {
+ // No action needed
+ }
+
+ @Override
+ public void flush() {
+ // No action needed
+ }
+
+ @Override
+ public void publish(LogRecord record) {
+ if (record.getLoggerName().equals(logger.getName())) {
+ // We don't want to propagate our own messages to the server since it
+ // would lead to an infinite loop.
+ return;
+ }
+ service.logOnServer(new SerializableLogRecord(record), callback);
+ }
+}
diff --git a/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
new file mode 100644
index 0000000..b68f281
--- /dev/null
+++ b/user/src/com/google/gwt/logging/server/RemoteLoggingServiceImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.server;
+
+import com.google.gwt.logging.shared.RemoteLoggingService;
+import com.google.gwt.logging.shared.SerializableLogRecord;
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import java.util.logging.Logger;
+
+/**
+ * Server side code for the remote log handler.
+ */
+public class RemoteLoggingServiceImpl extends RemoteServiceServlet implements
+ RemoteLoggingService {
+ private static Logger logger = Logger.getLogger("gwt.remote");
+
+ public final String logOnServer(SerializableLogRecord record) {
+ try {
+ logger.log(record.getLogRecord());
+ } catch (RuntimeException e) {
+ String exceptionString = e.toString();
+ String failureMessage = "Failed to log message due to " + exceptionString;
+ System.err.println(failureMessage);
+ e.printStackTrace();
+
+ // Return the exception description so that the client code can
+ // print or log it if it wants.
+ return e.toString();
+ }
+ return "";
+ }
+}
diff --git a/user/src/com/google/gwt/logging/shared/RemoteLoggingService.java b/user/src/com/google/gwt/logging/shared/RemoteLoggingService.java
new file mode 100644
index 0000000..2017dc0
--- /dev/null
+++ b/user/src/com/google/gwt/logging/shared/RemoteLoggingService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.shared;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+/**
+ * The client side stub for the logging RPC service.
+ */
+@RemoteServiceRelativePath("remote_logging")
+public interface RemoteLoggingService extends RemoteService {
+ String logOnServer(SerializableLogRecord record);
+}
diff --git a/user/src/com/google/gwt/logging/shared/RemoteLoggingServiceAsync.java b/user/src/com/google/gwt/logging/shared/RemoteLoggingServiceAsync.java
new file mode 100644
index 0000000..4d10df1
--- /dev/null
+++ b/user/src/com/google/gwt/logging/shared/RemoteLoggingServiceAsync.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.shared;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * The async counterpart of <code>RemoteLoggingService</code>.
+ */
+public interface RemoteLoggingServiceAsync {
+ void logOnServer(SerializableLogRecord record, AsyncCallback<String> callback);
+}
diff --git a/user/src/com/google/gwt/logging/shared/SerializableLogRecord.java b/user/src/com/google/gwt/logging/shared/SerializableLogRecord.java
new file mode 100644
index 0000000..28b5c65
--- /dev/null
+++ b/user/src/com/google/gwt/logging/shared/SerializableLogRecord.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.shared;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+/**
+ * A representation of a LogRecord which can be used by GWT RPC. In addition
+ * to the fact that LogRecord is not serializable, it has different
+ * implementations on the server and client side, so we cannot pass it over
+ * the wire directly.
+ */
+public class SerializableLogRecord implements IsSerializable {
+ private String level;
+ private String loggerName = "";
+ private String msg;
+ private long timestamp;
+ private SerializableThrowable thrown = null;
+
+ protected SerializableLogRecord() {
+ // for serialization
+ }
+
+ /**
+ * Create a new SerializableLogRecord from a LogRecord.
+ */
+ public SerializableLogRecord(LogRecord lr) {
+ level = lr.getLevel().toString();
+ loggerName = lr.getLoggerName();
+ msg = lr.getMessage();
+ timestamp = lr.getMillis();
+ if (lr.getThrown() != null) {
+ thrown = new SerializableThrowable(lr.getThrown());
+ }
+ }
+
+ /**
+ * Create a new LogRecord from this SerializableLogRecord.
+ */
+ public LogRecord getLogRecord() {
+ LogRecord lr = new LogRecord(Level.parse(level), msg);
+ lr.setLoggerName(loggerName);
+ lr.setMillis(timestamp);
+ if (thrown != null) {
+ lr.setThrown(thrown.getThrowable());
+ }
+ return lr;
+ }
+}
diff --git a/user/src/com/google/gwt/logging/shared/SerializableThrowable.java b/user/src/com/google/gwt/logging/shared/SerializableThrowable.java
new file mode 100644
index 0000000..5a90871
--- /dev/null
+++ b/user/src/com/google/gwt/logging/shared/SerializableThrowable.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.logging.shared;
+
+import com.google.gwt.user.client.rpc.IsSerializable;
+
+/**
+ * A representation of a Throwable which can be used by GWT RPC. Although
+ * Throwables are serializable, we don't want to use them directly in the
+ * SerializableLogRecord since including a class with so many possible
+ * subclasses will cause the client side JS to be very big.
+ */
+public class SerializableThrowable implements IsSerializable {
+ private String message;
+ private SerializableThrowable cause = null;
+ private StackTraceElement[] stackTrace;
+
+ protected SerializableThrowable() {
+ // for serialization
+ }
+
+ /**
+ * Create a new SerializableThrowable from a Throwable.
+ */
+ public SerializableThrowable(Throwable t) {
+ message = t.getMessage();
+ stackTrace = t.getStackTrace();
+ if (t.getCause() != null) {
+ cause = new SerializableThrowable(t.getCause());
+ }
+ }
+
+ /**
+ * Create a new Throwable from this SerializableThrowable.
+ */
+ public Throwable getThrowable() {
+ Throwable t;
+ if (cause != null) {
+ t = new Throwable(message, cause.getThrowable());
+ } else {
+ t = new Throwable(message);
+ }
+ t.setStackTrace(stackTrace);
+ return t;
+ }
+}