{"action":"opened","issue":{"url":"https://api.github.com/repos/pydoit/doit/issues/128","repository_url":"https://api.github.com/repos/pydoit/doit","labels_url":"https://api.github.com/repos/pydoit/doit/issues/128/labels{/name}","comments_url":"https://api.github.com/repos/pydoit/doit/issues/128/comments","events_url":"https://api.github.com/repos/pydoit/doit/issues/128/events","html_url":"https://github.com/pydoit/doit/issues/128","id":139865938,"number":128,"title":"Add `Logbook` or `logging` based Reporter","user":{"login":"vlcinsky","id":635911,"avatar_url":"https://avatars.githubusercontent.com/u/635911?v=3","gravatar_id":"","url":"https://api.github.com/users/vlcinsky","html_url":"https://github.com/vlcinsky","followers_url":"https://api.github.com/users/vlcinsky/followers","following_url":"https://api.github.com/users/vlcinsky/following{/other_user}","gists_url":"https://api.github.com/users/vlcinsky/gists{/gist_id}","starred_url":"https://api.github.com/users/vlcinsky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vlcinsky/subscriptions","organizations_url":"https://api.github.com/users/vlcinsky/orgs","repos_url":"https://api.github.com/users/vlcinsky/repos","events_url":"https://api.github.com/users/vlcinsky/events{/privacy}","received_events_url":"https://api.github.com/users/vlcinsky/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2016-03-10T11:52:11Z","updated_at":"2016-03-10T11:52:11Z","closed_at":null,"body":"[`Reporter`](http://pydoit.org/cmd_run.html#custom-reporter) is great feature.\r\n\r\nIn my case I deploy the solution as a service and needed to see some nice log records from the run.\r\n\r\nFor this case I have created `LogbookReporter` class which I feel might be useful to others either with `logbook` or stdlib `logging` loggers.\r\n\r\nHere is my implementation, feel free to add it to the codebase.\r\n\r\n\r\n```python\r\nfrom logbook import Logger\r\n\r\n\r\nclass LogbookReporter(object):\r\n    \"\"\"reporter. print results on console/terminal (stdout/stderr)\r\n    Actual logs are configured via logbook handlers\r\n    \"\"\"\r\n    # short description, used by the help system\r\n    desc = 'logbook output'\r\n    logger_name = \"doit.reporter\"\r\n\r\n    def __init__(self, outstream, options):\r\n        # save non-succesful result information (include task errors)\r\n        log = Logger(self.logger_name)\r\n        self.failures = []\r\n        self.runtime_errors = []\r\n\r\n        self.show_out = options.get(\"show_out\", True)\r\n        self.show_err = options.get(\"show_err\", True)\r\n        # functions for logging particular log level\r\n        self.debug = log.debug\r\n        self.info = log.info\r\n        self.warn = log.warn\r\n        self.error = log.error\r\n\r\n    def write(self, text):\r\n        self.outstream.write(text)\r\n\r\n    def initialize(self, tasks):\r\n        \"\"\"called just after tasks have benn loaded before execution starts\"\"\"\r\n        pass\r\n\r\n    def get_status(self, task):\r\n        \"\"\"called when task is selected (check if up-to-date)\"\"\"\r\n        pass\r\n\r\n    def execute_task(self, task):\r\n        \"\"\"called when excution starts\"\"\"\r\n        # ignore tasks that do not define actions\r\n        # ignore private/hidden tasks (tasks that start with an underscore)\r\n        if task.actions and (task.name[0] != '_'):\r\n            self.info(\". {}\", task.title())\r\n\r\n    def add_failure(self, task, exception):\r\n        \"\"\"called when excution finishes with a failure\"\"\"\r\n        # self.failures.append({'task': task, 'exception':exception})\r\n        kwargs = {'task': task, 'exception': exception}\r\n        self.error(\"task {task}: exception: {exception}\",\r\n                   **{\"task\": task, \"exception\": exception})\r\n\r\n    def add_success(self, task):\r\n        \"\"\"called when excution finishes successfuly\"\"\"\r\n        self.info(\"task {}: suceeded\", task.title())\r\n\r\n    def skip_uptodate(self, task):\r\n        \"\"\"skipped up-to-date task\"\"\"\r\n        if task.name[0] != '_':\r\n            self.info(\"-- {}\", task.title())\r\n\r\n    def skip_ignore(self, task):\r\n        \"\"\"skipped ignored task\"\"\"\r\n        self.info(\"!! {}\", task.title())\r\n\r\n    def cleanup_error(self, exception):\r\n        \"\"\"error during cleanup\"\"\"\r\n        self.error(exception.get_msg())\r\n\r\n    def runtime_error(self, msg):\r\n        \"\"\"error from doit (not from a task execution)\"\"\"\r\n        # saved so they are displayed after task failures messages\r\n        self.runtime_errors.append(msg)\r\n\r\n    def teardown_task(self, task):\r\n        \"\"\"called when starts the execution of teardown action\"\"\"\r\n        pass\r\n\r\n    def complete_run(self):\r\n        \"\"\"called when finshed running all tasks\"\"\"\r\n        # if test fails print output from failed task\r\n        for result in self.failures:\r\n            self.error(\"#\"*40)\r\n            msg = '%s - taskid:%s\\n' % (result['exception'].get_name(),\r\n                                        result['task'].name)\r\n            self.error(msg)\r\n            self.error(result['exception'].get_msg())\r\n            task = result['task']\r\n            if self.show_out:\r\n                out = \"\".join([a.out for a in task.actions if a.out])\r\n                self.debug(out)\r\n            if self.show_err:\r\n                err = \"\".join([a.err for a in task.actions if a.err])\r\n                self.error(err)\r\n\r\n        if self.runtime_errors:\r\n            self.error(\"#\"*40)\r\n            self.error(\"Execution aborted.\")\r\n            self.error(\"\\n\".join(self.runtime_errors))\r\n```\r\n\r\nUse in the code:\r\n\r\n\r\n\r\n```python\r\nclass MyServiceReporter(LogbookReporter):\r\n    logger_name = \"my_service\"\r\n\r\nDOIT_CONFIG = {'reporter': MyServiceReporter,\r\n               'verbosity': 2}\r\n```\r\n\r\nand optionally enable logging for `main` (which is called as script):\r\n\r\n\r\n```python\r\nfrom logbook import StreamHandler\r\n\r\n# content as in `dodo.py` comes here\r\n\r\ndef main():\r\n    StreamHandler(sys.stdout, level=\"INFO\").push_application()\r\n    doit.run(globals())\r\n```\r\n"}}