Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5aa4de8e8963
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3cf67afeb191
Choose a head ref
  • 4 commits
  • 3 files changed
  • 1 contributor

Commits on Jan 26, 2016

  1. Copy the full SHA
    19c5e89 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    ded1e31 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    1fed38a View commit details
  4. Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    chris-huxtable Chris Huxtable
    Copy the full SHA
    3cf67af View commit details
Showing with 84 additions and 41 deletions.
  1. +2 −2 artiq/master/worker_impl.py
  2. +59 −38 artiq/protocols/logging.py
  3. +23 −1 artiq/tools.py
4 changes: 2 additions & 2 deletions artiq/master/worker_impl.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@

import artiq
from artiq.protocols import pipe_ipc, pyon
from artiq.tools import file_import
from artiq.tools import multiline_log_config, file_import
from artiq.master.worker_db import DeviceManager, DatasetManager, get_hdf5_output
from artiq.language.environment import is_experiment
from artiq.language.core import set_watchdog_factory, TerminationRequested
@@ -187,7 +187,7 @@ def render_diagnostic(self, diagnostic):
def main():
global ipc

logging.basicConfig(level=int(sys.argv[2]))
multiline_log_config(level=int(sys.argv[2]))
ipc = pipe_ipc.ChildComm(sys.argv[1])

start_time = None
97 changes: 59 additions & 38 deletions artiq/protocols/logging.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import asyncio
import logging
import re

from artiq.protocols.asyncio_server import AsyncioServer
from artiq.tools import TaskObject
from artiq.tools import TaskObject, MultilineFormatter


logger = logging.getLogger(__name__)
@@ -25,25 +26,51 @@ def log_with_name(name, *args, **kwargs):


def parse_log_message(msg):
for name, level in _name_to_level.items():
if msg.startswith(name + ":"):
remainder = msg[len(name) + 1:]
try:
idx = remainder.index(":")
except:
continue
return level, remainder[:idx], remainder[idx+1:]
return logging.INFO, "print", msg
lr = "|".join(_name_to_level.keys())
m = re.fullmatch('('+lr+')(<\d+>)?:([^:]*):(.*)', msg)
if m is None:
return 0, logging.INFO, "print", msg
level = _name_to_level[m.group(1)]
if m.group(2):
multiline = int(m.group(2)[1:-1]) - 1
else:
multiline = 0
name = m.group(3)
message = m.group(4)
return multiline, level, name, message


class LogParser:
def __init__(self, source_cb):
self.source_cb = source_cb
self.multiline_count = 0
self.multiline_level = None
self.multiline_name = None
self.multiline_message = None

def line_input(self, msg):
level, name, message = parse_log_message(msg)
log_with_name(name, level, message,
extra={"source": self.source_cb()})
if self.multiline_count:
self.multiline_message += "\n" + msg
self.multiline_count -= 1
if not self.multiline_count:
log_with_name(
self.multiline_name,
self.multiline_level,
self.multiline_message,
extra={"source": self.source_cb()})
self.multiline_level = None
self.multiline_name = None
self.multiline_message = None
else:
multiline, level, name, message = parse_log_message(msg)
if multiline:
self.multiline_count = multiline
self.multiline_level = level
self.multiline_name = name
self.multiline_message = message
else:
log_with_name(name, level, message,
extra={"source": self.source_cb()})

async def stream_task(self, stream):
while True:
@@ -65,15 +92,20 @@ async def stream_task(self, stream):
class Server(AsyncioServer):
"""Remote logging TCP server.
Takes one log entry per line, in the format:
source:levelno:name:message
Log entries are in the format:
source:levelno<total_lines>:name:message
continuation...
...continuation
"""
async def _handle_connection_cr(self, reader, writer):
try:
line = await reader.readline()
if line != _init_string:
return

source = None
parser = LogParser(lambda: source)

while True:
line = await reader.readline()
if not line:
@@ -83,20 +115,16 @@ async def _handle_connection_cr(self, reader, writer):
except:
return
line = line[:-1]
linesplit = line.split(":", 3)
if len(linesplit) != 4:
logger.warning("received improperly formatted message, "
"dropping connection")
return
source, level, name, message = linesplit
try:
level = int(level)
except:
logger.warning("received improperly formatted level, "
"dropping connection")
return
log_with_name(name, level, message,
extra={"source": source})
if parser.multiline_count:
parser.line_input(line)
else:
linesplit = line.split(":", maxsplit=1)
if len(linesplit) != 2:
logger.warning("received improperly formatted message, "
"dropping connection")
return
source, remainder = linesplit
parser.line_input(remainder)
finally:
writer.close()

@@ -123,19 +151,12 @@ def __init__(self, host, port, reconnect_timer=5.0, queue_size=1000,
logging.Handler.__init__(self, **kwargs)
self.host = host
self.port = port
self.setFormatter(logging.Formatter(
"%(name)s:%(message)s"))
self.setFormatter(MultilineFormatter())
self._queue = asyncio.Queue(queue_size)
self.reconnect_timer = reconnect_timer

def emit(self, record):
message = self.format(record)
for part in message.split("\n"):
part = "{}:{}:{}".format(record.source, record.levelno, part)
try:
self._queue.put_nowait(part)
except asyncio.QueueFull:
break
self._queue.put_nowait(record.source + ":" + self.format(record))

async def _do(self):
while True:
24 changes: 23 additions & 1 deletion artiq/tools.py
Original file line number Diff line number Diff line change
@@ -133,8 +133,30 @@ def simple_network_args(parser, default_port):
group.add_argument("--port-" + name, default=default, type=int,
help=h)

class MultilineFormatter(logging.Formatter):
def __init__(self):
logging.Formatter.__init__(
self, "%(levelname)s:%(name)s:%(message)s")

def format(self, record):
r = logging.Formatter.format(self, record)
linebreaks = r.count("\n")
if linebreaks:
i = r.index(":")
r = r[:i] + "<" + str(linebreaks + 1) + ">" + r[i:]
return r


def multiline_log_config(level):
root_logger = logging.getLogger()
root_logger.setLevel(level)
handler = logging.StreamHandler()
handler.setFormatter(MultilineFormatter())
root_logger.addHandler(handler)


def init_logger(args):
logging.basicConfig(level=logging.WARNING + args.quiet*10 - args.verbose*10)
multiline_log_config(level=logging.WARNING + args.quiet*10 - args.verbose*10)


def bind_address_from_args(args):