Esempio n. 1
0
def playback_log(log_path, file_like, show_esc=False):
    """
    Plays back the log file at *log_path* by way of timely output to *file_like*
    which is expected to be any file-like object with write() and flush()
    methods.

    If *show_esc* is True, escape sequences and control characters will be
    escaped so they can be seen in the output.  There will also be no delay
    between the output of frames (under the assumption that if you want to see
    the raw log you want it to output all at once so you can pipe it into
    some other app).
    """
    prev_frame_time = None
    try:
        for count, frame in enumerate(get_frames(log_path)):
            frame_time = float(frame[:13]) # First 13 chars is the timestamp
            frame = frame[14:] # [14:] Skips the timestamp and the colon
            if count == 0:
                # Write it out immediately
                if show_esc:
                    frame = raw(frame)
                file_like.write(frame)
                prev_frame_time = frame_time
            else:
                if show_esc:
                    frame = raw(frame)
                else:
                    # Wait until the time between the previous frame and now
                    # has passed
                    wait_time = (frame_time - prev_frame_time)/1000.0
                    sleep(wait_time) # frame times are in milliseconds
                file_like.write(frame)
                prev_frame_time = frame_time
            file_like.flush()
    except IOError: # Something wrong with the file
        return
Esempio n. 2
0
def playback_log(log_path, file_like, show_esc=False):
    """
    Plays back the log file at *log_path* by way of timely output to *file_like*
    which is expected to be any file-like object with write() and flush()
    methods.

    If *show_esc* is True, escape sequences and control characters will be
    escaped so they can be seen in the output.  There will also be no delay
    between the output of frames (under the assumption that if you want to see
    the raw log you want it to output all at once so you can pipe it into
    some other app).
    """
    prev_frame_time = None
    try:
        for count, frame in enumerate(get_frames(log_path)):
            frame_time = float(frame[:13])  # First 13 chars is the timestamp
            frame = frame[14:]  # [14:] Skips the timestamp and the colon
            if count == 0:
                # Write it out immediately
                if show_esc:
                    frame = raw(frame)
                file_like.write(frame)
                prev_frame_time = frame_time
            else:
                if show_esc:
                    frame = raw(frame)
                else:
                    # Wait until the time between the previous frame and now
                    # has passed
                    wait_time = (frame_time - prev_frame_time) / 1000.0
                    sleep(wait_time)  # frame times are in milliseconds
                file_like.write(frame)
                prev_frame_time = frame_time
            file_like.flush()
    except IOError:  # Something wrong with the file
        return
