Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

net: give better error messages #7285

Closed
wants to merge 5 commits into from
Closed

Conversation

evanlucas
Copy link

Add address and/or port to errors where applicable for better reporting.

See #7005

@Nodejs-Jenkins
Copy link

Thank you for contributing this pull request! Here are a few pointers to make sure your submission will be considered for inclusion.

The following commiters were not found in the CLA:

  • Evan Lucas

You can fix all these things without opening another issue.

Please see CONTRIBUTING.md for more information

@evanlucas
Copy link
Author

I also went ahead and signed the CLA.

@tjfontaine
Copy link

Thanks for the contribution!

I think I'd prefer util.format over string concatenation.

Also what about moving some of this logic into errnoException and then just util._extend()ing the potentially returned object -- this means other places we want to decorate get this behavior by default.

@evanlucas
Copy link
Author

Ok, I can change the string concatenation. That makes more sense. I wasn't sure if moving it would be the direction to go or not. I'll go ahead and try to knock that out.

@evanlucas
Copy link
Author

alright I moved some of the logic into errnoException and got rid of the string concatenation :]

address: req.address
};
if (req.port) additions.port = req.port;
var ex = errnoException(status, 'write', err, additions);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not pass req directly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it sometimes could have additional properties, like afterShutdown.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could filter out functions, I'm mostly just looking for a generic way for this to work without having to be too explicit at every callsite

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about building the message from the keys of the object passed to errnoException?

On Mar 10, 2014, at 5:22 PM, Timothy J Fontaine [email protected] wrote:

In lib/net.js:

@@ -769,7 +769,12 @@ function afterWrite(status, handle, req, err) {
}

if (status < 0) {

  • var ex = errnoException(status, 'write', err);
  • err = util.format('%s %s:%s', err || '', req.address, req.port);
  • var additions = {
  •  address: req.address
    
  • };
  • if (req.port) additions.port = req.port;
  • var ex = errnoException(status, 'write', err, additions);
    We could filter out functions, I'm mostly just looking for a generic way for this to work without having to be too explicit at every callsite


Reply to this email directly or view it on GitHub.

@tj
Copy link

tj commented Mar 10, 2014

didn't look at the diff too closely but we'll have to make sure to support ephemeral ports as well, looks like maybe that's missing

@evanlucas
Copy link
Author

yep, I'll see if I can get that added tonight.

@tjfontaine
Copy link

just to be clear, you mean localPort and localAddress?

Depending on when the error is raised and by the time we've been notified it may not be possible to have that information. The most reliable way would be for us to exercise that path by default, but doing so results in an extra syscall by default

@tj
Copy link

tj commented Mar 10, 2014

Yeah, passing err.port == 0 etc wouldn't be too useful. We probably wouldn't have to worry about the syscalls, I'd hope errors wouldn't be "hot" code haha :D

@tjfontaine
Copy link

my point is that the fd may be gone by the time we want that information, so the only reliable way to have that information around is to always ask for that it when the handle is created, which means everyone will feel it -- I'm not opposed to it, it's generally useful, but to date we've so far waited to resolve that information until explicitly asked for it

@tj
Copy link

tj commented Mar 10, 2014

Ah ok I see what you're saying. I can only speak for myself there, I'd definitely prefer a tiny performance hit to having logs that have no zero value

@trevnorris
Copy link

Just curious, what good is knowing the fd after it's been closed? There's
no more inspection on it you can do at that time.

@tjfontaine
Copy link

That's not the question, he wants to know localPort, localAddress, remotePort, and remoteAddress, which depending on the disposition of the socket requires the fd to be valid when the syscall is performed. If we've already destroyed the handle (which we do by default for errors) the fd is probably invalid.

On the other hand, having the fd is very important for logging especially if you hit the scenario where you have a hung process or similar, being able to correlate lsof results with what you were able to have access to in process is important. Which is why the fact that we've gone to lengths to hide the literal fd is ridiculous, especially since it does have a useful concept on windows.

@trevnorris
Copy link

ok. well before we introduce a "tiny performance hit" I'd definitely like to review it. Per the usual. ;-)

@evanlucas
Copy link
Author

Ok, I've included the local port and address (where applicable)...here is the bench-net results:

Master

net/dgram.js
net/dgram.js len=1 num=100 type=send dur=5: 0.00046916
net/dgram.js len=1 num=100 type=recv dur=5: 0.00015590
net/dgram.js len=64 num=100 type=send dur=5: 0.030016
net/dgram.js len=64 num=100 type=recv dur=5: 0.0098559
net/dgram.js len=256 num=100 type=send dur=5: 0.11368
net/dgram.js len=256 num=100 type=recv dur=5: 0.037174
net/dgram.js len=1024 num=100 type=send dur=5: 0.24607
net/dgram.js len=1024 num=100 type=recv dur=5: 0.073494

net/net-c2s.js
net/net-c2s.js len=102400 type=utf dur=5: 3.4344
net/net-c2s.js len=102400 type=asc dur=5: 5.2294
net/net-c2s.js len=102400 type=buf dur=5: 6.0660
net/net-c2s.js len=16777216 type=utf dur=5: 2.4006
net/net-c2s.js len=16777216 type=asc dur=5: 4.5491
net/net-c2s.js len=16777216 type=buf dur=5: 6.1529

net/net-pipe.js
net/net-pipe.js len=102400 type=utf dur=5: 4.2717
net/net-pipe.js len=102400 type=asc dur=5: 5.4593
net/net-pipe.js len=102400 type=buf dur=5: 5.6436
net/net-pipe.js len=16777216 type=utf dur=5: 3.4148
net/net-pipe.js len=16777216 type=asc dur=5: 4.8503
net/net-pipe.js len=16777216 type=buf dur=5: 5.9216

net/net-s2c.js
net/net-s2c.js len=102400 type=utf dur=5: 3.4203
net/net-s2c.js len=102400 type=asc dur=5: 5.2393
net/net-s2c.js len=102400 type=buf dur=5: 6.0843
net/net-s2c.js len=16777216 type=utf dur=5: 2.4413
net/net-s2c.js len=16777216 type=asc dur=5: 4.5410
net/net-s2c.js len=16777216 type=buf dur=5: 6.0884

net/tcp-raw-c2s.js
net/tcp-raw-c2s.js len=102400 type=utf dur=5: 3.2528
net/tcp-raw-c2s.js len=102400 type=asc dur=5: 5.6648
net/tcp-raw-c2s.js len=102400 type=buf dur=5: 5.9811
net/tcp-raw-c2s.js len=16777216 type=utf dur=5: 2.4437
net/tcp-raw-c2s.js len=16777216 type=asc dur=5: 4.4782
net/tcp-raw-c2s.js len=16777216 type=buf dur=5: 5.4020

net/tcp-raw-pipe.js
0 'write'
util.js:741
  var errname = uv.errname(err);
                   ^
Error: err >= 0
    at Object.exports._errnoException (util.js:741:20)
    at fail (/Volumes/DEV/code/node/forks/node-fork/benchmark/net/tcp-raw-pipe.js:32:14)
    at Object.writeReq.oncomplete (/Volumes/DEV/code/node/forks/node-fork/benchmark/net/tcp-raw-pipe.js:63:11)
child process exited with code 1

net/tcp-raw-s2c.js
net/tcp-raw-s2c.js len=102400 type=utf dur=5: 3.6393
net/tcp-raw-s2c.js len=102400 type=asc dur=5: 5.4391
net/tcp-raw-s2c.js len=102400 type=buf dur=5: 5.8850
net/tcp-raw-s2c.js len=16777216 type=utf dur=5: 2.3136
net/tcp-raw-s2c.js len=16777216 type=asc dur=5: 4.1627
net/tcp-raw-s2c.js len=16777216 type=buf dur=5: 5.6639

With Changes

net/dgram.js
net/dgram.js len=1 num=100 type=send dur=5: 0.00046862
net/dgram.js len=1 num=100 type=recv dur=5: 0.00015282
net/dgram.js len=64 num=100 type=send dur=5: 0.029415
net/dgram.js len=64 num=100 type=recv dur=5: 0.0094112
net/dgram.js len=256 num=100 type=send dur=5: 0.10662
net/dgram.js len=256 num=100 type=recv dur=5: 0.033931
net/dgram.js len=1024 num=100 type=send dur=5: 0.23747
net/dgram.js len=1024 num=100 type=recv dur=5: 0.086841

net/net-c2s.js
net/net-c2s.js len=102400 type=utf dur=5: 3.2184
net/net-c2s.js len=102400 type=asc dur=5: 5.0028
net/net-c2s.js len=102400 type=buf dur=5: 5.7852
net/net-c2s.js len=16777216 type=utf dur=5: 2.3048
net/net-c2s.js len=16777216 type=asc dur=5: 4.5286
net/net-c2s.js len=16777216 type=buf dur=5: 5.9653

net/net-pipe.js
net/net-pipe.js len=102400 type=utf dur=5: 4.3703
net/net-pipe.js len=102400 type=asc dur=5: 5.2963
net/net-pipe.js len=102400 type=buf dur=5: 5.4195
net/net-pipe.js len=16777216 type=utf dur=5: 3.3145
net/net-pipe.js len=16777216 type=asc dur=5: 4.9447
net/net-pipe.js len=16777216 type=buf dur=5: 5.5724

net/net-s2c.js
net/net-s2c.js len=102400 type=utf dur=5: 3.0221
net/net-s2c.js len=102400 type=asc dur=5: 4.9844
net/net-s2c.js len=102400 type=buf dur=5: 5.6547
net/net-s2c.js len=16777216 type=utf dur=5: 2.0058
net/net-s2c.js len=16777216 type=asc dur=5: 4.1864
net/net-s2c.js len=16777216 type=buf dur=5: 5.0978

net/tcp-raw-c2s.js
net/tcp-raw-c2s.js len=102400 type=utf dur=5: 2.9586
net/tcp-raw-c2s.js len=102400 type=asc dur=5: 5.0143
net/tcp-raw-c2s.js len=102400 type=buf dur=5: 5.8931
net/tcp-raw-c2s.js len=16777216 type=utf dur=5: 2.2421
net/tcp-raw-c2s.js len=16777216 type=asc dur=5: 4.4877
net/tcp-raw-c2s.js len=16777216 type=buf dur=5: 5.7035

net/tcp-raw-pipe.js
0 'write'
util.js:741
  var errname = uv.errname(err);
                   ^
Error: err >= 0
    at Object.exports._errnoException (util.js:741:20)
    at fail (/Volumes/DEV/code/node/forks/node-fork/benchmark/net/tcp-raw-pipe.js:32:14)
    at Object.writeReq.oncomplete (/Volumes/DEV/code/node/forks/node-fork/benchmark/net/tcp-raw-pipe.js:63:11)
child process exited with code 1

net/tcp-raw-s2c.js
net/tcp-raw-s2c.js len=102400 type=utf dur=5: 3.7949
net/tcp-raw-s2c.js len=102400 type=asc dur=5: 5.6059
net/tcp-raw-s2c.js len=102400 type=buf dur=5: 5.6447
net/tcp-raw-s2c.js len=16777216 type=utf dur=5: 2.3139
net/tcp-raw-s2c.js len=16777216 type=asc dur=5: 4.3200
net/tcp-raw-s2c.js len=16777216 type=buf dur=5: 5.6513

@jcspencer
Copy link

👍

@evanlucas
Copy link
Author

anyone else had a chance to take a look at this?

@ilanbiala
Copy link

Can we get more people to look at this to get it pulled into master? The error messages are still the same after months and they provide very little description as to what the real error is.

@evanlucas
Copy link
Author

I went ahead and rebased off master again

@ilanbiala
Copy link

Thanks for all the help @evanlucas!

I'm really hoping to push Node to a point where error messages are very comprehensive and are useful, because some were very confusing for me when I started, even when they shouldn't have been. Some still are complicated, and I'm hoping that merging this will get the ball rolling on revamping the current state of error messages.

I think that if enough people support this, Node's developers will take a look and see how much this issue hinders beginners from using Node and how easy it is to fix compared to most of the other issues in the repository.

@gx0r
Copy link

gx0r commented Jul 14, 2014

👍 nice work.

@JacksonTian
Copy link

This pull request can be merged? What's the decision?

@indutny
Copy link
Member

indutny commented Aug 12, 2014

cc @tjfontaine

var additions = {
address: address
};
if (port) additions.port = port;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing two checks for port. I'd change it to the following:

var additions = {
  address: address
};
var details;
if (port) {
  details = util.format('%s:%s', address, port);
  additions.port = port;
} else {
  details = address;
}

@trevnorris
Copy link

Left a few comments. Ping me back when fixed.

@evanlucas
Copy link
Author

Thanks!

@JacksonTian
Copy link

ping reviewers.

req.localPort);
}
var ex = errnoException(status, 'connect', details, req);
self._destroy(ex);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you go ahead and put this into

self._destroy(errnoException(status, 'connect', details, req));

No need for var ex.

delete add[key];
}
}
e = add;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When _extend(e, additions) adds object properties to e. So add is superfluous. Just use e.

@trevnorris
Copy link

Few comments, other than that I'm okay with it.

Though I wonder about extending this to dgram and fs. Think both those places could use similar treatment. Basically, making a very simple generic way to propagate additional information. Like, passing the request itself and in _errnoException() change the following:

if (isNullOrUndefined(val) || isFunction(val))

to

if (isNullOrUndefined(val) || !isPrimitive(val))

I'd like to see if this might work, and then try extending it to the dgram module. Mode devs want "additional information" on the error for pretty much every module, so might as well setup a uniform way of doing it while we're here.

@evanlucas
Copy link
Author

@trevnorris thanks for your time on reviewing this. I went ahead and made the requested changes. I would love to help in any way on this. Should I go ahead and take a stab at doing this for dgram?

i--;
}
}
exports._extend(e, additions);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, screw using _extend(). Let's just do this manually:

if (additions && isObject(additions)) {
  var keys = Object.keys(additions);
  for (var i = 0; i < keys.length; i++)
    if (!isNullOrUndefined(keys[i]) && isPrimitive(keys[i]))
      e[i] = keys[i];
}

There, much simpler.

I use isPrimitive() because I assume no objects would be attached to the error. Would they?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I'll get this changed

@trevnorris
Copy link

@evanlucas One more revision on something I already commented on. Sorry for not seeing that the first round.

If you wouldn't mind doing this on dgram that would be a great way to start making this more ubiquitous.

Now, I would like it if the req object itself could be passed in. Instead of needing to create a new additions object. It would remove a lot of code and make cross-module implementation cleaner and future changes simpler. Think we can do that?

@evanlucas
Copy link
Author

I think for most cases, yes. Should we just continue using the additions in the event that req has not yet been created?

@trevnorris
Copy link

@evanlucas Just curious, do you have an example of when the req object hadn't been created before an error is emitted?

@evanlucas
Copy link
Author

@trevnorris
Copy link

@evanlucas Thanks. Let's make those cases the exception, and pass the req object where possible.

@evanlucas
Copy link
Author

Ok, _errnoException has been changed and now req is passed when it exists.

@@ -742,7 +742,8 @@ function afterWrite(status, handle, req, err) {
}

if (status < 0) {
var ex = errnoException(status, 'write', err);
err = util.format('%s %s:%s', err || '', req.address, req.port);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious why the err || ''. If status < 0 then we should have an error object. Otherwise Node had a bug.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I feel like there was a previous test that was failing without that, but after looking like it, doesn't appear to be true.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go ahead and remove it for now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah found it. When passing an err to uv.errname, if the err if ENOTFOUND, I get this:

util.js:739
  var errname = uv.errname(err);
                   ^
Error: err >= 0
    at Error (native)
    at exports._errnoException (util.js:739:20)
    at net.js:941:18
    at Object.asyncCallback [as callback] (dns.js:78:16)
    at Object.onlookup [as oncomplete] (dns.js:91:17)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Thanks for the info.

@trevnorris
Copy link

Few more comments, but looking great. Thanks for all your work here.

@evanlucas
Copy link
Author

Thanks! I'm happy to be able to contribute!

@trevnorris
Copy link

Remember that util._extend() extends the passed object, so there's no need to do var ex =. Go ahead and remove all cases of this.

@JacksonTian
Copy link

Any update? @evanlucas

@evanlucas
Copy link
Author

Closing as this landed in io.js a while back

@evanlucas evanlucas closed this Jun 22, 2015
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants