0

I am working on my own JavaScript console.log and have the following code, as a test working on Chrome, :

console.log(new Error().stack.replace(/\n/g, ''));

It produces the following:

Error    at http://www.mywebsite.com/sites/all/themes/mythemedir/js/page.js:353:15    at b.event.dispatch (http://www.mywebsite.com/sites/all/modules/jquery_update/replace/jquery/1.9/jquery.min.js?v=1.9.1:3:28337)    at b.event.add.v.handle (http://www.mywebsite.com/sites/all/modules/jquery_update/replace/jquery/1.9/jquery.min.js?v=1.9.1:3:25042)

I want to change /\n/g into whats needed to get each of the following results:

var errorPath = "http://www.mywebsite.com/sites/all/themes/mythemedir/js/";
var errorFile = "page.js";
var errorLoc = "353:15";
var errorLineNum = "353";
var errorColNum = "15";

For example, I have:

var errorFull = new Error().stack;
var errorPath = errorFull.replace(/(https?\:\/\/[^ ]*)/i, '');
var errorFile = errorFull.replace(/([^\\]+)\.js$/gi, '');
var errorLoc = errorFull.replace(/\n/g, '');
var errorLineNum = errorFull.replace(/\n/g, '');
var errorColNum = errorFull.replace(/\n/g, '');

Any ideas? I've read and have tested what I could from http://www.w3schools.com/jsref/jsref_obj_regexp.asp & https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp with varying results. var errorFile seems to crash and are the closest I've gotten to figuring it out from examples. Still haven't figured out a way to grab the text at the end of the first path on either and both sides of the colon (:).

Updates:

01. var errorPath is working, returning full match at the moment.

var errorPath = errorFull.replace(/(https?\:\/\/[^ ]*)/i, '');

Returns: "http://www.mywebsite.com/sites/all/themes/mythemedir/js/page.js:353:15"

02. var errorPath is working, returning only path:

var errorPath = errorFull.replace(/(https?\:\/\/[^ ]*)(?:\/)/i, '');

Returns: "http://www.mywebsite.com/sites/all/themes/mythemedir/js/"



Final Solution:

Finally figured it out with the understanding gained from the answers and help below! Thank you to @winner_joiner & @Tomalak for all of the resources and guidance! The final solution is as follows:

var errorData = /((?:https?\:\/\/[^\s]+)(?:\/))([^:]+).([^\D]+).([^\D]+)/i.exec((new Error()).stack);
var errorPath = errorData[1];
var errorFile = errorData[2];
var errorLoc = errorData[3] + ":" + errorData[4];
var errorLineNum = errorData[3];
var errorColNum = errorData[4];
console.log("errorPath: "+errorPath+", errorFile: "+errorFile+", errorLoc: "+errorLoc+", errorLineNum: "+errorLineNum+", errorColNum: "+errorColNum);

RegExr example in action global & non-global

The expression is broken up into 4 capturing groups: errorPath (Path), errorFile (File Name), errorLineNum (Line Number) and errorColNum (Column Number). The entire expression is not run globally, since the only URL in the error that references the location of the error is the first one, we only need to match the first set. So all that we are focusing on is:

http://www.mywebsite.com/sites/all/themes/mythemedir/js/page.js:353:15

errorPath (errorData[1]) (Path): ((?:https?\:\/\/[^\s]+)(?:\/))

This portion selects everything from http:// or https:// up to and including the last / resulting in: http://www.mywebsite.com/sites/all/themes/mythemedir/js/.

The s? in https? makes the s portion optional.

errorFile (errorData[2]) (File Name): ([^:]+)

This portion selects everything since the Path and the next (in this case also first) : resulting in: page.js.

errorLineNum (errorData[3]) (Line Number): .([^\D]+)

This portion skips any separating symbol (This case: :) & selects everything until the next non-digit (\D), resulting in: 353.

errorColNum (errorData[4]) (Column Number): .([^\D]+)

This portion skips any separating symbol (This case: :) & selects everything until the next non-digit (\D) again, resulting in: 15.