Esempio n. 3
0
def flatten_log(log_path, file_like, preserve_renditions=True, show_esc=False):
    """
    Given a log file at *log_path*, write a string of log lines contained
    within to *file_like*.  Where *file_like* is expected to be any file-like
    object with write() and flush() methods.

    If *preserve_renditions* is True, CSI escape sequences for renditions will
    be preserved as-is (e.g. font color, background, etc).  This is to make the
    output appear as close to how it was originally displayed as possible.
    Besides that, it looks really nice =)

    If *show_esc* is True, escape sequences and control characters will be
    visible in the output.  Trailing whitespace and escape sequences will not be
    removed.

    ..note::

        Converts our standard recording-based log format into something that
        can be used with grep and similar search/filter tools.
    """
    from terminal import Terminal, SPECIAL
    metadata = get_log_metadata(log_path)
    rows = metadata.get('rows', 24)
    cols = metadata.get('columns', None)
    if not cols:
        # Try the old metadata format which used 'cols':
        cols = metadata.get('cols', 80)
    term = Terminal(rows=rows, cols=cols, em_dimensions=0)
    out_line = u""
    cr = False
    # We skip the first frame, [1:] because it holds the recording metadata
    for count, frame in enumerate(get_frames(log_path)):
        if count == 0:
            # Skip the first frame (it's just JSON-encoded metadata)
            continue
        # First 13 chars is the timestamp:
        frame_time = float(frame.decode('UTF-8', 'ignore')[:13])
        # Convert to datetime object
        frame_time = datetime.fromtimestamp(frame_time/1000)
        if show_esc:
            frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S')
        else: # Renditions preserved == I want pretty.  Make the date bold:
            frame_time = frame_time.strftime(u'\x1b[0;1m%b %m %H:%M:%S\x1b[m')
        if not show_esc:
            term.write(frame[14:])
        if term.capture:
            # Capturing a file...  Keep feeding it frames until complete
            continue
        elif term.captured_files:
            for line in term.screen:
                # Find all the characters that come before/after the capture
                for char in line:
                    if ord(char) >= SPECIAL:
                        adjusted = escape_escape_seq(out_line, rstrip=True)
                        adjusted = frame_time + u' %s\n' % adjusted
                        file_like.write(adjusted.encode('utf-8'))
                        out_line = u""
                        if char in term.captured_files:
                            captured_file = term.captured_files[char].file_obj
                            captured_file.seek(0)
                            file_like.write(captured_file.read())
                            file_like.write(b'\n')
                            del captured_file
                            term.clear_screen()
                            term.close_captured_fds() # Instant cleanup
                    else:
                        out_line += char
            if not out_line:
                continue
            adjusted = frame_time + u' %s\n' % out_line.strip()
            file_like.write(adjusted.encode('utf-8'))
            out_line = u""
            continue
        else:
            term.clear_screen()
        frame = frame.decode('UTF-8', 'ignore')
        for char in frame[14:]:
            if '\x1b[H\x1b[2J' in out_line: # Clear screen sequence
                # Handle the clear screen (usually ctrl-l) by outputting
                # a new log entry line to avoid confusion regarding what
                # happened at this time.
                out_line += u"^L" # Clear screen is a ctrl-l or equivalent
                if show_esc:
                    adjusted = raw(out_line)
                else:
                    adjusted = escape_escape_seq(out_line, rstrip=True)
                adjusted = frame_time + u' %s\n' % adjusted
                file_like.write(adjusted.encode('utf-8'))
                out_line = u""
                continue
            if char == u'\n':
                if show_esc:
                    adjusted = raw(out_line)
                else:
                    adjusted = escape_escape_seq(out_line, rstrip=True)
                if not adjusted:
                    out_line = u"" # Skip empty lines
                    continue
                adjusted = frame_time + u' %s\n' % adjusted
                file_like.write(adjusted.encode('utf-8'))
                out_line = u""
                cr = False
            elif char == u'\r':
                # Carriage returns need special handling.  Make a note of it
                cr = True
            else:
                # \r without \n means that characters were (likely)
                # overwritten.  This usually happens when the user gets to
                # the end of the line (which would create a newline in the
                # terminal but not necessarily the log), erases their
                # current line (e.g. ctrl-u), or an escape sequence modified
                # the line in-place.  To clearly indicate what happened we
                # insert a '^M' and start a new line so as to avoid
                # confusion over these events.
                if cr:
                    out_line += "^M"
                    file_like.write((frame_time + u' ').encode('utf-8'))
                    if show_esc:
                        adjusted = raw(out_line)
                    else:
                        adjusted = escape_escape_seq(out_line, rstrip=True)
                    file_like.write((adjusted + u'\n').encode('utf-8'))
                    out_line = u""
                out_line += char
                cr = False
        file_like.flush()
    del term
Esempio n. 4
0
def escape_escape_seq(text, preserve_renditions=True, rstrip=True):
    """
    Escapes escape sequences so they don't muck with the terminal viewing *text*
    Also replaces special characters with unicode symbol equivalents (e.g. so
    you can see what they are without having them do anything to your running
    shell)

    If *preserve_renditions* is True, CSI escape sequences for renditions will
    be preserved as-is (e.g. font color, background, etc).

    If *rstrip* is true, trailing escape sequences and whitespace will be
    removed.
    """
    esc_sequence = re.compile(
        r'\x1b(.*\x1b\\|[ABCDEFGHIJKLMNOQRSTUVWXYZa-z0-9=]|[()# %*+].)')
    csi_sequence = re.compile(r'\x1B\[([?A-Za-z0-9;@:\!]*)([A-Za-z@_])')
    #esc_rstrip = re.compile('[ \t]+\x1b.+$')
    out = u""
    esc_buffer = u""
    # If this seems confusing it is because text parsing is a black art! ARRR!
    for char in text:
        if not esc_buffer:
            if char == u'\x1b': # Start of an ESC sequence
                esc_buffer = char
            # TODO: Determine if we should bring this back:
            #elif ord(char) in replacement_map:
                #out += replacement_map[ord(char)]
            else: # Vanilla char.  Booooring.
                out += raw(char)
        else: # Something interesting is going on
            esc_buffer += char
            if char == u'\x07' or esc_buffer.endswith(u'\x1b\\'): # Likely title
                esc_buffer = u'' # Nobody wants to see your naked ESC sequence
                continue
            elif esc_buffer.endswith('\x1b\\'):
                esc_buffer = u'' # Ignore
                continue
            # Nobody wants to see plain ESC sequences in the buf...
            match_obj = esc_sequence.match(esc_buffer)
            if match_obj:
                #seq_type = match_obj.group(1)
                esc_buffer = u'' # Just when you thought you've ESC'd...
                continue
            # CSI ESC sequences...  These are worth a second look
            match_obj = csi_sequence.match(esc_buffer)
            if match_obj:
                csi_type = match_obj.group(2)
                if csi_type == 'm' and preserve_renditions: # mmmmmm!
                    out += esc_buffer # Ooh, naked viewing of pretty things!
                elif csi_type == 'C': # Move cursor right (we want to do this)
                    # Will be something like this: \x1b[208C
                    num_spaces = match_obj.group(1)
                    if not num_spaces:
                        num_spaces = 1
                    spaces = int(num_spaces)
                    out += u' ' * spaces # Add an equivalent amount of spaces
                esc_buffer = u'' # Make room for more!
                continue
    if rstrip:
        # Remove trailing whitespace + trailing ESC sequences
        return out.rstrip()
    else: # All these trailers better make for a good movie
        return out
Esempio n. 5
0
def flatten_log(log_path, file_like, preserve_renditions=True, show_esc=False):
    """
    Given a log file at *log_path*, write a string of log lines contained
    within to *file_like*.  Where *file_like* is expected to be any file-like
    object with write() and flush() methods.

    If *preserve_renditions* is True, CSI escape sequences for renditions will
    be preserved as-is (e.g. font color, background, etc).  This is to make the
    output appear as close to how it was originally displayed as possible.
    Besides that, it looks really nice =)

    If *show_esc* is True, escape sequences and control characters will be
    visible in the output.  Trailing whitespace and escape sequences will not be
    removed.

    ..note::

        Converts our standard recording-based log format into something that
        can be used with grep and similar search/filter tools.
    """
    from terminal import Terminal, SPECIAL
    metadata = get_log_metadata(log_path)
    rows = metadata.get('rows', 24)
    cols = metadata.get('columns', None)
    if not cols:
        # Try the old metadata format which used 'cols':
        cols = metadata.get('cols', 80)
    term = Terminal(rows=rows, cols=cols, em_dimensions=0)
    out_line = u""
    cr = False
    # We skip the first frame, [1:] because it holds the recording metadata
    for count, frame in enumerate(get_frames(log_path)):
        if count == 0:
            # Skip the first frame (it's just JSON-encoded metadata)
            continue
        # First 13 chars is the timestamp:
        frame_time = float(frame.decode('UTF-8', 'ignore')[:13])
        # Convert to datetime object
        frame_time = datetime.fromtimestamp(frame_time / 1000)
        if show_esc:
            frame_time = frame_time.strftime(u'\x1b[0m%b %m %H:%M:%S')
        else:  # Renditions preserved == I want pretty.  Make the date bold:
            frame_time = frame_time.strftime(u'\x1b[0;1m%b %m %H:%M:%S\x1b[m')
        if not show_esc:
            term.write(frame[14:])
        if term.capture:
            # Capturing a file...  Keep feeding it frames until complete
            continue
        elif term.captured_files:
            for line in term.screen:
                # Find all the characters that come before/after the capture
                for char in line:
                    if ord(char) >= SPECIAL:
                        adjusted = escape_escape_seq(out_line, rstrip=True)
                        adjusted = frame_time + u' %s\n' % adjusted
                        file_like.write(adjusted.encode('utf-8'))
                        out_line = u""
                        if char in term.captured_files:
                            captured_file = term.captured_files[char].file_obj
                            captured_file.seek(0)
                            file_like.write(captured_file.read())
                            file_like.write(b'\n')
                            del captured_file
                            term.clear_screen()
                            term.close_captured_fds()  # Instant cleanup
                    else:
                        out_line += char
            if not out_line:
                continue
            adjusted = frame_time + u' %s\n' % out_line.strip()
            file_like.write(adjusted.encode('utf-8'))
            out_line = u""
            continue
        else:
            term.clear_screen()
        frame = frame.decode('UTF-8', 'ignore')
        for char in frame[14:]:
            if '\x1b[H\x1b[2J' in out_line:  # Clear screen sequence
                # Handle the clear screen (usually ctrl-l) by outputting
                # a new log entry line to avoid confusion regarding what
                # happened at this time.
                out_line += u"^L"  # Clear screen is a ctrl-l or equivalent
                if show_esc:
                    adjusted = raw(out_line)
                else:
                    adjusted = escape_escape_seq(out_line, rstrip=True)
                adjusted = frame_time + u' %s\n' % adjusted
                file_like.write(adjusted.encode('utf-8'))
                out_line = u""
                continue
            if char == u'\n':
                if show_esc:
                    adjusted = raw(out_line)
                else:
                    adjusted = escape_escape_seq(out_line, rstrip=True)
                if not adjusted:
                    out_line = u""  # Skip empty lines
                    continue
                adjusted = frame_time + u' %s\n' % adjusted
                file_like.write(adjusted.encode('utf-8'))
                out_line = u""
                cr = False
            elif char == u'\r':
                # Carriage returns need special handling.  Make a note of it
                cr = True
            else:
                # \r without \n means that characters were (likely)
                # overwritten.  This usually happens when the user gets to
                # the end of the line (which would create a newline in the
                # terminal but not necessarily the log), erases their
                # current line (e.g. ctrl-u), or an escape sequence modified
                # the line in-place.  To clearly indicate what happened we
                # insert a '^M' and start a new line so as to avoid
                # confusion over these events.
                if cr:
                    out_line += "^M"
                    file_like.write((frame_time + u' ').encode('utf-8'))
                    if show_esc:
                        adjusted = raw(out_line)
                    else:
                        adjusted = escape_escape_seq(out_line, rstrip=True)
                    file_like.write((adjusted + u'\n').encode('utf-8'))
                    out_line = u""
                out_line += char
                cr = False
        file_like.flush()
    del term
Esempio n. 6
0
def escape_escape_seq(text, preserve_renditions=True, rstrip=True):
    """
    Escapes escape sequences so they don't muck with the terminal viewing *text*
    Also replaces special characters with unicode symbol equivalents (e.g. so
    you can see what they are without having them do anything to your running
    shell)

    If *preserve_renditions* is True, CSI escape sequences for renditions will
    be preserved as-is (e.g. font color, background, etc).

    If *rstrip* is true, trailing escape sequences and whitespace will be
    removed.
    """
    esc_sequence = re.compile(
        r'\x1b(.*\x1b\\|[ABCDEFGHIJKLMNOQRSTUVWXYZa-z0-9=]|[()# %*+].)')
    csi_sequence = re.compile(r'\x1B\[([?A-Za-z0-9;@:\!]*)([A-Za-z@_])')
    #esc_rstrip = re.compile('[ \t]+\x1b.+$')
    out = u""
    esc_buffer = u""
    # If this seems confusing it is because text parsing is a black art! ARRR!
    for char in text:
        if not esc_buffer:
            if char == u'\x1b':  # Start of an ESC sequence
                esc_buffer = char
            # TODO: Determine if we should bring this back:
            #elif ord(char) in replacement_map:
            #out += replacement_map[ord(char)]
            else:  # Vanilla char.  Booooring.
                out += raw(char)
        else:  # Something interesting is going on
            esc_buffer += char
            if char == u'\x07' or esc_buffer.endswith(
                    u'\x1b\\'):  # Likely title
                esc_buffer = u''  # Nobody wants to see your naked ESC sequence
                continue
            elif esc_buffer.endswith('\x1b\\'):
                esc_buffer = u''  # Ignore
                continue
            # Nobody wants to see plain ESC sequences in the buf...
            match_obj = esc_sequence.match(esc_buffer)
            if match_obj:
                #seq_type = match_obj.group(1)
                esc_buffer = u''  # Just when you thought you've ESC'd...
                continue
            # CSI ESC sequences...  These are worth a second look
            match_obj = csi_sequence.match(esc_buffer)
            if match_obj:
                csi_type = match_obj.group(2)
                if csi_type == 'm' and preserve_renditions:  # mmmmmm!
                    out += esc_buffer  # Ooh, naked viewing of pretty things!
                elif csi_type == 'C':  # Move cursor right (we want to do this)
                    # Will be something like this: \x1b[208C
                    num_spaces = match_obj.group(1)
                    if not num_spaces:
                        num_spaces = 1
                    spaces = int(num_spaces)
                    out += u' ' * spaces  # Add an equivalent amount of spaces
                esc_buffer = u''  # Make room for more!
                continue
    if rstrip:
        # Remove trailing whitespace + trailing ESC sequences
        return out.rstrip()
    else:  # All these trailers better make for a good movie
        return out