-
Notifications
You must be signed in to change notification settings - Fork 38.7k
[qa] Add logging to test_framework.py #9768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
If you're working on custom logging stuff anyway: what would be really nice is something that would queue up debug messages, then print them only if the test fails. This lowers the amount of spam in the Travis log in the good case, and gives full troubleshooting information in the bad case (when you need it). |
| os.rmdir(self.options.root) | ||
| else: | ||
| print("Not cleaning up dir %s" % self.options.tmpdir) | ||
| self.log.info("Not cleaning up dir %s" % self.options.tmpdir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would upgrade this to a warning, since this is leaving files behind. Also this is a really useful print to see on the console when a test fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point. Fixed.
| help="Directory for caching pregenerated datadirs") | ||
| parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), | ||
| help="Root directory for datadirs") | ||
| parser.add_option("--loglevel", dest="loglevel", default="ERROR", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should say something about test_framework.log and console logging in the tests' --help output.
What you wrote in the commit message is good, but obviously no one is going to see if it is just in this commit message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Updated.
| except JSONRPCException as e: | ||
| print("JSONRPC error: "+e.error['message']) | ||
| traceback.print_tb(sys.exc_info()[2]) | ||
| self.log.exception("JSONRPC error: "+e.error['message']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should stop trying to print e here and below because log.exception already formats and prints the exception and stack trace: https://docs.python.org/3/library/logging.html#logging.Logger.exception. It's not helpful to see same information printed multiple times and in inconsistent ways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, good catch.
| parser.add_option("--tmpdir", dest="tmpdir", default=tempfile.mkdtemp(prefix="test"), | ||
| help="Root directory for datadirs") | ||
| parser.add_option("--loglevel", dest="loglevel", default="ERROR", | ||
| help="log events at this level and higher to the console") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you list what the log levels are? At least should probably mention that passing --loglevel=0 will show complete log output.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
| def start_logging(self): | ||
| # Add logger and logging handlers | ||
| self.log = logging.getLogger('TestFramework') | ||
| self.log.setLevel(logging.DEBUG) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're better off dropping this call. The default debug level seems to be 0, and the value of DEBUG is 10 so this potentially throws away logs.
Also would drop the 3 other setLevel(logging.DEBUG) calls below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DEBUG is good here. We shouldn't encourage people to log messages at custom levels (ie not one of debug, info, warning, error and critical), and we especially shouldn't encourage people to log at a level lower than DEBUG.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DEBUG is good here. We shouldn't encourage people to log messages at custom levels (ie not one of debug, info, warning, error and critical), and we especially shouldn't encourage people to log at a level lower than DEBUG.
Actually setting DEBUG seems to be needed here in order to be able to see debug messages at all, otherwise python seems to fall back to root logger level which is WARNING.
Still I would consider dropping the "fh.setLevel()" call below. Having that call seems less like "not encouraging" people to log messages at custom levels, and more like going out of our way to punish and confuse them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't agree. logging at non-standard levels is harder (you need to call self.log.log(lvl, message) rather than self.log.debug(message) or similar). Why would anyone want do that, and why would we want to allow people to add confusing logging levels to the test cases?
| fh.setLevel(logging.DEBUG) | ||
| # Create console handler to log messages to stderr. By default this logs only error messages, but can be configured with --loglevel. | ||
| ch = logging.StreamHandler() | ||
| console_log_level = getattr(logging, self.options.loglevel.upper(), None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling getattr doesn't seem to be necessary, the documentation says setLevel already accepts a string:
https://docs.python.org/3/library/logging.html#logging.Handler.setLevel. Maybe upper() is still useful though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. I was following the Python 'logging how to' guide here: https://docs.python.org/3/howto/logging.html#logging-to-a-file which uses getattr(). I guess that was written before 3.2, which is when setlevel() was updated to take a string argument.
I've tested the logging module and:
- we do need to use upper() to set the log levels to uppercase
- calling with an invalid string causes logging to raise a helpful exception (I was worried that it might fail silently).
So calling setlevel directly with self.options.loglevel.upper() is better.
| rpc_handler = logging.StreamHandler() | ||
| rpc_handler.setLevel(logging.DEBUG) | ||
| rpc_logger.addHandler(rpc_handler) | ||
| rpc_logger.debug("debug") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Junk lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. Junk lines from when I was testing this. Thanks. Removed.
|
utACK 3e7ed8c |
|
@ryanofsky thanks for the great feedback. I'm going to hold back on making any changes until I get some more concept ACKs/NACKs. I want to make sure people are happy going in this direction before I spend any time addressing review comments. |
|
@MarcoFalke @laanwj @jonasschnelli @sdaftuar you're the most active contributors to the qa directory. Can you have a quick look and give me a concept ACK/NACK so I know whether to continue in this direction. No rush. Obviously you're all busy with the 0.14 release. |
|
Concept ACK. I haven't reviewed this PR in detail yet, but what I'd find useful is if the default behavior when running a single test locally was to print all log messages to the console, and only suppress log messages when running the tests through the |
|
@sdaftuar the eventual aim will be to add a lot more low-level logging (eg logging when every p2p message is sent or received on the mininode connection). Those low-level logs will always be saved to test_framework.log, but by default are not printed to the console. If the user want low-level logs printed to console, they can specify --loglevel when running the script. |
7f55a3e to
3b336ab
Compare
|
I've addressed @ryanofsky's review comments and repushed. A few updates since I opened this PR:
I've been running with this patch set locally for some time, together with a tool to interleave test_framwork logs with individual node logs. It makes troubleshooting testcases a lot easier since I have a global view of everything that's happening in the test case (ie I can see messages being sent in by the test framework and then received by the node). I believe this is ready for merge, but any further review comments are welcomed. |
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 3b336aba4ef250868d6f3dc2bf741b0bbf621d39 assuming travis tests will be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should move the os.makedirs, and self.start_logging calls above the try: block, so error information won't be lost if these calls fail for some reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I needed to put start_logging() below the os.makedirs() call, but you're right that this should be outside the large try/except block. I've now moved them both up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be changed to error instead of warning, to avoid potential for silent errors slipping through (even though in most cases, an error probably did print previously).
Also, I think it would be good for this message to mention the temp directory path (or alternately the path to test_framework.log), since it is no longer printed by default and is a piece of information you probably need to understand what happened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Done both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I missed some discussion, but why is the default level error instead of debug or warning? I can understand disabling debug output by default when running pull-tester. But I run individual tests from the command line every day, and I can't remember a time when I've ever wanted to do this without seeing output.
If we have make error the default, can we at least support abbreviating "--loglevel=" as "-l", and allow passing integer log levels? This way it'd be possible to see debug output with -l0 instead of --loglevel=debug
Also would note that even the default level for the root python logger is warning, not error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. This should be INFO by default. DEBUG emits way too much to the console to be the default.
pull-tester diverts stdout from the subtests, so there's no output to console at all.
--loglevel now accepts ints as well as the named strings and can be specified with the short option -l
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to support integer debug levels for less verbose command line usage. Maybe setLevel(int(self.options.loglevel) if self.options.loglevel.isdigit() else self.options.loglevel.upper()).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. Good idea. Done (although I've split this over two lines for readability).
qa/rpc-tests/bip68-sequence.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why drop mention of previous versions? Also, it seems like without this qualification, maybe the word "always" should be dropped too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because I previously thought that it was a bug that Core v0.13 (and a few earlier versions) always relayed v2 transactions - hence the 'with current versions' comment. See #9739 for the discussion. Whether or not it's a bug is moot now, and I think the 'with current verions' comment doesn't clarify.
'always' here means 'both before and after BIP 68 activation'.
qa/rpc-tests/bip68-sequence.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably best to remove trailing and prefix \n here and in other logging lines. (There seem to be 4-5 other occurrances in this PR.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be logging.getLogger. This is causing travis errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. My mistake. Fixed.
| def start_logging(self): | ||
| # Add logger and logging handlers | ||
| self.log = logging.getLogger('TestFramework') | ||
| self.log.setLevel(logging.DEBUG) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DEBUG is good here. We shouldn't encourage people to log messages at custom levels (ie not one of debug, info, warning, error and critical), and we especially shouldn't encourage people to log at a level lower than DEBUG.
Actually setting DEBUG seems to be needed here in order to be able to see debug messages at all, otherwise python seems to fall back to root logger level which is WARNING.
Still I would consider dropping the "fh.setLevel()" call below. Having that call seems less like "not encouraging" people to log messages at custom levels, and more like going out of our way to punish and confuse them.
a00ec22 to
c9755da
Compare
|
@ryanofsky's comments addressed and repushed: c9755da / https://github.com/jnewbery/bitcoin/tree/pr9768.3 |
3f35e24 to
2c9fc0e
Compare
|
@laanwj @MarcoFalke I think this is a real improvement and helps debugging and fixing test cases when they fail. I've addressed #9768 (comment) in #9780 which builds off this PR. Any chance I can convince you to take a look at this? |
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK f546d06ac21e2953c0dc0b1d8fef459b186781ce
Thanks for supporting -l0.
I think this is a real improvement and helps debugging and fixing test cases when they fail.
Definitely agree. It should be especially useful to easily see interleaved python and bitcoin debug.log output while debugging.
qa/rpc-tests/proxy_test.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 72 Warning: testing without local IPv6 support above is now a print again, not sure if this was intentional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes - proxy_test does a lot of work in the __init__() method, which is called before the temp directory is set up. Logging requires the temp directory, so we can't use self.log until we're in main() and _start_logging() has been called.
I left this as a print() rather than trying to do anything clever to coerce the test into being log friendly.
|
Concept ACK. Going to give some feedback later tonight.
…On Wed, Mar 8, 2017 at 3:42 PM, John Newbery ***@***.***> wrote:
@laanwj @MarcoFalke I think this is a real improvement and helps debugging
and fixing test cases when they fail. I've addressed #9768 (comment) in
#9780 which builds off this PR. Any chance I can convince you to take a look
at this?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
maflcko
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK f546d06
qa/rpc-tests/invalidateblock.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove the prints above, you need to change those to assert_equal to maintain the debug print in case of failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't change indentation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason you switched the rpc handler to stderr by falling back to the default value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason. Just an oversight. Now fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might need to shutdown (flush) the logs here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
qa/rpc-tests/p2p-segwit.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need the assert(False) when you pass any AssertionError below. Though maybe a fix for an unrelated follow up pr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's pre-existing behaviour, which is unrelated to this PR. I agree it should be fixed in a different PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the print to stdout can use a formatter that produces smaller lines. (Maybe without the timestamp?) But no strong opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I specifically set the format of the logs to be the same as what bitcoind produces with microsecond logging.
b7dfc9e to
f2751d5
Compare
|
This works if you call |
|
documentation is here: https://github.com/bitcoin/bitcoin/blob/master/test/README.md#test-logging :
Feel free to open a PR if you think the documentation could be improved. |
|
@JNewberry it's probably sufficient if |
|
The help text for bitcoin/test/functional/test_runner.py Line 258 in a892218
|
|
Thanks, I'll take a look. |
This is a WIP PR. Comments and feedback on the overall approach welcomed!
(For an overview of python's logging module and terms, see https://docs.python.org/3/library/logging.html and https://docs.python.org/3/howto/logging.html)
This PR adds logging to the test-framwork.py module. The "TestFramework" logger is added as a member of the BitcoinTestFramework class, and two handlers are attached to the logger:
test_framework.logfile in the temporary test directory.Test scripts and helper modules can log to the same Logger so there's a single log output:
self.log.[debug|info|warning|error|critical|exception]().I've added examples of how individual scripts and helper modules should log events in p2p-segwit.py and mininode.py.
The eventual goal is to remove all
print()calls and replace them with logging.A few notes on the implementation:
--loglevelparameter can be used to log lower level records to the console, for example--loglevel=DEBUGwill emit all log records to the console.test_framework.logis the same as bitcoind's debug.log. It should be trivial to interleavetest_framework.logwith the variousnodeX/debug.logfiles for a global view of what's happening during the test.--tracerpcparameter