Re: Making RPC access logs intelligible
Jeff, You could create a common subclass (let's call it JeffRemoteServiceServlet) of RemoteServiceServlet that overrides the onAfterRequestDeserialized(RPCRequest) method. That method is called, as the name implies, everytime the servlet receives an RPC request, so it's the perfect spot to collect information for logging. That doesn't get you the timing information you're after, though, so you could instead override RemoteServiceServlet.processCall(String) to 1. Start a timer (i.e., record System.nanoTime() or whatever) 2. Call super.processCall 3. Stop the timer Now, so long as all of your service implementations override JeffRemoteServiceServlet, you've got the desired behavior everywhere. I'm not sure where the best spot to do the actual averaging would be, but this should at least let you hook in at the right spot. Another (much more difficult) option, that you and Philippe have alluded to, is to call setServiceEntryPoint client-side and pipe all of your RPC requests through a single servlet. You can make this happen automatically, or even completely take over the RPC transport process by creating a custom proxy generator. We've done this, and as you mentioned, it's a little difficult to wrap your head around at first, but once you have, the changes are actually pretty minor (unless you decide to start adding features like batching, etc.). You'd need: 1. A new subclass of RemoteServiceProxy (let's call it DispatchedRemoteServiceProxy) that overrides doInvoke() to take control of the transport of the request 2. A new subclass of ProxyCreator (let's call it DispatchedRemoteProxyCreator) that overrides getProxySupertype() to return DispatchedRemoteServiceProxy.class 3. A new subclass of ServiceInterfaceProxyGenerator (let's call it DispatchedRemoteServiceGenerator) that overrides createProxyCreator() to return a new DispatchedRemoteProxyCreator 4. Have your services inherit a new interface (DispatchedRemoteService) and set up your .gwt.xml file generate impls of that interface with DispatchedRemoteServiceGenerator. The trickiest part is what to do in DispatchedRemoteServiceProxy.doInvoke(). You'll want to make a request to your special dispatch servlet, of course, and then decode it once you get a response. RequestCallbackAdapter holds the keys to making this happen. Dispatching the request server-side is relatively trivial once you have the decoded RPCRequest. The point of all this is of course to have all your requests one through a single servlet, which would, among other things, probably make collecting information about requests a little easier. -Kelsey -- You received this message because you are subscribed to the Google Groups Google Web Toolkit group. To post to this group, send email to google-web-toolkit@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
Re: Making RPC access logs intelligible
Philippe, Kelsey - thanks for the very helpful responses. This sort of additional proxy behavior feels like something that would be of considerable value as a standard part of GWT. I'm surprised there aren't considerably more complaints in this group. setServiceEntryPoint() was the magic button I was looking for. However, I ended up coming up with an alternative that fits my (heavily Guice-dependent) architecture a little better. Since your responses were so good (I wish I could give you stackoverflow points), I feel obligated explain what I did. This involves a larger number of artifacts than I would prefer, but it feels fairly natural to me: * I derived a new GuiceRemoteServiceServlet that overrides processCall() to look up the interface in the Guice injector and execute code on throwaway objects. * I created separate interface, interfaceAsync, and implementation classes for every single method. Each interface has a different @RemoteServiceRelativePath. Some of the impl classes handle multiple interfaces when there is appropriate shared logic. * I map the interfaces to implementation in GuiceConfig. * I created a Gin singleton that implements all the async interfaces and proxies to the individual gwt-rpc proxies; this keeps my client code blissfully ignorant of the complexity. It's a little painful to add an RPC method: * Create new interface and interfaceAsync * Implement the interface with a new impl class or on an existing impl class * Map the interface to impl in Guice * Add the async interface to the Gin singleton proxy On the other hand my biz logic is now the way I want it to be - wrapped up in small, modular, reusable, appropriately scoped, guice-injected chunks. Parameter/method changes refactor nicely with Eclipse. I could eliminate the mapping step with annotations if I was willing to accept the startup time penalty of classpath scanning, and I could probably even generate the Gin singleton if I wanted. But my remote interface doesn't change that often. This may not be for everyone, but for a DI addict like me it seems pretty reasonable. And my appengine logs are now beautiful again - current load, errors, and individual log entries are now broken down by method call. Jeff On Wed, Mar 30, 2011 at 4:33 AM, Kelsey Francis kelseyfran...@gmail.com wrote: Jeff, You could create a common subclass (let's call it JeffRemoteServiceServlet) of RemoteServiceServlet that overrides the onAfterRequestDeserialized(RPCRequest) method. That method is called, as the name implies, everytime the servlet receives an RPC request, so it's the perfect spot to collect information for logging. That doesn't get you the timing information you're after, though, so you could instead override RemoteServiceServlet.processCall(String) to 1. Start a timer (i.e., record System.nanoTime() or whatever) 2. Call super.processCall 3. Stop the timer Now, so long as all of your service implementations override JeffRemoteServiceServlet, you've got the desired behavior everywhere. I'm not sure where the best spot to do the actual averaging would be, but this should at least let you hook in at the right spot. Another (much more difficult) option, that you and Philippe have alluded to, is to call setServiceEntryPoint client-side and pipe all of your RPC requests through a single servlet. You can make this happen automatically, or even completely take over the RPC transport process by creating a custom proxy generator. We've done this, and as you mentioned, it's a little difficult to wrap your head around at first, but once you have, the changes are actually pretty minor (unless you decide to start adding features like batching, etc.). You'd need: 1. A new subclass of RemoteServiceProxy (let's call it DispatchedRemoteServiceProxy) that overrides doInvoke() to take control of the transport of the request 2. A new subclass of ProxyCreator (let's call it DispatchedRemoteProxyCreator) that overrides getProxySupertype() to return DispatchedRemoteServiceProxy.class 3. A new subclass of ServiceInterfaceProxyGenerator (let's call it DispatchedRemoteServiceGenerator) that overrides createProxyCreator() to return a new DispatchedRemoteProxyCreator 4. Have your services inherit a new interface (DispatchedRemoteService) and set up your .gwt.xml file generate impls of that interface with DispatchedRemoteServiceGenerator. The trickiest part is what to do in DispatchedRemoteServiceProxy.doInvoke(). You'll want to make a request to your special dispatch servlet, of course, and then decode it once you get a response. RequestCallbackAdapter holds the keys to making this happen. Dispatching the request server-side is relatively trivial once you have the decoded RPCRequest. The point of all this is of course to have all your requests one through a single servlet, which would, among other things, probably make collecting information about requests a little easier. -Kelsey -- You received
Making RPC access logs intelligible
GWT's RPC system produces distinctly unfriendly access logs. When I look at my logs (especially the GAE dashboard), every RPC basically boils down to a single line item for the whole servlet (and all the various RPC methods): /mymodule/myservletNNN requests avg MMM ms each I really want this broken down by the method that gets called: /mymodule/myservlet/login NNN requests avg MMM ms each /mymodule/myservlet/doSomething NNN requests avg MMM ms each /mymoudle/myservlet/doOtherThing NNN requests avg MMM ms each How can I make this happen elegantly? All I can think of is to make separate servlets for each method. This is tedious. Even my minimal API still has 14 different methods. Create 14 servlets, 14 RemoteService interfaces, 14 Async interfaces? Yuck. Even if I made a single servlet that implemented all those interfaces and was mapped to 14 different URLs, it's still a PITA. Is there any other solution? It may seem trivial, but this is really a critical issue. It's hard enough to measure that an application is doing even with well-organized log messages. GAE's Appstats is almost unreadable right now. Thanks in advance, Jeff -- You received this message because you are subscribed to the Google Groups Google Web Toolkit group. To post to this group, send email to google-web-toolkit@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
Re: Making RPC access logs intelligible
Browsing through the GWT code some more, it doesn't look promising. Is there any way to look at the output of the RpcProxyCreator? Reading code is ok, reading code that generates code tortures my brainmeats. Jeff On Tue, Mar 29, 2011 at 2:28 PM, Jeff Schnitzer j...@infohazard.org wrote: GWT's RPC system produces distinctly unfriendly access logs. When I look at my logs (especially the GAE dashboard), every RPC basically boils down to a single line item for the whole servlet (and all the various RPC methods): /mymodule/myservlet NNN requests avg MMM ms each I really want this broken down by the method that gets called: /mymodule/myservlet/login NNN requests avg MMM ms each /mymodule/myservlet/doSomething NNN requests avg MMM ms each /mymoudle/myservlet/doOtherThing NNN requests avg MMM ms each How can I make this happen elegantly? All I can think of is to make separate servlets for each method. This is tedious. Even my minimal API still has 14 different methods. Create 14 servlets, 14 RemoteService interfaces, 14 Async interfaces? Yuck. Even if I made a single servlet that implemented all those interfaces and was mapped to 14 different URLs, it's still a PITA. Is there any other solution? It may seem trivial, but this is really a critical issue. It's hard enough to measure that an application is doing even with well-organized log messages. GAE's Appstats is almost unreadable right now. Thanks in advance, Jeff -- You received this message because you are subscribed to the Google Groups Google Web Toolkit group. To post to this group, send email to google-web-toolkit@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
Re: Making RPC access logs intelligible
The command pattern included in GWT-Platform gives a different path to each command and yield very readable logs in GAE. The trick is to call setServiceEntryPoint from the client async service implementation. If you want to see how we do it in GWTP check out: http://code.google.com/p/gwt-platform/source/browse/gwtp-core/gwtp-dispatch-client/src/main/java/com/gwtplatform/dispatch/client/DefaultDispatchAsync.java#70 Cheers! Philippe -- You received this message because you are subscribed to the Google Groups Google Web Toolkit group. To post to this group, send email to google-web-toolkit@googlegroups.com. To unsubscribe from this group, send email to google-web-toolkit+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.