blob: 2274f7c01f7ae1a007939c86c1ac9cb0366bb3e5 [file]
#!/usr/bin/env vpython3
# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import logging
import os
import subprocess
import sys
import tempfile
import unittest
import unittest.mock as mock
import re
from io import StringIO
# Mutates sys.path.
import test_env
from utils import file_path
from utils import logging_utils
# PID YYYY-MM-DD HH:MM:SS.MMM
_LOG_HEADER = r'^%d \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d' % os.getpid()
_LOG_HEADER_PID = r'^\d+ \d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d'
class Test(unittest.TestCase):
# This test fails when running via test runner
# Need to run in sequential_test_runner.py as an executable
no_run = 1
def setUp(self):
super(Test, self).setUp()
self.tmp = tempfile.mkdtemp(prefix='logging_utils')
def tearDown(self):
try:
file_path.rmtree(self.tmp)
finally:
super(Test, self).tearDown()
def test_user_logs_function(self):
with mock.patch('sys.stdout', new=StringIO()) as fake_stdout:
logger = logging.Logger(name="relevance_logger")
logging_utils.set_user_level_logging(logger)
with mock.patch('logging.root', new=logger):
message = "I'm sorry %s"
logging_utils.user_logs(message, "dave")
self.assertTrue(fake_stdout.getvalue().endswith(message % "dave\n"))
def test_user_logs_filter(self):
with mock.patch('sys.stdout', new=StringIO()) as fake_stdout:
logger = logging.Logger(name="relevance_logger")
logging_utils.set_user_level_logging(logger)
message = "a message"
logger.log(logging_utils.USER_LOGS, message)
info_message = "info_message"
logger.info(info_message)
fake_stdout_contents = fake_stdout.getvalue()
self.assertRegex(fake_stdout_contents, message)
# Test implicitly tests logging_utils.ExactLevelFilter
# since logging_utils.ExactLevelFilter would prevent
# logging.info from being called
self.assertNotRegex(fake_stdout_contents, info_message)
def test_capture(self):
root = logging.RootLogger(logging.DEBUG)
with logging_utils.CaptureLogs('foo', root) as log:
root.debug('foo')
result = log.read()
expected = _LOG_HEADER + ': DEBUG foo\n$'
if sys.platform == 'win32':
expected = expected.replace('\n', '\r\n')
self.assertTrue(
re.match(expected.encode('utf-8'), result), (expected, result))
def test_prepare_logging(self):
root = logging.RootLogger(logging.DEBUG)
filepath = os.path.join(self.tmp, 'test.log')
logging_utils.prepare_logging(filepath, root)
root.debug('foo')
with open(filepath, 'rb') as f:
result = f.read()
# It'd be nice to figure out a way to ensure it's properly in UTC but it's
# tricky to do reliably.
expected = _LOG_HEADER + ' D: foo\n$'
self.assertTrue(
re.match(expected.encode('utf-8'), result), (expected, result))
def test_rotating(self):
# Create a rotating log. Create a subprocess then delete the file. Make sure
# nothing blows up.
# Everything is done in a child process because the called functions mutate
# the global state.
r = subprocess.call(
[sys.executable, '-u', 'phase1.py', self.tmp],
cwd=os.path.join(test_env.TESTS_DIR, 'logging_utils'))
self.assertEqual(0, r)
self.assertEqual({'shared.1.log'}, set(os.listdir(self.tmp)))
with open(os.path.join(self.tmp, 'shared.1.log'), 'rb') as f:
lines = f.read().splitlines()
expected = [
r' I: Parent1',
r' I: Child1',
r' I: Child2',
r' I: Parent2',
]
for e, l in zip(expected, lines):
ex = _LOG_HEADER_PID + e + '$'
self.assertTrue(re.match(ex.encode('utf-8'), l), (ex, l))
self.assertEqual(len(expected), len(lines))
if __name__ == '__main__':
test_env.main()