As mentioned in one of the comments above, you could wrap all functions in a try/catch to watch for errors and track them. There's a couple ways you could go about this programmatically, but there's some pitfalls to them.
The first option would be to take all the class functions in the constructor and re-assign them with a new, wrapped version of the function. There's a couple ways to do this, one where you explicitly do each function, and another where you iterate over all the keys and then wrap them, something along the lines of this:
function wrapFunction<T extends Function>(original: T) {
return ((...args: any[]) => {
try {
return original(...args);
} catch (e) {
// Do you logging here
// Then re-throw the error so that it can still be handled by consumers
throw e;
}
}) as unknown as T;
}
class MyClass {
constructor() {
// Iterate over all the keys,
for(const key in this) {
const value = this[key];
if(value !== "function") return;
// Wrap all the functions and assign them
this[key] = wrapFunction(value as Function) as any;
}
}
someFunction() {
throw new Error("Test error");
}
}
The downside here is that this is a little hard to type if you're using TS, and it can cause issues if there's things you don't want to wrap, or if people extend the class and do weird things with it (This can also end up attempting to wrap things that aren't actually class methods).
Another option is to use the new decorators to achieve the same wrapping, but this requires transpilation as decorators aren't at full support in most places yet. This allows you more control over which functions get wrapped and which ones don't, but again has issues if a consumer of your library extends or does weird stuff with your class.
The last option is to manually add that try/catch yourself to all the functions you want logged.