Header Sep
Symbian Tips, Tricks & Code
My Rating Score
Login to rate page

October 2003
Compliments of Carmichael Data Systems Ltd: Remote Logging in pJava with UIQ Smartphones

[Back]

Watching your application's internals across the Internet, Java programmers are familiar with the standard logging feature System.out.println(text). This is useful in an emulator environment but increasingly applications are becoming Internet aware and need a way to monitor activity on target. One approach is to wrap the standard logging call into a common routine that can be directed to various destinations, all from the device itself. This code is in PersonalJava for UIQ, but a J2ME version is very similar and the changes required for this are indicated.

// Example application
public class MyApp extends Frame implements ActionListener {

  // Flags to control logging, managed by some means in code
  private boolean logToRemote;
  private boolean logToFile;
  private boolean logToStdout;

  ...

  public void log(String text) {
    if (logToRemote) {
      try {
        Http.post("
http://www.myloggingsite.com/monitor", "log="+text);
      } catch (Exception e) {
      }
    }
  }

  if (logToFile) { // Not possible on J2ME
    BufferedWriter bw = null;
    try {
      bw = new BufferedWriter(new FileWriter("MyApp.log"), true));
      bw.write(message);
      bw.newLine();
      bw.flush();
    } catch (IOException ioe) {
      ioe.printStackTrace();
    } finally {
      // always close the file
      if (bw != null) {
        try {
          bw.close();
        } catch (IOException ioe2) {
          // just ignore it
        }
      }
    }
  }
  if (logToStdout) {
    System.out.println(text);
  }
  return;
}

// Utility class to POST data to a url
public class Http throws Exception {
  public static String post(String url, String data) throws Exception {
    String response="";
    try {
      URL targetUrl = new URL (url);
      HttpURLConnection uc
       = (HttpURLConnection) targetUrl.openConnection();

      uc.setRequestMethod("POST");
      uc.setAllowUserInteraction(false);
      uc.setDoInput(true);
      uc.setDoOutput(true);
      uc.setUseCaches(false);
      uc.setRequestProperty("Content-length", ""+data.length());
      PrintWriter out= new PrintWriter(uc.getOutputStream(), true);
      out.println(data);
      BufferedReader in
       = new BufferedReader(new InputStreamReader(uc.getInputStream()));
      uc.connect();
      String newLine="";
      try {
        while ((newLine=in.readLine())!=null) {
          response=response+newLine+"\n";
        }
      } catch (IOException ioe) {
      }
      uc.disconnect();
      uc = null;
      in.close();
      out.flush();
      out.close();
    } catch (Exception e) {
      throw e;
    }
    return response.trim();
  }
}

Now, all that needs to be done is to ensure all logging calls go through MyApp.log(). The flags to control logging could be set via e.g. debug menu options. HTTP POST is used rather than a datagram socket (or other means), as it is the only protocol mandated in the MIDP 1.x specification, and will be more widely compatible. The logs made to a local file open and close it each time – this is so there is something to examine in case the program exits unexpectedly. Remote logging will obviously slow down the applications operation, but used sensibly, it can give a valuable "real time" insight into the workings of an application. Any kind of logging calls can be made, for example it is very interesting to see memory usage remotely as a user moves through a menu structure:

// Using the logger
public class MyAppSub {

  private final SimpleDateFormat
  todayDateFormatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss");
  MyApp app;

  public MyAppSub(MyApp app) {
    this.app = app;
  }

  private String getFormattedDateTime() {
    Date today = new Date();
    return new String(todayDateFormatter.format(today));
  }

  public void actionPerformed( ActionEvent ae ) {
    app.log("MyAppSub::actionPerformed() Entry "+getFormattedDateTime()
     + " "+Runtime.getRuntime().freeMemory());
    long start = System.currentTimeMillis();

    // Code to analyse "ae" and perform various operations...

    ...

    // Then, on leaving the event handler report stats

    long end = System.currentTimeMillis();
    long elapsed = (end - start) / 1000;
    int mins = (int) elapsed / 60;
    int secs = (int) elapsed % 60;
    app.log("MyAppSub::actionPerformed() Exit ("+mins+"m"+secs+"s) "
      + getFormattedDateTime()+" "+Runtime.getRuntime().freeMemory());
  }
}


Finally, a way of monitoring the calls to the remote server is needed. Using an appropriately configured J2EE server, this could just be a tail running on its log file. The monitor servlet need do no more than the log operation itself:

public class MonitorServlet extends HttpServlet {

  public void doPost (HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {
      System.out.println(req.getParameter("log"));
    }
  }

}

So there we have it - just remember to disable it in production code, or your users really will have "Big Brother" watching them!

 

 

My Rating Score
Login to rate page