22

When working in Python I always have this simple utility function which returns the file name and line number from where the function is called:

from inspect import getframeinfo, stack def d(): """ d stands for Debug. It returns the file name and line number from where this function is called.""" caller = getframeinfo(stack()[1][0]) return "%s:%d -" % (caller.filename, caller.lineno) 

So in my code I simply put a couple debug lines like this to see how far we get before some error occurs:

print d() # Some buggy code here print d() # More buggy code here print d(), 'here is the result of some var: ', someVar 

This works really well for me because it really helps debugging quickly.

I'm now looking for the equivalent in a node backend script. I was searching around but I can't find anything useful (maybe I'm looking for the wrong words?).

Does anybody know how I can create a Javascript/nodejs function which outputs the file name and line number from where the function is called? All tips are welcome!

9
  • stackoverflow.com/questions/1340872/… and stackoverflow.com/questions/13815640/… Commented Feb 20, 2015 at 14:31
  • Have you ever tried chrome://inspect? Commented Nov 3, 2017 at 22:53
  • @PatrickRoberts How do you want to use that in order to print the filename and the line of code in NodeJS? Commented Nov 3, 2017 at 22:54
  • @lilezek if I was trying to answer the question as asked, I would have written an answer. I was suggesting a much better debugging experience than sprinkling random function calls into your code to see approximately where it crashes. Commented Nov 3, 2017 at 22:55
  • @PatrickRoberts - I'm not using a browser. This is a pure backend system in which I want to log certain information, including the source of the log. Commented Nov 3, 2017 at 22:57

6 Answers 6

36
+400

You can create an Error to get where the Error is, and its stack trace. Then you can put that into a function, to get the line where it is.

function thisLine() { const e = new Error(); const regex = /\((.*):(\d+):(\d+)\)$/ const match = regex.exec(e.stack.split("\n")[2]); return { filepath: match[1], line: match[2], column: match[3] }; } console.log(thisLine()); 

This works for me in Google Chrome.

And also in node.

Note to @j08691's comment:

Both this and this seem to be using lineNumber, which is not present (as far as I could test) in NodeJS.

Sign up to request clarification or add additional context in comments.

8 Comments

Seems to work fine. I also need the filename though. Can that be added somehow as well? So I need it to look like filename.js:38 (no need for character number on the line)
@kramer65 Check the new code. You just have to play with the return value (which right now is an array of two elements) as you need, but it is a beginning.
Thanks! This is what I just ended up with: pastebin.com/68pa6jVH . It creates lines like thefilename.js:345. I guess this can be made better/cleaner, but now it at least does what I want it to do.. :-)
@kramer65 By the way just edited the snippet again with a better construction. Uses only a single regex, and returns a human-readable object.
@kramer65 For sure, but book writers usually let readers do something on their own. Look here: nodejs.org/api/path.html#path_path_basename_path_ext
|
2

Printing line number with custom string

const moment = require('moment'); const log = console.log; const path = require('path'); function getTime(time) { return moment().format('YYYY-MM-DD HH:mm:ss') }; function line(num = 2) { const e = new Error(); const regex = /\((.*):(\d+):(\d+)\)$/ const match = regex.exec(e.stack.split("\n")[num]); const filepath = match[1]; const fileName = path.basename(filepath); const line = match[2]; const column = match[3]; return { filepath, fileName, line, column, str: `${getTime()} - ${fileName}:${line}:${column}` }; } log(line().str, "mylog1"); log(line().str, "mylog2"); log(line().str, "mylog3"); 

OUTPUT

2021-11-22 13:07:15 - test.js:44:5 mylog1 2021-11-22 13:07:15 - test.js:45:5 mylog2 2021-11-22 13:07:15 - test.js:46:5 mylog3 

Comments

0

You can use this gulp plugin gulp-log-line . It Logs file and line number without the extra cost of reading the stack.

you just have to install gulp and gulp-log-line using the npm install gulp --save and npm install gulp-log-line command. after that you need to create and write the below code in gulpfile.js and run gulp log-line to create a duplicate file in the build folder :-

