New Posts New Posts RSS Feed: Writing to Server Debug Log from Client
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Writing to Server Debug Log from Client

 Post Reply Post Reply
Author
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Topic: Writing to Server Debug Log from Client
    Posted: 18-Sep-2009 at 12:13am
I've been asked a few times (e.g., here) about how to use the DevForce Debug and Trace facilities to put client-side messages in the server-side log.
 
There are lots of ways to go about it but I think one of the easiest is to use the DevForce InvokeServerMethod to pass a message to the middle tier server (our BOS) which can post the message to the server-side DebugLog on the client's behalf.
 
You won't have to do any fancy configuration; there is no new WCF channel to configure, open, or close. You'll be using the same pipeline DevForce established for sending entities back and forth.
 
This is actually a nifty example of using the DevForce ServerMethod; I hope it will inspire you to think of other uses. You should read up on it in the Developer's Guide.
 
Before I show you the code, I want to warn you not to be too chatty in production. Every call consumes some server cycles that will not be available for more important needs. The more likely risk is that you blow up the server log with a lot of noise. You'll want to take a measured approach here.
 
With that advisory out of the way, I proceed.
 
ServerLogger on the Middle Tier (BOS)
 
Let's start with the Server where we will create a static class, ServerLogger, and give it a single static method, Log.
 
We have to think about where to put this. It could go in its own assembly, in which case you have to remember to deploy it to the server and remember to add that assembly to the Probe Assemblies in your server configuration file (typically a web.config).
 
I'm lazy so I'll put mine in the model assembly (called "ModelExplorer.Model" in my example) because that is sure to be deployed to the server and is already named in the Probe Assemblies of the configuration file.
 
Our "Log" method must have the signature of a DevForce "IdeaBlade.EntityModel.ServerMethodDelegate". It looks like this:
namespace DomainModel {
  public static class ServerLogger {
 
    [IdeaBlade.EntityModel.AllowRpcAttribute]
    public static object Log(
      System.Security.Principal.IPrincipal principal,
      IdeaBlade.EntityModel.EntityManager serverEntityManager,
      params object[] args) {
 
      if (args.Length == 0) return null;
      var msg = string.Format("Client message: '{0}'", args[0]);
 
      IdeaBlade.Core.TraceFns.WriteLine(msg);
      return null;
    }
  }
}
I highlighted the things I want to talk about in blue.
 
The namespace could be anything but I have to take note of it because I'll need to know it when I construct my client call.
 
The class (and its methods) must be static. I don't like statics either but that's the way it works.
 
Be sure to keep the class stateless. That will forestall threading nightmares and help keep your server light.
 
The log method must carry the AllowRpc attribute or DevForce won't call it. This is partly a security measure to ensure that methods which happen to have a conforming signature are not callable from the client by accident.
 
Look at the good stuff we pass into your method. You get the Principal of the client user. Your method can determine by checking principal.IsInRole("xxx") if this client has the right to call your method.
 
We also give you an empty but logged-in EntityManager in case your method wants to perform some DevForce persistence operations on the server. We won't need it for our logger.
 
Finally, there are the "args" which hold the information passed to us from the client. In our example, we expect the message to be a string in the first (and only) arg; we dress it up and pass it along to the DevForce TraceFns ... as if we had composed it on the server to begin with.
 
There is not much type-safety here; you'll have to check that you received args and that they are of the expected type. It is up to you to ensure that all objects passed from the client are serializable and understood on the server.
 
Notice that we return null. You can return any serializable object that the client understands. We have nothing to send back so we return null.
 
Invoke the ServerLogger from the Client
 
In my example, I will log a message in the server DebugLog whenever I attempt a query on the client.
 