Demonstration can be found at RegExr.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • If you did try something *real*, why not post those attempts? Or do you expect a ready-to-use code? – Wiktor Stribiżew Oct 01 '15 at 11:11
  • I didn't want to confuse anyone by the faulty patterns I have, shouldn't have worried about confusing anyone though this is the place for figuring programming problems out. I cant get RegEx to work for me in the last 3 cases as I haven't found a pattern to grab text on either side of a colon (:). No, I'm not asking for ready to use code, I'm looking for an explanation as to how I would do this and why it would work, and why my patterns aren't. – Leviscus Tempris Oct 01 '15 at 11:32
  • 2
    You should use a tool like https://regex101.com/ to build your regex step by step. Start out with `.*` and keep refining it until it matches what you want. That will be way more fruitful than trial and error with the full expression in JS. – Tomalak Oct 01 '15 at 11:46
  • Thank you for the link @Tomalak! I'll continue my work on this and will update my question as I go. – Leviscus Tempris Oct 01 '15 at 11:56
  • Here's another hint: Be as specific as you have to be, but no more. For example, URLs can be mopped up with `\S+`, until you want to match only *specific* URLs. In this case here you want to match any URL, so `\S+` is probably fine. – Tomalak Oct 01 '15 at 12:13
  • Oh, and another one: Backtracking works *for* you, use it: `\S+:\d+` (Besides, you really don't want to throw away the newlines in the stacktrace. They are valuable information. Work with `Error().stack` directly.) – Tomalak Oct 01 '15 at 12:17
  • Thank you again. I'm new to RegExp and have been trying to figure this out all night where I'm at. Are you suggesting that I change the way I tell it to pull the first URL? I will play around with your hints and see how they work. – Leviscus Tempris Oct 01 '15 at 12:21

1 Answers1

1

This could be a quick solution, where you get the whole data with one expression

var errorData = /\s*at\s*(.*\/)([^\/:]+):([^:]+):([^:]+)\n/gi.exec((new Error()).stack);
var errorPath = errorData[1];
var errorFile = errorData[2];
var errorLoc = errorData[3] + ":" + errorData[4];
var errorLineNum = errorData[3];
var errorColNum = errorData[4];
// tested on Win7 with chrome 44+ 

Just an Hint: if you use the g modifier in a regex an call the function moretimes very time it gets the next find. detail to this you can find here Why RegExp with global flag in Javascript give wrong results?

Update 1:

\s*at\s* gets the first line with spaces and at and spaces

(.*\/) gets the url until to the last slash /

([^\/:]+) gets the file name

:([^:]+) gets the colon and anything until to the next colon

:([^:]+)\n gets anything from the colon until to the newline

Community
  • 1
  • 1
winner_joiner
  • 12,173
  • 4
  • 36
  • 61
  • Wow! That is almost perfect, all I need to do with this is make it select the first URL of the string. I'm working on that now. Through enough trial and error I should be able to figure it out. Love that we can create an array of matched objects and call for them like you have here! – Leviscus Tempris Oct 01 '15 at 13:08
  • 1
    I updated the code since my demo worked locally, now it should work with urls. (insted of an url it had ``):) – winner_joiner Oct 01 '15 at 13:31
  • Absolutely brilliant! There is only one problem, when there is `HTMLDocument.` returned with `errorData` the variable `errorData[4]` returns with a trailing parentheses `71)` How could we fix that? And what would you suggest as a good place to learn RegExp? Thank you so much for your help so far! – Leviscus Tempris Oct 01 '15 at 14:19
  • 1
    What excatly is caught, depence on the format of the data, you would have to ajust the regex. I think my answer covers the input data from your question and with my answer you should be able to solve the last issues. – winner_joiner Oct 01 '15 at 18:14
  • Thank you so much for your help! With your help and guidance of another I was able to make sense of everything and managed to get it to explicitly select the first entire error URL statement, and break the information up into its respective components. I tried to explain my final solution as best as possible for anyone else who may want to use this in their own project. – Leviscus Tempris Oct 02 '15 at 04:25
  • @LeviscusTempris, why did you remove the accepted? is my answer not more correct? – winner_joiner Oct 02 '15 at 18:53
  • you're not mistaken, I am just confused as to how I present the final solution to my answer. I supposed by providing my own answer and trying to point to it as the final solution to the question it must have unchecked yours while also not allowing me to do so within 24 hours. My apologies I'm new here. – Leviscus Tempris Oct 02 '15 at 21:34
  • As of right now my following answer is still not exactly what I needed. I need some way to be able to control which matching line of the stack globally is returned. Unless I can have a two dimensional array returned natively in the expression then I will need to create the array myself of each main capture group (The entire expression as a capture group) and then regex each of them and construct an array within each containing the smaller groups. So that I can call for errorData[2][1-4] in order to grab the caller's or their caller's locations up the stack. – Leviscus Tempris Oct 02 '15 at 21:40
  • I marked your answer again since it was the one that pointed me into the final solution. My phone encounter however it's more exact as it picks up only the URL/file chain nothing more or lasts and takes an account more possibilities as far as characters go. – Leviscus Tempris Oct 02 '15 at 21:41
  • Thanks for accepting my answer again. I have seen people adding there findings as an answer(as you did) for documentation reasons. – winner_joiner Oct 03 '15 at 05:28