Revision: 004b5ff3ac8e
Author:   Pekka Klärck
Date:     Tue May 17 14:28:11 2011
Log:      Support for Pytho's standard `logging` module

Update issue 455
Status: Started
Owner: pekka.klarck
I implemented the support for the logging module by propogating messages
received by the root logger to new robot.api.logger (issue 339).

Chris, could you take a look at the implementation and, if possible, checkout
the latest version and test this?
http://code.google.com/p/robotframework/source/detail?r=004b5ff3ac8e

Added:
 /atest/robot/test_libraries/logging_with_logging.txt
 /atest/testdata/test_libraries/LibUsingPyLogging.py
 /atest/testdata/test_libraries/logging_with_logging.txt
 /src/robot/output/pyloggingconf.py
Modified:
 /src/robot/__init__.py

=======================================
--- /dev/null
+++ /atest/robot/test_libraries/logging_with_logging.txt Tue May 17 14:28:11 2011
@@ -0,0 +1,42 @@
+*** Settings ***
+Documentation   Tests for logging using Python's `logging` module.
+Suite Setup Run Tests ${EMPTY} test_libraries/logging_with_logging.txt
+Force Tags      regression  pybot  jybot
+Resource        atest_resource.txt
+
+*** Test Cases ***
+
+Log with default levels
+    ${tc} =  Check test case  ${TEST NAME}
+    Check log message  ${tc.kws[0].msgs[0]}  debug message     DEBUG
+    Check log message  ${tc.kws[0].msgs[1]}  info message      INFO
+    Check log message  ${tc.kws[0].msgs[2]}  warning message   WARN
+    Check log message  ${tc.kws[0].msgs[3]}  error message     WARN
+    Check log message  ${tc.kws[0].msgs[4]}  critical message  WARN
+    Check log message  ${ERRORS.msgs[0]}     warning message   WARN
+    Check log message  ${ERRORS.msgs[1]}     error message     WARN
+    Check log message  ${ERRORS.msgs[2]}     critical message  WARN
+
+Log with custom levels
+    ${tc} =  Check test case  ${TEST NAME}
+ Check log message ${tc.kws[0].msgs[0]} below debug DEBUG
+    Check log message  ${tc.kws[0].msgs[1]}  between debug and info    INFO
+    Check log message  ${tc.kws[0].msgs[2]}  between info and warning  INFO
+    Check log message  ${tc.kws[0].msgs[3]}  above warning             WARN
+
+Log using custom logger
+    ${tc} =  Check test case  ${TEST NAME}
+    Check log message  ${tc.kws[0].msgs[0]}  custom logger
+    Check stdout contains  Custom Logger
+
+Log using non-propagating logger
+    ${tc} =  Check test case  ${TEST NAME}
+    Should be empty  ${tc.kws[0].msgs}
+    Check stdout contains  Nonprop Logger
+
+Timestamps are accurate
+    ${tc} =  Check test case  ${TEST NAME}
+    ${msg1}  ${msg2} =  Set variable  ${tc.kws[0].msgs}
+    Check log message  ${msg1}  First message
+    Check log message  ${msg2}  Second message 0.1 sec later
+    Should be true  '${msg1.timestamp}' < '${msg2.timestamp}'
=======================================
--- /dev/null
+++ /atest/testdata/test_libraries/LibUsingPyLogging.py Tue May 17 14:28:11 2011
@@ -0,0 +1,42 @@
+import logging
+import time
+import sys
+
+
+class CustomHandler(logging.Handler):
+
+    def emit(self, record):
+        sys.__stdout__.write(record.getMessage().title() + '\n')
+
+
+custom = logging.getLogger('custom')
+custom.addHandler(CustomHandler())
+nonprop = logging.getLogger('nonprop')
+nonprop.propagate = False
+nonprop.addHandler(CustomHandler())
+
+
+def log_with_default_levels():
+    logging.debug('debug message')
+    logging.info('%s %s', 'info', 'message')
+    logging.warning('warning message')
+    # error and critical are considered warnings
+    logging.error('error message')
+    logging.critical('critical message')
+
+def log_with_custom_levels():
+    logging.log(logging.DEBUG-1, 'below debug')
+    logging.log(logging.INFO-1, 'between debug and info')
+    logging.log(logging.INFO+1, 'between info and warning')
+    logging.log(logging.WARNING*100, 'above warning')
+
+def log_using_custom_logger():
+    logging.getLogger('custom').info('custom logger')
+
+def log_using_non_propagating_logger():
+    logging.getLogger('nonprop').info('nonprop logger')
+
+def log_messages_different_time():
+    logging.info('First message')
+    time.sleep(0.1)
+    logging.info('Second message 0.1 sec later')
=======================================
--- /dev/null
+++ /atest/testdata/test_libraries/logging_with_logging.txt Tue May 17 14:28:11 2011
@@ -0,0 +1,22 @@
+*** Settings ***
+Documentation   Tests for logging using Python's `logging` module.
+Library         LibUsingPyLogging.py
+Suite Setup     Set log level  DEBUG
+Suite Teardown  Set log level  INFO
+
+*** Test Cases ***
+
+Log with default levels
+    Log with default levels
+
+Log with custom levels
+    Log with custom levels
+
+Log using custom logger
+    Log using custom logger
+
+Log using non-propagating logger
+    Log using non propagating logger
+
+Timestamps are accurate
+    Log messages different time
=======================================
--- /dev/null
+++ /src/robot/output/pyloggingconf.py  Tue May 17 14:28:11 2011
@@ -0,0 +1,52 @@
+#  Copyright 2008-2011 Nokia Siemens Networks Oyj
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+"""Module to configure Python's standard `logging` module.
+
+After this module is imported, messages logged with `logging` module
+are, by defaul, propagated to Robot's log file.
+"""
+
+import logging
+
+from robot.api import logger
+
+
+class RobotHandler(logging.Handler):
+
+    def emit(self, record):
+        self._get_logger_method(record.levelno)(record.getMessage())
+
+    def _get_logger_method(self, level):
+        if level >= logging.WARNING:
+            return  logger.warn
+        if level <= logging.DEBUG:
+            return logger.debug
+        return logger.info
+
+
+class NullStream(object):
+
+    def write(self, message):
+        pass
+
+    def close(self):
+        pass
+
+    def flush(self):
+        pass
+
+
+logging.basicConfig(level=logging.NOTSET, stream=NullStream())
+logging.getLogger().addHandler(RobotHandler())
=======================================
--- /src/robot/__init__.py      Mon Apr 18 21:30:37 2011
+++ /src/robot/__init__.py      Tue May 17 14:28:11 2011
@@ -41,7 +41,7 @@

 if 'pythonpathsetter' not in sys.modules:
     import pythonpathsetter
-from output import Output, LOGGER
+from output import Output, LOGGER, pyloggingconf
 from conf import RobotSettings, RebotSettings
 from running import TestSuite, STOP_SIGNAL_MONITOR
from serializing import RobotTestOutput, RebotTestOutput, SplitIndexTestOutput

Reply via email to