var gulp = require('gulp'); var logLine = require('gulp-log-line'); gulp.task('line-log', function() { return gulp.src("file.js", {buffer : true}) //Write here the loggers you use. .pipe(logLine(['console.log', 'winston.info'])) .pipe(gulp.dest('./build')) }) gulp.task('default', ['line-log']) 

Example

file.js :-

console.log('First log') var someVariable console.log('Second log') 

Becomes

console.log('file.js:1', 'First log') var someVariable console.log('file.js:3', 'Second log') 

Comments

0

The only way I've found to get anything relating to line numbers is to trap the window.onerror function, and when there's an error that will get passed the error message, the file URL and the line number:

window.onerror = function(msg, url, line) { alert(msg + "\n" + url + ":" + line); }; 

This works for me on Chrome - I don't know about other browsers.

EDIT when this answer was given in Feb' 15 there was no mention of NodeJS in the question. That was only added in November '17.

1 Comment

Node wasn't present in the original question but wasn't Javascript for browsers either. So assuming either Node or web-based JS was wrong IMHO.
0

Another option is to use method Error.captureStackTrace with any object which can be placed in any place you need a stack trace from.

const obj = {}; // or [], or new Error(), ... Error.captureStackTrace(obj); /* if called with the second param Error.captureStackTrace(obj, myFunction), any trace above and including myFunction will be excluded from trace */ console.log(obj.stack) 

Comments

0

I made this function based on another answer here from StackOverflow, as I say in the first comment on the function. Hope it's useful to someone else! Call logLnDebug() , not the weird one.

function logLnDebug(message) { console.debug("D:" + ___8drrd3148796d_Xaf() + message); } let __correct_stack_line_idx_GL = -1; function ___8drrd3148796d_Xaf() { // Adapted from: https://stackoverflow.com/a/26410435/8228163. const err = new Error(); const stack_lines = err.stack.split('\n'); if (__correct_stack_line_idx_GL === -1) { for (let i = 0; i < stack_lines.length; i++) { let element_split = stack_lines[i].trim().split(' '); for (const part of element_split) { let part_trimmed = part.trim(); if (part_trimmed === "___8drrd3148796d_Xaf") { // Chrome __correct_stack_line_idx_GL = i; break; } else if (part_trimmed.includes("@")) { // Firefox let at_split = part_trimmed.split('@'); if (at_split.length >= 2 && at_split[0] === "___8drrd3148796d_Xaf") { __correct_stack_line_idx_GL = i; break; } } } } } let file_name = "ERROR"; let line_number = -1; // stack_lines[0] is "Error", stack_lines[1] is this function, stack_lines[2] is the caller if (stack_lines.length >= __correct_stack_line_idx_GL + 2 + 1) { // Example stack line: " at callerFunction (file.js:10:5)" // Another example: "EventHandlerNonNull*checkWebSocket@http://localhost:63344/Test/js/Test.js:160:13" // And another example: "at http://localhost:63344/Test/Test.html?_ijt=b3b2k4v2u542v6gnonnpivb0e4&_ij_reload=RELOAD_ON_SAVE:137:13" let stack_line = stack_lines[__correct_stack_line_idx_GL + 2].trim(); let dot_idx = stack_line.indexOf('.'); let last_slash_idx = stack_line.lastIndexOf('/'); let last_colon_idx = stack_line.lastIndexOf(':'); let second_last_colon_idx = stack_line.lastIndexOf(':', last_colon_idx - 1); if (last_colon_idx > dot_idx && second_last_colon_idx > dot_idx) { line_number = stack_line.substring(second_last_colon_idx + 1, last_colon_idx); file_name = stack_line.substring(last_slash_idx + 1, second_last_colon_idx); } else if (last_colon_idx > dot_idx && second_last_colon_idx < last_slash_idx) { line_number = stack_line.substring(last_slash_idx + 1); file_name = stack_line.substring(last_slash_idx + 1, last_colon_idx); } else { // Keep line_number as -1 (non-existent) file_name = stack_line.substring(last_slash_idx + 1); } if (file_name.indexOf('?') !== -1) { file_name = file_name.substring(0, file_name.indexOf('?')); } } return file_name + ":" + line_number + "|> "; } 

This should print for example D:ThisIsATest.js:166|> Hello! . Works for me on Firefox and Chrome.

Then to make it prettier in the output, you can print only the uppercase letters of the file name without the extension and pad to a maximum number of characters on that and the line number. Would become D: TIAT:166 |> Hello! in that case. JetBrains IDEs can find the right files with the uppercase letters and the line numbers, that's why I have it like this.

Note: I just realized the question is for Node. This pure JS for the browser. Sorry if it's not useful for this case. I'll leave it here anyway in case anyone finds it useful.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.