{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python - Logging\n",
    "\n",
    "- [Understanding pythons logging module (don't log directly against the root logger)](https://www.electricmonk.nl/log/2017/08/06/understanding-pythons-logging-module/)\n",
    "- [System Development with Python - Logging and the logging module](http://uwpce-pythoncert.github.io/SystemDevelopment/logging.html)\n",
    "- [python logging配置時間或大小輪轉](http://www.361way.com/python-logging-timedrotating/5043.html)\n",
    "- [Python 3 Logging using DictConfig](https://simpletutorials.com/c/1457/Python+3+Logging+using+DictConfig)\n",
    "- [Logging Exceptions](https://stackoverflow.com/questions/5191830/how-do-i-log-a-python-error-with-debug-information)\n",
    "- [Python Logging: An In-Depth Tutorial](https://www.toptal.com/python/in-depth-python-logging)\n",
    "- [StackOverflow - Using Python logging in multiple modules\n",
    "](https://stackoverflow.com/questions/15727420/using-python-logging-in-multiple-modules)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic Concept\n",
    "\n",
    "### The logging classes\n",
    "\n",
    "http://uwpce-pythoncert.github.io/SystemDevelopment/logging.html\n",
    "\n",
    "- **Loggers** - the interface for your code\n",
    "- **Handlers** - handle log routing\n",
    "- **Filters** - define which log messages to let through. It uses the logger hierarchy from `logging.getLogger(\"A.B.C\")`\n",
    "- **Formatters** - how the log messages get rendered\n",
    "\n",
    "![python-logging](./img/python-logging.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Formatters\n",
    "\n",
    "```python\n",
    "formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n",
    "```\n",
    "\n",
    "Common LogRecord attributes:\n",
    "\n",
    "```\n",
    "%(asctime)s    current time. default format: '2003-07-08 16:49:45,896'\n",
    "%(name)s       logger name\n",
    "%(levelname)s  the \"level\" of the log message: debug, warning, etc.\n",
    "%(filename)s   the filename of the message came from\n",
    "%(module)s     the name of the module the message came from\n",
    "%(message)s    the message itself\n",
    "```\n",
    "\n",
    "There are lots of other options:\n",
    "\n",
    "https://docs.python.org/3/library/logging.html#logrecord-attributes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Logging \"levels\"\n",
    "\n",
    "When log level is `0`, it look **up** the hierarchy for the first logger with a **non-zero level**.\n",
    "\n",
    "The built-in set is:\n",
    "\n",
    "```\n",
    "CRITICAL    50\n",
    "ERROR       40\n",
    "WARNING     30\n",
    "INFO        20\n",
    "DEBUG       10\n",
    "```\n",
    "\n",
    "Levels could be set in `Handlers` and `Loggers`. When the message is fired, it will be passed to relevant `Handlers` and `Loggers` and see if it passes the log level.\n",
    "\n",
    "```python\n",
    "my_logger.setLevel(logging.DEBUG) # or just use the arugument as string 'DEBUG'\n",
    "console_handler.setLevel(logging.DEBUG)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filters - Logging hierarchy\n",
    "\n",
    "A good convention to use when naming loggers is to use a **module-level logger**, in each module which uses logging, named as follows:\n",
    "\n",
    "```python\n",
    "logger = logging.getLogger(__name__)\n",
    "```\n",
    "\n",
    "Multiple calls to [`getLogger()`](https://docs.python.org/3.6/library/logging.html#logging.getLogger \"logging.getLogger\") with the same name will return a **reference to the same logger object**. Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of `foo`, loggers with names of`foo.bar`, `foo.bar.baz`, and `foo.bam` are all descendants of `foo`.\n",
    "\n",
    "\n",
    "**Example:**\n",
    "\n",
    "在Handler上添加了一個過濾器，現在我們輸出日誌信息的時候就會經過過濾器的處理。名為 `\"chat.gui\"` 的過濾器只讓名字帶有 `\"chat.gui\"` 前綴的 Logger 輸出信息。可以添加多個過濾器，只要有一個過濾器拒絕，日誌信息就不會被輸出。另外，在Logger中也可以添加過濾器。\n",
    "\n",
    "\n",
    "```python\n",
    "filter_chat_gui = logging.Filter(\"chat.gui\")\n",
    "handler_console = logging.StreamHandler()\n",
    "\n",
    "logger=logging.getLogger(\"chat.gui.statistic\")\n",
    "logger.addHandler(handler_console)\n",
    "h_console.addFilter(filter_chat_gui)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## TD;DR\n",
    "\n",
    "- All loggers are descendants of the `root logger`.\n",
    "- Don't log directly against the `root logger`. That means: no `logging.basicConfig()` or `logging.getLogger()` and no usage of module-level loggers such as `logging.warning()`, as they have unintended side-effects (no output, or double lines, etc.). (see [Appendix](#Appendix))\n",
    "- Configure a specific \"main\" logger for your program and put all the other loggers under that logger.\n",
    "- The easiest way that's usually correct is to use `__name__` as the logger name: `logger = logging.getLogger(__name__)`. This uses the module hierarchy as the name, which is generally what you want. \n",
    "\n",
    "## Best practices\n",
    "\n",
    "- Configure the root logger but never use it in your code --- e.g., never call a function like `logging.info()`, which actually calls the root logger behind the scene. If you want to catch error messages from libraries you use, make sure to configure the root logger to write to a file, for example, to make the debugging easier. By default, the root logger only outputs to `stderr`, so the log can get lost easily.\n",
    "- To use the logging, make sure to create a new logger by using `logging.getLogger(logger name)`. I usually use  `__name__` as the logger name, but anything can be used, as long as it is consistent. To add more handlers, I usually have a method that returns a logger (you can find the gist [here](https://gist.github.com/nguyenkims/e92df0f8bd49973f0c94bddf36ed7fd0)).\n",
    "\n",
    "In general, __your program or library shouldn’t log directly against the root logger__. Instead configure a specific **\"main\" logger** for your program and put all the other loggers under that logger. This way, you can toggle logging for your specific program on and off by setting the level of the main logger.\n",
    "\n",
    "```\n",
    "Project \n",
    "├── __init__.py\n",
    "├── logging.conf\n",
    "├── pkg01\n",
    "│   └── test01.py\n",
    "└── pkg02\n",
    "    └── test02.py\n",
    "```\n",
    "\n",
    "Best practice is\n",
    "\n",
    "1. In your main program or `__init__.py` file, do:\n",
    "\n",
    "```py\n",
    "# __init__.py\n",
    "def main():\n",
    "    \"\"\"your program code\"\"\"\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    import logging.config\n",
    "    logging.config.fileConfig('/path/to/logging.conf')\n",
    "    main()\n",
    "```\n",
    "\n",
    "2. In each module, to have a logger defined near the top of the module like this:\n",
    "\n",
    "```py\n",
    "# test01.py\n",
    "import logging\n",
    "logger = logging.getLogger(__name__)\n",
    "\n",
    "# and then in other code in the module do e.g.\n",
    "logger.debug('My message with %s', 'variable data')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Started"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Use Custom logger**\n",
    "\n",
    "```python\n",
    "import logging\n",
    "\n",
    "# create logger\n",
    "logger = logging.getLogger('simple_example')\n",
    "logger.setLevel(logging.DEBUG)\n",
    "\n",
    "# create console handler and set level to debug\n",
    "console_handler = logging.StreamHandler()\n",
    "console_handler.setLevel(logging.DEBUG)\n",
    "\n",
    "# create formatter\n",
    "formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n",
    "\n",
    "# add formatter to console_handler\n",
    "console_handler.setFormatter(formatter)\n",
    "\n",
    "# add console_handler to logger\n",
    "logger.addHandler(console_handler)\n",
    "\n",
    "# 'application' code\n",
    "logger.debug('debug message')\n",
    "logger.info('info message')\n",
    "logger.warn('warn message')\n",
    "logger.error('error message')\n",
    "logger.critical('critical message')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**Use Root logger (basicConfig)**\n",
    "\n",
    "> Just for demonstration. Never use root logger!!!\n",
    "\n",
    "It's not immediately obvious, but what this does is configure the **root logger**. Doing this may cause some counter-intuitive behaviour, because it causes debugging output for **all loggers** in your program, including every library that uses logging. This is why the requests module suddenly starts outputting debug information when you configure the root logger.\n",
    "\n",
    "```python\n",
    "import logging\n",
    "\n",
    "logging.basicConfig(\n",
    "    level=logging.DEBUG,  \n",
    "    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',\n",
    "    datefmt='%Y-%m-%d %H:%M:%S',\n",
    "    filename='/tmp/test.log',\n",
    "    filemode='w'  # default \"a\" for append; \"w\" for write.\n",
    ")\n",
    "\n",
    "logging.debug('debug message')  \n",
    "logging.info('info message')  \n",
    "logging.warning('warning message')  \n",
    "logging.error('error message')  \n",
    "logging.critical('critical message')  \n",
    "```\n",
    "\n",
    "The result:\n",
    "\n",
    "```\n",
    "Wed, 07 Nov 2018 23:33:19 <ipython-input-1-45bdb6a32ce8>[line:11] DEBUG debug message\n",
    "Wed, 07 Nov 2018 23:33:19 <ipython-input-1-45bdb6a32ce8>[line:12] INFO info message\n",
    "Wed, 07 Nov 2018 23:33:19 <ipython-input-1-45bdb6a32ce8>[line:13] WARNING warning message\n",
    "Wed, 07 Nov 2018 23:33:19 <ipython-input-1-45bdb6a32ce8>[line:14] ERROR error message\n",
    "Wed, 07 Nov 2018 23:33:19 <ipython-input-1-45bdb6a32ce8>[line:15] CRITICAL critical message\n",
    "```\n",
    "\n",
    "What does basicConfig do for you?\n",
    "\n",
    "```python\n",
    "filename = '/tmp/test.log'\n",
    "filemode = 'w'\n",
    "handler = logger.FileHandler(filename, mode)\n",
    "format_str = '%(asctime)s %(message)s'\n",
    "fmt = logger.Formatter(format_str)\n",
    "handler.setFormatter(fmt)\n",
    "logging.root.addHandler(h)\n",
    "logging.root.setLevel(logging.DEBUG)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## FAQ"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How Logging Exceptions?\n",
    "\n",
    "Setting argument `exc_info=True` will output a stack trace alongside the error message.\n",
    "\n",
    "For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:root:message\n",
      "Traceback (most recent call last):\n",
      "  File \"<ipython-input-1-a7f0e7b79c7b>\", line 3, in <module>\n",
      "    1/0\n",
      "ZeroDivisionError: division by zero\n"
     ]
    }
   ],
   "source": [
    "import logging\n",
    "try:\n",
    "    1/0\n",
    "except Exception as e:\n",
    "    logging.error(\"message\", exc_info=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:root:message\n",
      "Traceback (most recent call last):\n",
      "  File \"<ipython-input-2-617bab5d92d6>\", line 3, in <module>\n",
      "    1/0\n",
      "ZeroDivisionError: division by zero\n"
     ]
    }
   ],
   "source": [
    "import logging\n",
    "try:\n",
    "    1/0\n",
    "except Exception as e:\n",
    "    logging.exception(\"message\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Debugging logging problems\n",
    "\n",
    "When I run into weird logging problems such as **no output**, or **double lines**, I generally put the following debugging code at the point where I'm logging the message.\n",
    "\n",
    "```py\n",
    "log_to_debug = logging.getLogger(\"myapp.ui.edit\")\n",
    "while log_to_debug is not None:\n",
    "    print(f'level: {log_to_debug.level}, name: {log_to_debug.name}, handlers: {log_to_debug.handlers}')\n",
    "    log_to_debug = log_to_debug.parent\n",
    "```\n",
    "\n",
    "which outputs:\n",
    "\n",
    "```\n",
    "level: 0, name: myapp.ui.edit, handlers: []\n",
    "level: 0, name: myapp.ui, handlers: []\n",
    "level: 0, name: myapp, handlers: []\n",
    "level: 30, name: root, handlers: []\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Appendix"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test for logging hierarchy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:logger warning message\n",
      "2019-07-13 14:04:45,059 - root - WARNING - logger warning message\n",
      "ERROR:root:logger error message\n",
      "2019-07-13 14:04:45,064 - root - ERROR - logger error message\n",
      "CRITICAL:root:logger critical message\n",
      "2019-07-13 14:04:45,072 - root - CRITICAL - logger critical message\n",
      "2019-07-13 14:04:45,080 - mylogger - INFO - logger1 info message\n",
      "INFO:mylogger:logger1 info message\n",
      "2019-07-13 14:04:45,080 - mylogger - INFO - logger1 info message\n",
      "2019-07-13 14:04:45,098 - mylogger - WARNING - logger1 warning message\n",
      "WARNING:mylogger:logger1 warning message\n",
      "2019-07-13 14:04:45,098 - mylogger - WARNING - logger1 warning message\n",
      "2019-07-13 14:04:45,112 - mylogger - ERROR - logger1 error message\n",
      "ERROR:mylogger:logger1 error message\n",
      "2019-07-13 14:04:45,112 - mylogger - ERROR - logger1 error message\n",
      "2019-07-13 14:04:45,132 - mylogger - CRITICAL - logger1 critical message\n",
      "CRITICAL:mylogger:logger1 critical message\n",
      "2019-07-13 14:04:45,132 - mylogger - CRITICAL - logger1 critical message\n",
      "2019-07-13 14:04:45,144 - mylogger - INFO - logger2 info message\n",
      "INFO:mylogger:logger2 info message\n",
      "2019-07-13 14:04:45,144 - mylogger - INFO - logger2 info message\n",
      "2019-07-13 14:04:45,159 - mylogger - WARNING - logger2 warning message\n",
      "WARNING:mylogger:logger2 warning message\n",
      "2019-07-13 14:04:45,159 - mylogger - WARNING - logger2 warning message\n",
      "2019-07-13 14:04:45,167 - mylogger - ERROR - logger2 error message\n",
      "ERROR:mylogger:logger2 error message\n",
      "2019-07-13 14:04:45,167 - mylogger - ERROR - logger2 error message\n",
      "2019-07-13 14:04:45,170 - mylogger - CRITICAL - logger2 critical message\n",
      "CRITICAL:mylogger:logger2 critical message\n",
      "2019-07-13 14:04:45,170 - mylogger - CRITICAL - logger2 critical message\n",
      "2019-07-13 14:04:45,175 - mylogger.child1 - WARNING - logger3 warning message\n",
      "2019-07-13 14:04:45,175 - mylogger.child1 - WARNING - logger3 warning message\n",
      "WARNING:mylogger.child1:logger3 warning message\n",
      "2019-07-13 14:04:45,175 - mylogger.child1 - WARNING - logger3 warning message\n",
      "2019-07-13 14:04:45,182 - mylogger.child1 - ERROR - logger3 error message\n",
      "2019-07-13 14:04:45,182 - mylogger.child1 - ERROR - logger3 error message\n",
      "ERROR:mylogger.child1:logger3 error message\n",
      "2019-07-13 14:04:45,182 - mylogger.child1 - ERROR - logger3 error message\n",
      "2019-07-13 14:04:45,188 - mylogger.child1 - CRITICAL - logger3 critical message\n",
      "2019-07-13 14:04:45,188 - mylogger.child1 - CRITICAL - logger3 critical message\n",
      "CRITICAL:mylogger.child1:logger3 critical message\n",
      "2019-07-13 14:04:45,188 - mylogger.child1 - CRITICAL - logger3 critical message\n",
      "2019-07-13 14:04:45,204 - mylogger.child1.child2 - DEBUG - logger4 debug message\n",
      "2019-07-13 14:04:45,204 - mylogger.child1.child2 - DEBUG - logger4 debug message\n",
      "2019-07-13 14:04:45,204 - mylogger.child1.child2 - DEBUG - logger4 debug message\n",
      "DEBUG:mylogger.child1.child2:logger4 debug message\n",
      "2019-07-13 14:04:45,204 - mylogger.child1.child2 - DEBUG - logger4 debug message\n",
      "2019-07-13 14:04:45,215 - mylogger.child1.child2 - INFO - logger4 info message\n",
      "2019-07-13 14:04:45,215 - mylogger.child1.child2 - INFO - logger4 info message\n",
      "2019-07-13 14:04:45,215 - mylogger.child1.child2 - INFO - logger4 info message\n",
      "INFO:mylogger.child1.child2:logger4 info message\n",
      "2019-07-13 14:04:45,215 - mylogger.child1.child2 - INFO - logger4 info message\n",
      "2019-07-13 14:04:45,232 - mylogger.child1.child2 - WARNING - logger4 warning message\n",
      "2019-07-13 14:04:45,232 - mylogger.child1.child2 - WARNING - logger4 warning message\n",
      "2019-07-13 14:04:45,232 - mylogger.child1.child2 - WARNING - logger4 warning message\n",
      "WARNING:mylogger.child1.child2:logger4 warning message\n",
      "2019-07-13 14:04:45,232 - mylogger.child1.child2 - WARNING - logger4 warning message\n",
      "2019-07-13 14:04:45,253 - mylogger.child1.child2 - ERROR - logger4 error message\n",
      "2019-07-13 14:04:45,253 - mylogger.child1.child2 - ERROR - logger4 error message\n",
      "2019-07-13 14:04:45,253 - mylogger.child1.child2 - ERROR - logger4 error message\n",
      "ERROR:mylogger.child1.child2:logger4 error message\n",
      "2019-07-13 14:04:45,253 - mylogger.child1.child2 - ERROR - logger4 error message\n",
      "2019-07-13 14:04:45,275 - mylogger.child1.child2 - CRITICAL - logger4 critical message\n",
      "2019-07-13 14:04:45,275 - mylogger.child1.child2 - CRITICAL - logger4 critical message\n",
      "2019-07-13 14:04:45,275 - mylogger.child1.child2 - CRITICAL - logger4 critical message\n",
      "CRITICAL:mylogger.child1.child2:logger4 critical message\n",
      "2019-07-13 14:04:45,275 - mylogger.child1.child2 - CRITICAL - logger4 critical message\n",
      "2019-07-13 14:04:45,288 - mylogger.child1.child2.child3 - DEBUG - logger5 debug message\n",
      "2019-07-13 14:04:45,288 - mylogger.child1.child2.child3 - DEBUG - logger5 debug message\n",
      "2019-07-13 14:04:45,288 - mylogger.child1.child2.child3 - DEBUG - logger5 debug message\n",
      "2019-07-13 14:04:45,288 - mylogger.child1.child2.child3 - DEBUG - logger5 debug message\n",
      "DEBUG:mylogger.child1.child2.child3:logger5 debug message\n",
      "2019-07-13 14:04:45,288 - mylogger.child1.child2.child3 - DEBUG - logger5 debug message\n",
      "2019-07-13 14:04:45,305 - mylogger.child1.child2.child3 - INFO - logger5 info message\n",
      "2019-07-13 14:04:45,305 - mylogger.child1.child2.child3 - INFO - logger5 info message\n",
      "2019-07-13 14:04:45,305 - mylogger.child1.child2.child3 - INFO - logger5 info message\n",
      "2019-07-13 14:04:45,305 - mylogger.child1.child2.child3 - INFO - logger5 info message\n",
      "INFO:mylogger.child1.child2.child3:logger5 info message\n",
      "2019-07-13 14:04:45,305 - mylogger.child1.child2.child3 - INFO - logger5 info message\n",
      "2019-07-13 14:04:45,316 - mylogger.child1.child2.child3 - WARNING - logger5 warning message\n",
      "2019-07-13 14:04:45,316 - mylogger.child1.child2.child3 - WARNING - logger5 warning message\n",
      "2019-07-13 14:04:45,316 - mylogger.child1.child2.child3 - WARNING - logger5 warning message\n",
      "2019-07-13 14:04:45,316 - mylogger.child1.child2.child3 - WARNING - logger5 warning message\n",
      "WARNING:mylogger.child1.child2.child3:logger5 warning message\n",
      "2019-07-13 14:04:45,316 - mylogger.child1.child2.child3 - WARNING - logger5 warning message\n",
      "2019-07-13 14:04:45,330 - mylogger.child1.child2.child3 - ERROR - logger5 error message\n",
      "2019-07-13 14:04:45,330 - mylogger.child1.child2.child3 - ERROR - logger5 error message\n",
      "2019-07-13 14:04:45,330 - mylogger.child1.child2.child3 - ERROR - logger5 error message\n",
      "2019-07-13 14:04:45,330 - mylogger.child1.child2.child3 - ERROR - logger5 error message\n",
      "ERROR:mylogger.child1.child2.child3:logger5 error message\n",
      "2019-07-13 14:04:45,330 - mylogger.child1.child2.child3 - ERROR - logger5 error message\n",
      "2019-07-13 14:04:45,342 - mylogger.child1.child2.child3 - CRITICAL - logger5 critical message\n",
      "2019-07-13 14:04:45,342 - mylogger.child1.child2.child3 - CRITICAL - logger5 critical message\n",
      "2019-07-13 14:04:45,342 - mylogger.child1.child2.child3 - CRITICAL - logger5 critical message\n",
      "2019-07-13 14:04:45,342 - mylogger.child1.child2.child3 - CRITICAL - logger5 critical message\n",
      "CRITICAL:mylogger.child1.child2.child3:logger5 critical message\n",
      "2019-07-13 14:04:45,342 - mylogger.child1.child2.child3 - CRITICAL - logger5 critical message\n"
     ]
    }
   ],
   "source": [
    "import logging\n",
    "\n",
    "# 創建一個logger  \n",
    "logger = logging.getLogger()\n",
    "\n",
    "logger1 = logging.getLogger('mylogger')\n",
    "logger1.setLevel(logging.DEBUG)\n",
    "\n",
    "logger2 = logging.getLogger('mylogger')\n",
    "logger2.setLevel(logging.INFO)\n",
    "\n",
    "logger3 = logging.getLogger('mylogger.child1')\n",
    "logger3.setLevel(logging.WARNING)\n",
    "\n",
    "logger4 = logging.getLogger('mylogger.child1.child2')\n",
    "logger4.setLevel(logging.DEBUG)\n",
    "\n",
    "logger5 = logging.getLogger('mylogger.child1.child2.child3')\n",
    "logger5.setLevel(logging.DEBUG)\n",
    "\n",
    "# 創建一個handler，用於寫入日誌文件  \n",
    "fh = logging.FileHandler('/tmp/test.log')\n",
    "\n",
    "# 再創建一個handler，用於輸出到控制台  \n",
    "ch = logging.StreamHandler()\n",
    "\n",
    "# 定義handler的輸出格式formatter  \n",
    "formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n",
    "fh.setFormatter(formatter)\n",
    "ch.setFormatter(formatter)\n",
    "\n",
    "#定義一個filter\n",
    "log_filter = logging.Filter('mylogger.child1.child2')\n",
    "#fh.addFilter(log_filter)  \n",
    "\n",
    "# 給logger添加handler  \n",
    "#logger.addFilter(log_filter)\n",
    "logger.addHandler(fh)\n",
    "logger.addHandler(ch)\n",
    "\n",
    "#logger1.addFilter(log_filter)\n",
    "logger1.addHandler(fh)\n",
    "logger1.addHandler(ch)\n",
    "\n",
    "logger2.addHandler(fh)\n",
    "logger2.addHandler(ch)\n",
    "\n",
    "#logger3.addFilter(log_filter)\n",
    "logger3.addHandler(fh)\n",
    "logger3.addHandler(ch)\n",
    "\n",
    "#logger4.addFilter(log_filter)\n",
    "logger4.addHandler(fh)\n",
    "logger4.addHandler(ch)\n",
    "\n",
    "logger5.addHandler(fh)\n",
    "logger5.addHandler(ch)\n",
    "\n",
    "# 記錄一條日誌  \n",
    "logger.debug('logger debug message')\n",
    "logger.info('logger info message')\n",
    "logger.warning('logger warning message')\n",
    "logger.error('logger error message')\n",
    "logger.critical('logger critical message')\n",
    "\n",
    "logger1.debug('logger1 debug message')\n",
    "logger1.info('logger1 info message')\n",
    "logger1.warning('logger1 warning message')\n",
    "logger1.error('logger1 error message')\n",
    "logger1.critical('logger1 critical message')\n",
    "\n",
    "logger2.debug('logger2 debug message')\n",
    "logger2.info('logger2 info message')\n",
    "logger2.warning('logger2 warning message')\n",
    "logger2.error('logger2 error message')\n",
    "logger2.critical('logger2 critical message')\n",
    "\n",
    "logger3.debug('logger3 debug message')\n",
    "logger3.info('logger3 info message')\n",
    "logger3.warning('logger3 warning message')\n",
    "logger3.error('logger3 error message')\n",
    "logger3.critical('logger3 critical message')\n",
    "\n",
    "logger4.debug('logger4 debug message')\n",
    "logger4.info('logger4 info message')\n",
    "logger4.warning('logger4 warning message')\n",
    "logger4.error('logger4 error message')\n",
    "logger4.critical('logger4 critical message')\n",
    "\n",
    "logger5.debug('logger5 debug message')\n",
    "logger5.info('logger5 info message')\n",
    "logger5.warning('logger5 warning message')\n",
    "logger5.error('logger5 error message')\n",
    "logger5.critical('logger5 critical message')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Logging flow - from python doc\n",
    "\n",
    "![logging_flow](https://docs.python.org/3/_images/logging_flow.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Dict logger settings\n",
    "\n",
    "```python\n",
    "# logging_config.py\n",
    "\n",
    "config = {\n",
    "    'version': 1,  # int representing the schema version. The only valid value at present is 1\n",
    "    'formatters': {\n",
    "        'default': {\n",
    "            'format': '%(asctime)s - %(levelname)s - %(message)s', \n",
    "            'datefmt': '%Y-%m-%d %H:%M:%S'\n",
    "        }\n",
    "    },\n",
    "    'handlers': {\n",
    "        'console': {\n",
    "            'level': 'DEBUG',  # default 'WARNING'\n",
    "            'class': 'logging.StreamHandler',\n",
    "            'formatter': 'default',\n",
    "            'stream': 'ext://sys.stdout'\n",
    "        },\n",
    "        'file': {\n",
    "            'level': 'DEBUG',  # default 'WARNING'\n",
    "            'class': 'logging.handlers.RotatingFileHandler',\n",
    "            'formatter': 'default',\n",
    "            'filename': log_path,\n",
    "            'maxBytes': 1024,\n",
    "            'backupCount': 3\n",
    "        }\n",
    "    },\n",
    "    'loggers': {\n",
    "        'default': {\n",
    "            'level': level,  # default 'WARNING'\n",
    "            'handlers': ['console', 'file']\n",
    "        }\n",
    "    },\n",
    "    'disable_existing_loggers': False\n",
    "}\n",
    "```\n",
    "\n",
    "```python\n",
    "import logging\n",
    "import logging.config\n",
    "from logging_config import config\n",
    "\n",
    "\n",
    "def configure_logger(name, log_path, level):\n",
    "    logging.config.dictConfig(config)\n",
    "    return logging.getLogger(name)\n",
    "\n",
    "logger = configure_logger('default', 'log13.txt', 'DEBUG')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Find the root Logger\n",
    "\n",
    "```python\n",
    "In [1]: import logging\n",
    "\n",
    "In [2]: lab = logging.getLogger(\"a.b\")\n",
    "\n",
    "In [3]: lab.parent\n",
    "Out[3]: <RootLogger root (WARNING)>\n",
    "\n",
    "In [4]: lab.parent.name\n",
    "Out[4]: 'root'\n",
    "\n",
    "In [5]: la = logging.getLogger(\"a\")\n",
    "\n",
    "In [6]: lab.parent.name\n",
    "Out[6]: 'a'\n",
    " \n",
    "In [7]: root_logger = logging.getLogger()\n",
    "\n",
    "In [8]: root_logger.name\n",
    "Out[8]: 'root'\n",
    "\n",
    "In [9]: root_logger.handlers[0]\n",
    "Out[9]: <StreamHandler <stderr> (NOTSET)>\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
