How to show TypeScript source in Firebase Cloud Function callable stacktraces?
Asked Answered
M

2

6

I have a set of Cloud Functions that perform CRUD-like functions to get an individual resource, list resources and so forth, getWidgetByURL, listWidgets, deleteWidget.

For a broader context, these are written in a single src/service.ts file and the src/index.ts exposes a set of callables:

import * as functions from 'firebase-functions'
import * as service from './service'

const region = 'europe-west1'

exports.addJob = functions.region(region).https.onCall(async (data, context) => {
  try {
    functions.logger.debug('addJob called with data', data)
    const job = await service.addJob(data.title, data.company,
      data.location, data.applyUrl, data.salary, data.tags)
    return job
  } catch (err) {
    functions.logger.error(err)
    throw new functions.https.HttpsError('internal', 'internal server error', err)
  }
})

During the development cycle I run npm run build locally to compile to JavaScript into the target lib directory. Note the *.map files are produced.

enter image description here

In production, if a runtime error occurs, the stacktrace shown within Firebase console logs shows only the .js files callstack.

enter image description here

The debug process involves having to locate the runtime error in my local lib/service.js file and then by manually find the corresponding line in the corresponding source code lib/service.ts file. Tedious.

Is it possible for stacktraces to automatically make use of the .map files to produce something more useable? If not, what is the best practice/workflow?

Maharajah answered 6/8, 2020 at 11:24 Comment(0)
I
7

We use node-source-map-support for this. I tried this just a few minutes ago and it worked just fine.

Assuming you're using NPM, run npm install source-map-support. (Not --save-dev of course, you'll need it at runtime).

In index.ts, import source-map-support/register early, preferrably first thing, like so:

// index.ts
import "source-map-support/register";

It doesn't much matter how this compiles. As far as I can tell, the call really just needs to happen in the main module there, and it compiles out early enough to make the magic happen.

Now, to test! As per this Firebase doc, as long as the logger.error call includes an Error object, we get the stack printout (handy for testing). Logging an error like so, then:

// foo.ts
import * as functions from "firebase-functions";
const { logger } = functions;

...

const error = new Error("This should log.");
logger.error(error);

This compiles from line 83 in foo.ts to line 54 in foo.js.

And once triggered we get this in the Firebase functions console:

enter image description here

As you can see, the filename and line number point correctly to line 83 the .ts source file!

Since this has to do with logging, I should note that because we use Firebase Cloud Functions on the Node.js 10 runtime, the old console.log syntax is kinda spoopy. So in our tsconfig.json file, we get to have lib: ["es2018"], target: "es2018", but we also need to use functions.logger.* to get the same behavior as we got with console.*. As usual, throwing any old Error object will do as well.

I found this article handy for the Node.js 10 migration. I'll include it here in case that ends up being relevant for you with this.

Irv answered 13/9, 2020 at 3:17 Comment(0)
M
5

You can use the module source-map-support. Just install the module with npm, then put one line of code at the top of your index.js.

require('source-map-support').install();
Moira answered 6/8, 2020 at 15:39 Comment(1)
That's the compiled index.js, right? For sure not index.ts? I'd like to have compilation and upload happen in one build step if it's possible, without having to manually build, add a line to the output, then deploy.Irv

© 2022 - 2024 — McMap. All rights reserved.