Apps Script meet Stackdriver

A brief detour

I have written a few Apps Scripts to solve “toil” problems. The first of these scripts I run prior to every review period. It greps through my emails for the past 6 months and derives a list of my most-frequent-recent email recipients. I then review this list to determine a short list of peer reviews. The second of these scripts iterates over my Gmail labels-folders to mark the “honestly, never gonna read” unreads as read.

Recently, I’ve discovered that Apps Script is integrated with Stackdriver Logging and Stackdriver Error Reporting. These appears to be unsung features of the platform so, let’s sing a little!

Setup

In truth, I only learned when writing this post that there’s a dashboard for Apps Script(s). I customarily access Apps Script through one of the G Suite apps and “Tools” then “Script Editor”. But:

https://script.google.com

Google Apps Script: who knew?

This highlights a useful point. That, while it appears Apps Scripts are embedded within G Suite documents, they are — in fact — associated with Google (Cloud Platform) Projects. We’ll see more of this very soon.

I created a “New Script”:

Google Apps Script: Blank Canvas

For the purposes of this post, I’m going to focus on generating errors :-) Some would say that I spend much of day on this task! My intent is to show the relationship between Apps Script and Google Projects and to show the awesomeness of Stackdriver Logging and Error Reporting.

Let’s make some errors!

Code.gs:

This code — if it didn’t have intentional errors — would report its start and end times (in Epoch milliseconds) to both Apps Script’s internal logger and to Stackdriver Loggging. Before it reports the end times, an error is thrown intentionally. This error should be reported to Stackdriver Error Reporting.

Let’s check

Save Code.gs in the Apps Script editor and, when prompted give it a Project Name. Let’s reference your choice for this name as YOUR-PROJECT-NAME. Nothing much appears to have happened.

But, oh so much has. All of Google’s services behave remarkably (it’s wonderful that they do but not surprising) consistently. Services may be grouped together often in the pursuit of some overall user-developed service. Google’s name for this aggregation is a Project. And the name that you just chose for YOUR-PROJECT-NAME is actually a Google Project Name. If you are familiar with Google Cloud Platform, you will be very familiar with Google Projects and with Project Names and IDs. We have just created a Google Project. Really? Yes.

Click “Resources” and then “Google Cloud Platform project…” and you should see something similar to:

Google Cloud Platform project

Let’s prove that it’s a Google Cloud Platform project, click the hyperlink under [YOUR-PROJECT-ID]] — project-id-1234567890123456789. And you should see:

Google Cloud Platform Console

The Project ID is generated for you. These must be globally unique. The Project ID is a more common unique identifier for projects. The hyperlink to the Cloud Console above, in this case would be:

http://console.cloud.google.com/project=project-id-1234567890123456789

Run the code and when it appears to finish, click “View”, “Logs”. You should see something similar to this:

Apps Script Logs

The Logger provides us with a timestamp but note that our logged entries begin [myFunction]. These correspond to the Logger.log(...) statements in our code. What about the console..log(...) statements?

Stackdriver Logging

Check “View”, “Stackdriver Logging”. You should see something similar to this:

Cloud Console: Stackdriver Logging

If you have Cloud SDK (aka “gcloud”) installed, you can use the following command. Replace the value of PROJECT_ID with the Project ID of your project. It has the form project-id-1234567890123456789:

PROJECT_ID="project-id-1234567890123456789"
FILTER="resource.type=\"app_script_function\" severity=\"DEBUG\""
gcloud logging read "${FILTER}" \
--project=${PROJECT_ID} \
--format=json \
| jq --raw-output ".[].textPayload"
[152urz1P2fYDq-l0m4iCGJqEFsa5mHayUrBUHgG7GsRY97lC7Out4ws42:myFunction] Ends  : 1.522194140884E12ms
[152urz1P2fYDq-l0m4iCGJqEFsa5mHayUrBUHgG7GsRY97lC7Out4ws42:myFunction] Starts: 1.522194140881E12ms
NB My apologies for resorting to (the awesome and generally applicable) jq tool in preference to using the gcloud format (which I can never remember) flag. I’ll work that out and update the post.

Update: here’s the way to perform the above log query using gcloud only:

gcloud logging read "${FILTER}" \
--project=${PROJECT_ID} \
--flatten="[]" \
--format="value(textPayload)"

Stackdriver Error Reporting

Back in the Apps Script editor, click “View” and then “Stackdriver Error Reporting”. If this is the first time that Error Reporting has been used in your project, you may see the following prompt. You do *not* need to do anything to enable Error Reporting beyond generating some errors. Run your code:

Cloud Console: Stackdriver Error Reporting

Once we’ve run the script containing the error, you should (!) see:

Cloud Console: Our First Error (Ever)!

You can drill-down into the Error(s) by clicking the relevant line:

Cloud Console: Detailed Error Details

Stackdriver Error Reporting is very powerful. Not only are we told that this error arose in line 11 (correct) in myFunction but we are also told the number of occurrences, the times of occurrence, the last seen etc. etc.

We can drill-down further into “Recent samples”:

I’ll leave you to explore further.

Conclusion

It’s not unusual for Google to underplay the value in its services. I’m a Googler (and try to be objective) and I’m still discovering these hidden gems. Apps Script is a powerful tool that builds upon JavaScript (albeit no lambdas and no funky template literals) but, if you can live with the slightly outdated runtime version, Google provides comprehensive APIs to G Suite apps *and* seamless integration with Stackdriver Logging and Stackdriver Error Reporting.

Feedback is always welcome.

That’s it!

Post Script

I revised the script to use Date.now().toString() to avoid the undesired numeric exponential formatting.

jshint is a useful tool for validating Apps Script (JavaScript) code as you’re developing:

jshint.com

Another — more widely known — Google tool is the Closure Compiler. The Closure Compiler “compiles JavaScript to better JavaScript”. I use it often when I write JavaScript because it takes my JavaScript and (really) makes it better. Let’s see what it does to this simple example:

Google Closure Compiler

What did it produce? The good news is, for something so simple, it mostly obfuscated variable names :-)

function myFunction() {
var a = Date.now().toString();
Logger.log("[myFunction] Starts: %sms", a);
var b = ScriptApp.getScriptId();
console.log("[%s:myFunction] Starts: %sms", b, a);
a = Date.now().toString();
Logger.log("[myFunction] Ends : %sms", a);
console.log("[%s:myFunction] Ends : %sms", b, a);
};

References

https://developers.google.com/apps-script/guides/logging

https://developers.google.com/apps-script/guides/logging#stackdriver_error_reporting