Unhandled exceptions in Google Apps Script - javascript

I have created a public Web App with access to my private spreadsheet data. I can catch and log exceptions intry..catch, but:
is it possible to catch all unhandled exceptions, like browsers window.onerror?
can I view logs of unhandled exceptions somewhere?
by exceptions like "Service invoked too many times" my app is even not getting run, so here I definitely can`t handle the exceptions. Is there logs with such kind of exceptions?
These are so simple questions, so that I'm bit confused to ask them, but after hours of research I could not find the answers.
Thank you in advance.

These are issues that are being addressed currently. Right now in the Apps Script Early Access Program are two new additions that handle these cases. The first is native integration with stackdriver logging and the addition of google.script.run.withLogger().
First off for now you need to apply for the EAP:
https://developers.google.com/apps-script/guides/apps-script-eap
Stackdriver Logging:
To log to stackdriver the console object has been added to the server side.
code.gs
console.log('This will log to stackdriver')
Check out the docs for all the methods of console.
https://developers.google.com/apps-script/guides/logging#stackdriver_logging
Example from the docs:
function measuringExecutionTime() {
// A simple INFO log message, using sprintf() formatting.
console.info('Timing the %s function (%d arguments)', 'myFunction', 1);
// Log a JSON object at a DEBUG level. The log is labeled
// with the message string in the log viewer, and the JSON content
// is displayed in the expanded log structure under "structPayload".
var parameters = {
isValid: true,
content: 'some string',
timestamp: new Date()
};
console.log({message: 'Function Input', initialData: parameters});
var label = 'myFunction() time'; // Labels the timing log entry.
console.time(label); // Starts the timer.
try {
myFunction(parameters); // Function to time.
} catch (e) {
// Logs an ERROR message.
console.error('myFunction() yielded an error: ' + e);
}
console.timeEnd(label);
}
In addition you can also check Log Exceptions in the scripts properties. This will generate a stackdriver entry every time any error occurs in your script.
Error recovery in a web app
To recover in a web app from a failure you have access to the withFailureHandler() method found in the google.script.run object. With this you can register a callback in the event your script hits an exception.
Full documentation can be found at:
https://developers.google.com/apps-script/guides/html/reference/run
If you are doing server side checks with try...catch you may be getting an exception but gracefully handling it. In this case withFailureHandler() will not execute and onSuccessHandler() propably isnt the best place to handle errors. In the EAP there is now a withLogger method to google.script.run. For now there no documentation for google.script.run.withLogger(). I found it by digging through devtools. withLogger() allows you to register a function as a callback when ever a stackdriver entry is created. This is particularly helpful when you have log exceptions checked in your script properties. In this sense it is a bit like withFailureHandler() but it can be triggered by any stackdriver entry you add though the server-side console object.
index.html
<script>
google.script.run
.withSuccessHandler(function(){console.log('OK')})
.withFailureHandler(function(e){console.error(e)})
.withLogger(function(e){console.warn("The following log was generated:"+e)})
.serverFunctionCall();
</script>
code.gs
function serverFunctionCall(){
console.log("This log will generate a callback");
return true;
}

Try/catch at global scope will work, however any let/const container will not be globally exposed.
To fix this, you can use var within the try/catch at global scope

Related

Handling service bus error messages in azure function using javascript

I have an azure function using service bus topic trigger and I want to handle error messages gracefully, I want to be able to do an abandon of the message and pass the exception to it so I can see it in a property when I read the dead letters queue.
this is my code:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
// do something messy that may fail.
};
When my function fails with an exception the message goes to the DLQ as expected, but the problem is, that it doesn't save the exception thrown, it only tells you that it tried to execute the method 10 times and it couldn't.
What I want its to be able to catch the exception and add it to the message properties so, when I process the DLQ queue I will be able to know the reason for the error. Even more, as the code is failing with an exception I would like it to abandon from the first time it runs the message, so it doesn't have to retry 10 times.
I'm thinking something like this:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
try{
// do something messy and that may fail
}
catch(error){
context.bindingData.userProperties['DeadLetterReason'] = 'Internal server error';
context.bindingData.userProperties['DeadLetterErrorDescription'] = JSON.stringify(error);
context.bindingData.abandonMsg();
}
};
I haven't been able to find any documentation about something like this, so is it possible? or can I force the message to the DLQ? with something like this:
const serviceBusTopicTrigger: AzureFunction = async function(context: Context, mySbMsg: any): Promise<void> {
try{
// do something messy and that may fail
}
catch(error){
context.bindings.deadLetterQueue.userProperties['DeadLetterReason'] = 'Internal server error';
context.bindings.deadLetterQueue.userProperties['DeadLetterErrorDescription'] = JSON.stringify(error);
context.bindings.deadLetterQueue = mySbMsg;
}
};
Or finally and sadly do I have to manage my error directly from the method and maybe send it directly from there to an azure storage table or queue to notify errors?, I wouldn't like that because then I would be handling, both errors from the dead letter queue, and from my functions in different places. Is this the only way?
Any more ideas?
Thanks.
First of all, I think nodejs can not do this. nodejs lost the type information in C#, it should have been sent like this:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.servicebus.messaging.brokeredmessage.deadletterasync?view=azure-dotnet
When my function fails with an exception the message goes to the DLQ
as expected, but the problem is, that it doesn't save the exception
thrown, it only tells you that it tried to execute the method 10 times
and it couldn't.
Max Delivery Count is set to 10 by default when you create the subscription of the service bus topic. The default value is 10, and the minimum can be set to 1.
When you turn on the service bus information automatic completion, just like below:
host.json
{
"version": "2.0",
"extensions": {
"serviceBus": {
"messageHandlerOptions": {
"autoComplete": true
}
}
},
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 3.1.0)"
}
}
At that time, when an error occurs, the function run failed, call the abandon method, Delivery Count +1, and send the message back at the same time.
Then once you use try-catch, you will not retry 10 times at all. The function is regarded as a successful operation, function run success and the message disappears without sending it back.(That is, once try-catch you will only execute it once.)
I haven't been able to find any documentation about something like
this, so is it possible? or can I force the message to the DLQ?
No, it can't be done. The related type is missing and cannot be manually sent to dead-letter via JavaScript.
Or finally and sadly do I have to manage my error directly from the
method and maybe send it directly from there to an azure storage table
or queue to notify errors?, I wouldn't like that because then I would
be handling, both errors from the dead letter queue, and from my
functions in different places. Is this the only way?
Why do you say that the error still appears in the dead-letter? Using try-catch in the case of automatic completion should not be sent to dead-letter.

Erratic Google Cloud Function when running a shell script

I am trying to run a shell script on a Google Cloud Function.
I am using Node as the framework, and have tried different ways to run the script: execSync, execFileSync, spawnSync, etc:
module.exports.main = function() {
try {
const output = require('child_process').execSync(__dirname + '/run.sh', [],
{
stdio: 'inherit',
shell: '/bin/bash'
});
}
catch (error) {
process.exit(error.status);
}
}
The behavior I get is very erratic. Overall I think it worked once, but the rest of the time I either get a connection error and nothing happens, or the function terminates "successfully", but nothing from the shell script gets outputted, so I don't know whether it even ran or not.
To be more precise, the log lines I see when the function "fails" are only the following:
Function execution started
Function execution took 122 ms, finished with status: 'connection error'
I couldn't detect a pattern in when it fails and when it runs.
Exact reproduction steps:
Create a Google Cloud Function
set its runtime to Node 8
set its trigger to Pub/Sub topic
set its function to call to main
create a zip containing
index.js with the content above (module.exports.main = ...)
run.sh containing only
#! /bin/bash
echo "hi"
upload zip as source for Cloud Function
go to Testing tab of Cloud function and hit "Test this functions"
view logs
The solution I found was to switch to IBM Cloud Functions. They enable you to run a docker image, thus executing any binary is possible.
My suspicion as to why Google Cloud Function failed though, is that it probably ran out of memory. But that is just a guess

In designing a Javascript API, should I raise a TypeError or ignore with a console message?

I'm having to make a Javascript API that is the public interface to a C-based API that has enumerations and required parameters. Javascript, being a loosely typed language, wouldn't offer the same kind of compiler warnings one might get when using a C-based API.
My question is when making a Javascript wrapper around the C API, is it expected in the Javascript culture that one would receive a TypeError when passing invalid data or would it be more expected for the API to output a message to the console and ignore the error?
Here's some sample code for the API...
var Foo = (function() {
var foo = {
Enum: {
First: {value: 0},
Second: {value: 1},
Third: {value: 2}
},
bar: function(eenoom, aNumber) {
if (!eenoom) {
throw new TypeError("bar: You must specify the 'eenoom' parameter");
return;
}
if (!aNumber || typeof aNumber != 'number') {
throw new TypeError("bar: You must specify the 'aNumber' parameter (as a number)");
return;
}
for (var key in foo.Enum) {
if (foo.Enum.hasOwnProperty(key)) {
if (foo.Enum[key] === eenoom) {
console.log("You did it!");
return;
}
}
}
throw new TypeError("bar: You must use the Foo.Enum enumeration");
}
}
// So no one can mess with our enums
Object.freeze(foo.Enum);
return foo;
})();
Foo.bar(Foo.Enum.First, 20);
Foo.bar({value: 0}, 20); // Throws a TypeError
Note the use of TypeError. Is this an expected way for a Javascript API to behave?
I mainly do the following:
In synchronous public APIs I always check parameters and throw the appropriate error. Not only TypeError but also other errors as appropriate.
Generally I throw errors in exceptional cases - i.e. not fitting the thought-out execution flow.
In asynchronous APIs I report errors via an error callback/reject function.
I think console.log(...) and go on is not a good idea. If the call did not met the pre-conditions of the API, the API contract is broken from the very start. (Unless the contract is, specifically "yes, I can handle bad input" which is quite decadent.)
In this case nothing that you do afterwards will be correct. So throw an error and break off.
As an API consumer I'd be grateful for a thrown error with a good stack trace and a meaningful error message - much more that for just a meaningfull error message in the console (which I may or may not notice).
If the programmer has written code that will never work (not provided required parameters, sent the wrong type or order of parameters, etc...), then you want the code to fail immediately, fail visibly, clearly tell the programmer what is wrong and provide a stack trace that shows exactly where the offending code is.
The cleanest way to do that in Javascript is by throwing an appropriate type of error with a meaningful description. The very first time this code is executed, it will fail and the developer will immediately know they have done something wrong. This is what you want. Anything else increases the chances that the developer does not immediately realize the programming mistake they've made or even if they realize something isn't working, it might take them significantly longer to figure out why it isn't working.
A console.log() statement has these drawbacks:
It may not be seen immediately or at all.
It doesn't provide a stack trace so it may not be obvious what specific part of the caller's code is causing the error.
Since the app may not fail noticeably, it may appear that nothing is wrong or nothing serious is wrong when in reality there is a major programming mistake that must be fixed.

What is the difference between throw Error and console.error

What is the difference between these two statements, and is there a good reason to use one over the other?
throw Error("msg");
console.error("msg");
In my limited experience, I've only really seen throw Error() used. Is there any particular reason why?
Also, is there an equivalent to console.warn() in the same fashion?
throw ... raises an exception in the current code block and causes it to exit, or to flow to next catch statement if raised in a try block.
console.error just prints out a red message to the browser developer tools javascript console and does not cause any changes of the execution flow.
Some of the Differences are:
throw Error("msg"):
Stops js execution.
Mostly used for code handling purpose.
Can alter main flow of execution.
This syntax is mostly same for all browser as this is specified and validated by W3C.
console.error("msg"):
It just shows output in Red color at Browser console
It is mostly used to print values for debugging purpose.
Cannot harm main flow of execution.
This Syntax sometimes vary according to vendor browser and not standardized by W3C.
i.e. For IE accepted syntax is window.console.debug("msg")
Throw is for actually changing the control flow (jumping out of the current context and up to an error handler) for handling errors programmatically. The console statement is just for debugging and printing text to the error console. You may see them used in conjunction, for example:
var doSomethingDangerous = function(response) {
if (isMalformed(response)) {
throw Error('Response is malformed.');
}
process(response);
};
var sendAsyncRequest = function() {
var request = buildAsyncRequest();
request.sendThen(function (response) {
try {
doSomethingDangerous(response);
} catch (e) {
console.error(e);
doSomeAdditionalErrorHandling();
}
});
};

Why "fail" in promise does not catch errors?

I'm trying to access a file, that might not exist:
var localFolder = Windows.Storage.ApplicationData.current.localFolder;
localFolder.getFileAsync(stateFile).then(function (file) {
Windows.Storage.FileIO.readTextAsync(file).then(function (text) {
// do something with the text
});
}, function (err) {
// log error, load dummy data
});
if the file is not there, the "fail" method does get called, BUT it happens only AFTER my application halts with an exception "file not found". only when I press "continue" for the debugger does it continue to the "fail" method..
what am i doing wrong? should i check for existence beforehand?
You're seeing a first-chance exception. The debugger is breaking at the point of throw. If you hit the "continue" button on the debugger dialog, the app will keep running and the exception handler will run.
You can change this by using the "Debug|Exceptions" menu to turn off first chance exceptions.
I have been struggling with this same problem for two or three days and finally came to the following solution: I use getFilesAsync function to get the list of all files in a folder, and then look through this list to know if the needed file exists. After that I can call getFileAsyns without throwing. Not very pretty, but works for me. I agree that assuming that a developer can turn exceptions off is not the best advice... In my opinion this issue should be considered as a bug in RT implementation.

Categories