I've jacked into the QueryRepository class in ModelExplorer.Explorer project of my Silverlight PrismExplorer demo where I have a collection of query "key" names and their associated LINQ queries. Just before I ask DevForce to run the query, I call the following method:
LogToServer("Query called: " + queryKey;
That's it. Construct the message from the "queryKey" and we're done!  Ok ... here's the guts of LogToServer:
private void LogToServer(string message) {
  
  Manager.InvokeServerMethodAsync(
    "DomainModel.ServerLogger,ModelExplorer.Model", // Assembly-qualified name for the server class
    "Log", // name of the server method
    delegate {}, // callback (here a "do nothing" delegate)
    null, // state object to identify this particular call (we don't care)
    message);
}
The method does need access to a logged in and connected EntityManager.
 
We are using the async form of InvokeServerMethod here. On a regular .NET client we could use the synchronous version which would block the client calling thread until the server responded. That's not allowed in Silverlight so we don't bother offering it.
 
In practice, no matter what the client environment, you may still prefer the async approach for sending a message to the server; why block when you don't really care about the server response?
 
The trickiest part of this is getting the ServerLogger class name right.
 
I am assuming that you don't have access to the ServerLogger type on the client (generally true of server method classes) so you can't use the simpler overload in which you just supply the method delegate (e.g., ServerLogger.Log). You'll have to describe the type and method with two strings.
 
First you specify the class; you have to use the "assembly-qualified" name to identify it. That name is of the form, "namespace.typename,assemblyname", which in our example is "DomainModel.ServerLogger,ModelExplorer.Model".
Remember from our discussion above that we slipped our server class into the same assembly that holds the domain model on the server. You wouldn't have to do it the lazy way as I have here.
We must specify a callback for when the async method returns. We don't really care when it returns or what the server returns so we'll give it a "do nothing" delegate; the expression "delegate {}" is a quick-and-dirty way of writing a delegate that takes any parameters and does nothing.
 
We don't care to distinguish one invocation from another so we'll give it a null "state" object.
 
Finally, the meat: we send the message which is simply a string. This (and all arguments) passed to the server must be serializable ... which strings are.
 
Again, no type safety here. That's why I wrapped the particulars in this type-safe, general purpose method which, in a real application, would be made available everywhere via a client-side "service".
 
Results
 
After running the application and my first query, the relevant portion of the server-side DebugLog looks like this:


 
I highlighted the "IdeaBladeConfig resoution so you'd know we are looking at the server-side log.
 
I highlighted the entry for the client message which shows the user name of the client (this came from the Principal), the server logging method, and the message itself.
 
Conclusion
 
DevForce offers an extremely simple mechanism for sending serializable material to (and from) your custom server-side method. You don't have to configure anything to use it because you're using the same client/server pipeline that DevForce employs for its own purposes.
 
Your server method is given the client's Principal so you can impose the authorization rules you deem appropriate. You could use this same basic approach to write a server-side method that routed client traffic to anything your server can reach. The BOS becomes a service hub.
 
In this example, we've put EntityManager.InvokeServerMethod to use as a simple way to pass messages from the client to the server-side log.


Edited by WardBell - 18-Sep-2009 at 12:15am
Back to Top
mamadero View Drop Down
Newbie
Newbie
Avatar

Joined: 28-Dec-2009
Posts: 2
Post Options Post Options   Quote mamadero Quote  Post ReplyReply Direct Link To This Post Posted: 28-Dec-2009 at 6:21pm
Ward,

I think a bit more is needed. I'd expect DevForce to cache those logging messages and send them in batch since I don't care about the response and the timing isn't critical I can't wait and that would make it more efficient. That would also allow us to log when working offline and then resume the connection if necessary. It would also be good to have a logging configuration in the server and only send the logging messages that match that configuration (e.g. only errors, debug or info). You can see clog.codeplex.com as an example. I think this would be a nice feature as part of DevForce following the same nice programming model.

Also it would be great if I could be logging to a different server to keep our app server free for real business logic.

The other thing, I don't mind how the communication and things are resolved under the covers, but probably DevForce could help to create the LogToServer method for the client, not a big deal, just a nice to have.


Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 04-Jan-2010 at 5:28pm

DevForce itself doesn't have a facility for logging client messages to the server.

What I showed you here is the bare, open-source minimum of a suggestion for doing this yourself in just a few lines. In the real world I would do just as you say and
 
(a) cache log messages in client memory
(b) provide a flush-to-server method
(c) consider storing on local disk (async of course) in case of crash
(d) consider a switch to force flush periodically
(e) consider log message filtering
 
These are all things you write yourself with little difficulty. In fact, it should be easy to plug in your own preferred logger that does all that you want on the client side. Then you have to decide how to propagate to the server; you might follow the path hinted at in my post.
 
Our InvokeServerMethod frees you from having to write all the configuration and hook up yourself. It does route through our BOS middle tier server. You won't know if there is a significant performance effect until you measure; then you can decide if this is really a problem. If you want to by-pass the BOS and log to a separate server, you're on your own..
 
Logging is not a central feature of DevForce. It's an important aid to development; it will not suit many production applications ... nor was that our intention. There are many stronger loggers out there to choose from. In fact, you might want to route our native DevForce server-side log messages to such a logger so you can consolidate feeds from the DevForce server with other log sources. The "how to" of that is a topic for another day (not soon).
 
I have lobbied (to myself) for decoupling DevForce from its own logger. It's nice that we have a logger but you should be able to substitute your own. However, when I put my PM hat on, I can't justify that effort (small as it is) in place of more urgent development. I may revisit that decision if we see enough demand; it stands for now.
 
Seems to me you have a good handle on what you want. I've given you some clues as to how you might proceed on your own. At a minimum, you've got a demo of InvokeServerMethod.
 
Cheers - W
Back to Top
mamadero View Drop Down
Newbie
Newbie
Avatar

Joined: 28-Dec-2009
Posts: 2
Post Options Post Options   Quote mamadero Quote  Post ReplyReply Direct Link To This Post Posted: 04-Jan-2010 at 8:28pm
Thanks Ward.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down