How to implement your Custom Logger in Google Script?!

By | February 7, 2016

Do you have created and published your own Web App with Google Script? Then you probably want to observe what is happening at runtime. You can of course use the Logger class for logging. However, this class doesn’t provide you the possibility to route the logging in a custom destination, e.g. into an own log file in your Google Drive. Therefore, I wrote my own Logger functionality.

While you cannot yet define your own class in Google Script, I start with an global object CustomLogger. As an attribute, I add the default name that should be used for the log file and a (still empty) log function.

var CustomLogger = {

logFileName:”My Log File”,

log:function(text) {
}

}

The first task is to find or create the log file of your web app. Once, I have created the log file I use the scripts properties to store the id of the file. This way, I can easily find the log file and continue logging when the web app is used again. I create a new file if I can’t find the ID in the properties.

However, the file could have been deleted meanwhile. Therefore, I have also to check if the file for the stored ID still exists and create a new file if not. At this point, it is important to update ID as well in the script properties because you will otherwise create a new file again and again.

var CustomLogger = {

  logFileName:”My Log File”,
propKeyLogFileID:”log_doc_id”,

log:function(text) {

// Retrieve the ID property for the Logfile
    var log_doc_id = PropertiesService.getScriptProperties().getProperty(this.propKeyLogFileID);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc_id == null) {
      log_doc_id = DocumentApp.create(this.logFileName).getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }
    
    // Retrieve the document from the ID
    var log_doc = DocumentApp.openById(log_doc_id);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc == null) {
      log_doc = DocumentApp.create(logFileName);
      log_doc_id = log_doc.getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }
  }
}

Now that I have created a way to retrieve the log file I can finally start to write log entries. Therefore, I create an entry that consists of the date and time, and the text that should be logged. Additionally, I add a line break so that every entry starts on a new line. Finally, I write the created entry to log file.

var CustomLogger = {

  logFileName:”My Log File”,
  propKeyLogFileID:”log_doc_id”,

  log:function(text) {
    
    // Retrieve the ID property for the Logfile
    var log_doc_id = PropertiesService.getScriptProperties().getProperty(this.propKeyLogFileID);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc_id == null) {
      log_doc_id = DocumentApp.create(this.logFileName).getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }
    
    // Retrieve the document from the ID
    var log_doc = DocumentApp.openById(log_doc_id);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc == null) {
      log_doc = DocumentApp.create(logFileName);
      log_doc_id = log_doc.getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }

// Construct log entry
    var logText = “[” + new Date().toLocaleTimeString() + “] ” + text + “\n”;
    
    // Write to the log file
    var logDocText = log_doc.getBody().editAsText().appendText(logText);
  }
}

Now, you can call the log function via CustomLogger.log(text) from everywhere in the scripts of your web app. However, you might want to distinguish the expected log entries and the errors you may log. The latter one you would probably like to expose more clearly in the log file. Therefore, I seperate the log function in a logWithColor function that can write colored log entries. You can color the text that you insert or append in a Google Document after you have inserted it.

Next to the log function, I also add a new functions to the CustomLogger object that logs errors. This function can be used when you reach the catch block of a try {} catch(e) {} statement.

var CustomLogger = {

  logFileName:”My Log File”,
  propKeyLogFileID:”log_doc_id”,
logColor:”#000000″,
  errorColor:”#FF0000″,

logWithColor:function(text, color) {

  // Retrieve the ID property for the Logfile
    var log_doc_id = PropertiesService.getScriptProperties().getProperty(this.propKeyLogFileID);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc_id == null) {
      log_doc_id = DocumentApp.create(this.logFileName).getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }
    
    // Retrieve the document from the ID
    var log_doc = DocumentApp.openById(log_doc_id);
    
    // Create a new document if an ID cannot be retrieved
    if (log_doc == null) {
      log_doc = DocumentApp.create(logFileName);
      log_doc_id = log_doc.getId();
      PropertiesService.getScriptProperties().setProperty(this.propKeyLogFileID, log_doc_id);
    }
    
    // Construct log entry
    var logText = “[” + new Date().toLocaleTimeString() + “] ” + text + “\n”;
    
    // Write to the log file
    var logDocText = log_doc.getBody().editAsText().appendText(logText);

// Set the color of the last log entry
    logDocText.setForegroundColor(logDocText.getText().length – 1 – logText.length, logDocText.getText().length – 1, color);

  },

  log:function(text) {

// Write to the log file
    this.logWithColor(text, this.logColor);
  },

logError:function(e) {
 
    // Construct logging text
    var errorLogText = “[Error in ” + e.fileName + ” in line ” + e.lineNumber + “]: ” +  e.message;
    
    // Write to the log file
    this.logWithColor(errorLogText, this.errorColor);
  }
}

This provides you basic functionalities to log normal behaviour and errors into a log file stored in your Google Drive. However, there are more ideas for reasonable extensions or changes that can be made easily. You can e.g.

  • Automatically add the calling user name to the log entry (if the web app is restricted to users who logged in and the user gives the permission) or
  • Write the log in a Google Spreadsheet instead of a Google Document in order to filter more easily.

Feel free to add more ideas, comments, improvements, etc. in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *