Skip to content
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

Configure logging via TOML config #2483

Merged
merged 68 commits into from Nov 25, 2018
Merged

Conversation

orsinium
Copy link
Contributor

@orsinium orsinium commented Aug 7, 2018

Description

  1. Now you can fully configure logging from TOML-config.
  2. Option no_configure_logging disable any logging configuring.
  3. Separate logging configuration from other code for better maintainability.
  4. Also I update Apache copyright notice from my previous PR. Cindicator's leader recommend to use "Vote Inc." company name in copyrights.

Motivation and Context

Q. Why do you use same file for Luigi's config and logging config?
A. Because this is all project config. Also in future Pull Requests I plan to integrate in Luigi configs inheritance which allow also use separate file for logging, local settings or something else.

Q. Why do you do this? We already have support for logging.conf.
A. Because logging via ini-file is ugly, since ini format doesn't support inherit dicts. But TOML (and YAML. JSON and other modern formats) can do it.

Q. Why do you change no_configure_logging behavior?
A. Option no_configure_logging useful when you're configure logging in project bypass to Luigi. For example, when you want to use JSON or YAML configs for it. This is intuitive behavior for option with this name.

Q. Why do you change code structure?
A. Old code structure is hard to test, explore and extend. Now it's beautiful :)

Have you tested this? If so, how?

  1. I have included unit tests.
  2. Don't trust me! I update example repository, and you can play with new logging configuring yourself.

And...

Thank you for best pipeline library. And best music streaming service too. All this things is amazing!

@Tarrasch
Copy link
Contributor

Tarrasch commented Aug 8, 2018

I'm in favor of this. I'll have to review another time though. :)

@orsinium orsinium changed the title WIP: Configure logging via TOML config Configure logging via TOML config Aug 8, 2018
@orsinium
Copy link
Contributor Author

orsinium commented Aug 8, 2018

Now all tests is fine :)

@orsinium
Copy link
Contributor Author

orsinium commented Aug 9, 2018

Thank you for review :) All your advice applied, tests is passed. Do you have any other improvements?

@orsinium
Copy link
Contributor Author

@Tarrasch, how is your review? ^^

Copy link
Contributor

@Tarrasch Tarrasch left a comment

Choose a reason for hiding this comment

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

So cool to see that we're getting somewhere with improving the logging experience in luigi!

I just added a lot of comments on the code so that the code for this important core part of luigi keeps a high standard. I hope you can find time to address this stuff. I'm happy to merge (without more approval from me) once the issues are addressed.

Thanks!

5. logging_conf_file option
6. ``logging`` section
7. ``--log-level``
8. log_level option
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for doing the deep-dive and finding these things out.

try:
from ConfigParser import NoSectionError
except ImportError:
from configparser import NoSectionError
Copy link
Contributor

Choose a reason for hiding this comment

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

I recall this was a standard trick, can you link to some stack overflow thread or something?

Alternatively say # codepath for python < 2.7 something



class DaemonLogging(BaseLogging):
configured = False
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this internal state? Make it _configured maybe?


class DaemonLogging(BaseLogging):
configured = False
log_format = "%(asctime)s %(name)s[%(process)s] %(levelname)s: %(message)s"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you document if you can override this? If it's private do as commented above.

return configured


class DaemonLogging(BaseLogging):
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe document this is to be used for luigid

return True

@classmethod
def _default(cls, opts):
Copy link
Contributor

Choose a reason for hiding this comment

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

add a more descriptive name please

InterfaceLogging.config = get_config()

def _clean_config(self):
DaemonLogging.config = LuigiTomlParser()
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks weird. Where does toml come in in this generic context?

Should code like this be in a class TomlParserCmdlineTest(unittest.TestCase): context or something??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is for creating minimal config for tests. It's here for old tests.

self.assertTrue(result)


class PatchedLogging(InterfaceLogging):
Copy link
Contributor

Choose a reason for hiding this comment

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

Was it possible to mock one layer less?


@mock.patch("warnings.warn")
@mock.patch("luigi.interface.setup_interface_logging")
def test_cmdline_logger(self, setup_mock, warn):
Copy link
Contributor

Choose a reason for hiding this comment

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

It's so much code that it's hard to review it all. But can you make sure that all tests you delete are added somewhere else?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, all logging well tested in test/setup_logging_test.py

result = self.cls._cli(opts)
self.assertFalse(result)

def test_section(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

This test name setup_logging_test.TestDaemonLogging.test_section could have more context if you rename test_section to maybe test_section_returns_true_with_config and another test test_section_returns_false_without_config.

I know we don't follow this style at most places in this code base. But code testing improvements can start somewhere. If you want you can do this is too much you can do this as a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's test all what we need test for _section method

Copy link
Contributor

Choose a reason for hiding this comment

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

The point is that test_section is not saying much compared to test_section_returns_true_with_config.

@orsinium
Copy link
Contributor Author

Added some @Tarrasch's improvements. Other requests commented

@orsinium
Copy link
Contributor Author

CI is OK now. What about this PR? :)

@dlstadther
Copy link
Collaborator

I'm good here.

Have you been using this feature in production? All is good for your use?

@orsinium
Copy link
Contributor Author

Now I'm in another team in the same company. I'll get actual feedback on this feature by next few days :)

@dlstadther
Copy link
Collaborator

Congrats on your move @orsinium ! Appreciate the feedback

@orsinium
Copy link
Contributor Author

orsinium commented Nov 5, 2018

I got feedback and have fixed example based on this:

  1. disable_existing_loggers should be false for saving already existing loggers.
  2. dot in luigi.server logging means inheritance so we can just configure only luigi logger.

I think, it will be better change luigi-interface to luigi.interface, but I don't do it in this PR because it can broke logging in existing projects.

So. now it is ready, I hope :)

@orsinium
Copy link
Contributor Author

@dlstadther, @Tarrasch, what are we going to do with this Pull Request?

@dlstadther
Copy link
Collaborator

I just looked back through this PR and I think it LGTM. Given @Tarrasch had some changes requested, I'll wait for him to give his approval that his comments were addressed.

Thanks for continuing to follow up @orsinium !

Copy link
Contributor

@Tarrasch Tarrasch left a comment

Choose a reason for hiding this comment

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

I think things can be improved (as always). But anyway you've waited long already (sorry for my slow review), and this PR is actually a big improvement. Thanks!

'core', 'no_configure_logging', False):
setup_interface_logging(logging_conf, env_params.log_level)

InterfaceLogging.setup(env_params)
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh nice you even cleaned up this part!

#
"""
This module contains helper classes for configuring logging for luigid and
workers via command line arguments and options from config files.
Copy link
Contributor

Choose a reason for hiding this comment

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

Do these docs render nicely? Can you be explicit about if other's can use these methods or not?

return True

@classmethod
def _default(cls, opts):
Copy link
Contributor

Choose a reason for hiding this comment

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

(just a reminder to feel free to do this)

@Tarrasch Tarrasch merged commit 0a098f6 into spotify:master Nov 25, 2018
@orsinium orsinium deleted the logging branch November 29, 2018 17:24
@@ -38,31 +36,7 @@
from luigi import worker
from luigi import execution_summary
from luigi.cmdline_parser import CmdlineParser


def setup_interface_logging(conf_file='', level_name='DEBUG'):
Copy link
Contributor

Choose a reason for hiding this comment

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

A quick question. We call this method directly to set up logging. What is the equivalence now? Or it is not needed anymore?

@orsinium
Copy link
Contributor Author

Yeah, of course, we call InterfaceLogging instead of:

InterfaceLogging.setup(env_params)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants