Merge ~raharper/cloud-init:logging-gmtime into cloud-init:master

Proposed by Ryan Harper
Status: Merged
Approved by: Scott Moser
Approved revision: 7c790cbcc882dc164a8fbfdb0b352c18b74614b3
Merged at revision: 556a0220734097aa4e9fbfd93c8f263684232b3b
Proposed branch: ~raharper/cloud-init:logging-gmtime
Merge into: cloud-init:master
Diff against target: 87 lines (+63/-0)
2 files modified
cloudinit/log.py (+5/-0)
tests/unittests/test_log.py (+58/-0)
Reviewer Review Type Date Requested Status
Scott Moser Approve
Server Team CI bot continuous-integration Approve
Chad Smith Approve
Review via email: mp+329122@code.launchpad.net

Description of the change

Configure logging module to always use UTC time.

Currently the python logging module will default to a local time which may contain an TZ offset in the values it produces, but the logged time format
does not contain the offset. Switching to UTC time for logging produces
consistent values in the cloud-init.log file and avoids issues when the timezone is changed during boot.

LP: #1713158

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:e8c2885ff24e74af027dc297bed85c1d744c039c
https://jenkins.ubuntu.com/server/job/cloud-init-ci/149/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/149/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

Looks good, I'm going to keep beating the "unit test coverage" drum on this. Can we add an initial unit test for log in
cloudinit/tests/test_log for this? I'd like to get to a point where we have at least some unit test coverage on every module in cloudinit.
Maybe this nit isn't worth it though, your call.

I tested on systems w/ localtime set mountain and things look good.

review: Approve
Revision history for this message
Chad Smith (chad.smith) wrote :

minor flake8 fix as you'll see from CI

Revision history for this message
Scott Moser (smoser) wrote :

lets add a unit test and then i approve.

just do a log, and verify the format comes out correctly.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:89508f822bfb81f831bf04bad03175c482ef994b
https://jenkins.ubuntu.com/server/job/cloud-init-ci/187/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/187/rebuild

review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

I think your test case has fence post errors at the end of a minute. I, if we read it now() in the 59th second and the log message happens in the next second the test will fail. Right?

One way to combat this would be to get a before timestamp and an after timestamp. Then parse the log message time stamp and assert that before >= log >= after.

Revision history for this message
Ryan Harper (raharper) wrote :

On Tue, Aug 22, 2017 at 10:17 PM, Scott Moser <email address hidden> wrote:

> I think your test case has fence post errors at the end of a minute. I, if
> we read it now() in the 59th second and the log message happens in the next
> second the test will fail. Right?
>
> One way to combat this would be to get a before timestamp and an after
> timestamp. Then parse the log message time stamp and assert that before >=
> log >= after.
>

Good suggestion. I'll fix

>
> --
> https://code.launchpad.net/~raharper/cloud-init/+git/
> cloud-init/+merge/329122
> You are the owner of ~raharper/cloud-init:logging-gmtime.
>

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:7c790cbcc882dc164a8fbfdb0b352c18b74614b3
https://jenkins.ubuntu.com/server/job/cloud-init-ci/189/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/189/rebuild

review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

As long as AssertGreater and friends work in a python2.6 (tools/run-centos 6) i approve.
just make sure of that and then merge.
thanks!
(i do not know that they don't, i just hadn't used these before).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/log.py b/cloudinit/log.py
2index 3861709..1d75c9f 100644
3--- a/cloudinit/log.py
4+++ b/cloudinit/log.py
5@@ -19,6 +19,8 @@ import sys
6 import six
7 from six import StringIO
8
9+import time
10+
11 # Logging levels for easy access
12 CRITICAL = logging.CRITICAL
13 FATAL = logging.FATAL
14@@ -32,6 +34,9 @@ NOTSET = logging.NOTSET
15 # Default basic format
16 DEF_CON_FORMAT = '%(asctime)s - %(filename)s[%(levelname)s]: %(message)s'
17
18+# Always format logging timestamps as UTC time
19+logging.Formatter.converter = time.gmtime
20+
21
22 def setupBasicLogging(level=DEBUG):
23 root = logging.getLogger()
24diff --git a/tests/unittests/test_log.py b/tests/unittests/test_log.py
25new file mode 100644
26index 0000000..68fb4b8
27--- /dev/null
28+++ b/tests/unittests/test_log.py
29@@ -0,0 +1,58 @@
30+# This file is part of cloud-init. See LICENSE file for license information.
31+
32+"""Tests for cloudinit.log """
33+
34+from .helpers import CiTestCase
35+from cloudinit.analyze.dump import CLOUD_INIT_ASCTIME_FMT
36+from cloudinit import log as ci_logging
37+import datetime
38+import logging
39+import six
40+import time
41+
42+
43+class TestCloudInitLogger(CiTestCase):
44+
45+ def setUp(self):
46+ # set up a logger like cloud-init does in setupLogging, but instead
47+ # of sys.stderr, we'll plug in a StringIO() object so we can see
48+ # what gets logged
49+ logging.Formatter.converter = time.gmtime
50+ self.ci_logs = six.StringIO()
51+ self.ci_root = logging.getLogger()
52+ console = logging.StreamHandler(self.ci_logs)
53+ console.setFormatter(logging.Formatter(ci_logging.DEF_CON_FORMAT))
54+ console.setLevel(ci_logging.DEBUG)
55+ self.ci_root.addHandler(console)
56+ self.ci_root.setLevel(ci_logging.DEBUG)
57+ self.LOG = logging.getLogger('test_cloudinit_logger')
58+
59+ def test_logger_uses_gmtime(self):
60+ """Test that log message have timestamp in UTC (gmtime)"""
61+
62+ # Log a message, extract the timestamp from the log entry
63+ # convert to datetime, and compare to a utc timestamp before
64+ # and after the logged message.
65+
66+ # Due to loss of precision in the LOG timestamp, subtract and add
67+ # time to the utc stamps for comparison
68+ #
69+ # utc_before: 2017-08-23 14:19:42.569299
70+ # parsed dt : 2017-08-23 14:19:43.069000
71+ # utc_after : 2017-08-23 14:19:43.570064
72+
73+ utc_before = datetime.datetime.utcnow() - datetime.timedelta(0, 0.5)
74+ self.LOG.error('Test message')
75+ utc_after = datetime.datetime.utcnow() + datetime.timedelta(0, 0.5)
76+
77+ # extract timestamp from log:
78+ # 2017-08-23 14:19:43,069 - test_log.py[ERROR]: Test message
79+ logstr = self.ci_logs.getvalue().splitlines()[0]
80+ timestampstr = logstr.split(' - ')[0]
81+ parsed_dt = datetime.datetime.strptime(timestampstr,
82+ CLOUD_INIT_ASCTIME_FMT)
83+
84+ self.assertLess(utc_before, parsed_dt)
85+ self.assertLess(parsed_dt, utc_after)
86+ self.assertLess(utc_before, utc_after)
87+ self.assertGreater(utc_after, parsed_dt)

Subscribers

People subscribed via source and target branches