Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move logger into own class and separate from main script #8

Merged
merged 8 commits into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Any log message starting with `#Custom: ` is parsed and it will appear in the ou
### Adding a parser
The tool can be extended to implement custom parsers by following these steps:

1. Add the regular expression for the log message. Open *src/custom/logs.py* and append a new tuple with the following format to the `regex` variable:
1. Add the regular expression for the log message. Open *logparser/custom/logs.py* and append a new tuple with the following format to the `regex` variable:
```
regex.append([custom.FUNCTION_NAME_TO_CALL_IF_MATCHED, LOG_REGEX])
```
Expand Down
1 change: 0 additions & 1 deletion src/__main__.py → __main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/bin/python
# Log Parser for RTI Connext.
#
# Copyright 2016 Real-Time Innovations, Inc.
Expand Down
4 changes: 2 additions & 2 deletions create_redist.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/bin/bash
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OUT_FILE=${SCRIPT_DIR}/rtilogparser
pushd "$SCRIPT_DIR/src"
pushd "$SCRIPT_DIR"

# Create a zip file with all the python files.
# The python interpreter is able to read zip files and it will execute the
# content from __main__.py
zip -r "${OUT_FILE}.zip" . -i '*.py'
cd .. && zip "${OUT_FILE}.zip" LICENSE
zip "${OUT_FILE}.zip" LICENSE

echo '#!/usr/bin/env python' | cat - "${OUT_FILE}.zip" > "${OUT_FILE}"
chmod +x "${OUT_FILE}"
Expand Down
20 changes: 20 additions & 0 deletions logparser/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Log Parser for RTI Connext.
#
# Copyright 2016 Real-Time Innovations, Inc.
#
# 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.
"""Package LogParser."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Package LogParser or LogParser Package?


__version__ = "1.2a2"
__license__ = "Apache"
__copyright__ = "Copyright 2016 Real-Time Innovations, Inc."
File renamed without changes.
2 changes: 1 addition & 1 deletion src/custom/custom.py → logparser/custom/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
+ on_custom_log: Parse a log with a custom prefix.
"""
from __future__ import absolute_import
from devices.logger import log_event
from logparser.devices.logger import log_event


def on_custom_log(match, state):
Expand Down
2 changes: 1 addition & 1 deletion src/custom/logs.py → logparser/custom/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
+ CUSTOM_PREFIX: Prefix for custom logs.
"""
from __future__ import absolute_import
import custom.custom as custom
import logparser.custom.custom as custom

CUSTOM_PREFIX = "#Custom: "

Expand Down
File renamed without changes.
1 change: 0 additions & 1 deletion src/debug/debug.py → logparser/debug/debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
+ on_unmatched_message: write into a file the unmatched log
+ on_ignored_message: ignore this matched log.
"""
from __future__ import absolute_import


UNMATCHED_LOG_FILENAME = "unmatched.txt"
Expand Down
2 changes: 1 addition & 1 deletion src/debug/logs.py → logparser/debug/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
+ get_regex_list: Get the regular expressions and function list.
"""
from __future__ import absolute_import
import debug.debug as debug
import logparser.debug.debug as debug
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should do something with this

Copy link
Contributor Author

@pleonex pleonex Dec 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will discuss the import statements in the log files in #13.



def get_regex_list():
Expand Down
File renamed without changes.
65 changes: 65 additions & 0 deletions logparser/devices/formatdevice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Log Parser for RTI Connext.
#
# Copyright 2016 Real-Time Innovations, Inc.
#
# 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.
"""Device for the output format.

The module contains the abstract class representation for a formatter of the
parsed logs.

Classes:
+ FormatDevice: Abstract base class for format device implementations.
"""


class FormatDevice(object):
"""Abstract base class for format device implementations.

You will need to implement the following methods:
+ write_header: write the header if any.
+ write_configurations: write the configuration messages.
+ write_warnings: write the warning messages.
+ write_errors: write the error messages.
"""

def write_header(self, state):
"""Write the header if any."""
raise NotImplementedError("write_header not implemented")

def write_message(self, content, state):
"""Write the message.

The content argument is a dictionary with at least 'description' item.
The optional items are:
+ kind: the kind or remark for the message.
+ timestamp: the timestamp of the message.
+ input_line: the current input line.
+ output_line: the current output line.
+ inout: [packets-only] 'in' if it's input packet, 'out' otherwise.
+ remote: [packets-only] the remote address of the sender/receiver.
+ entity: [packets-only] the local entity sending/receiving.
"""
raise NotImplementedError("write_message not implemented")

def write_configurations(self, state):
"""Write the configuration messages."""
raise NotImplementedError("write_configurations not implemented")

def write_warnings(self, state):
"""Write the warning messages."""
raise NotImplementedError("write_warnings not implemented")

def write_errors(self, state):
"""Write the error messages."""
raise NotImplementedError("write_errors not implemented")
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
+ InputConsoleDevice: Reads the DDS log messages from the standard input.
+ InputFileDevice: Reads the DDS log messages from a file.
"""
from __future__ import print_function
from __future__ import absolute_import, print_function
from os import fstat
from sys import stdin, stdout
from time import time
from devices.outputdevices import OutputConsoleDevice
from logparser.devices.outputdevices import OutputConsoleDevice


class InputDevice(object):
Expand Down
166 changes: 166 additions & 0 deletions logparser/devices/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Log Parser for RTI Connext.
#
# Copyright 2016 Real-Time Innovations, Inc.
#
# 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.
"""Logger functions.

The module contains functions to log the new messages.
The 'content' dictionary is defined in the FormatDevice class.

Functions:
+ log: log the given message.
+ log_recv: log a received packet.
+ log_send: log a sent packet.
+ log_process: log a processed packet.
+ log_cfg: log a configuration message.
+ log_warning: log a warning message.
+ log_error: log an error.
+ countset_add_element: add an element to the countset.
+ dict_regex_search: apply the regex over all the fields of the dictionary.

Constants:
+ COLORS: ANSI colors to use in log messages.
+ KIND_TO_COLOR: conversion between log kind and ANSI color
"""


COLORS = {
'RED': '\033[91m',
'GREEN': '\033[92m',
'YELLOW': '\033[93m',
'BLUE': '\033[94m',
'MAGENTA': '\033[95m',
'BOLD': '\033[1m',
'FAINT': '\033[2m',
'ITALIC': '\033[3m',
'UNDERLINE': '\033[4m',
'END': '\033[0m',
}

KIND_TO_COLOR = {
'WARNING': 'YELLOW|ITALIC',
'ERROR': 'RED|BOLD',
'IMPORTANT': 'BOLD'
}


def log(content, level, state):
"""Log the given message."""
if state['verbosity'] < level:
return

# Add the clock if available
if 'clocks' in state and state['clocks'][1]:
content['timestamp'] = " %s " % state['clocks'][1].isoformat()

# Add the current line
content['input_line'] = state['input_line']
content['output_line'] = state['output_line'] + 1 # This message count

# Apply the filter
if 'onlyIf' in state and not dict_regex_search(content, state['onlyIf']):
return

# Highlight the message if match
if 'highlight' in state and dict_regex_search(content, state['highlight']):
content['kind'] = content.get('kind', "") + "|IMPORTANT"

# Apply color if specified
if not state['no_colors']:
color = ""
for kind in filter(None, content.get('kind', '').split("|")):
for subkind in KIND_TO_COLOR[kind].split("|"):
color += COLORS[subkind]
if len(color) > 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary the comparation

if len(color):

content['description'] = color + content['description'] + \
COLORS['END']

# Write the message
state['format_device'].write_message(content, state)


def log_recv(addr, entity, text, state, level=0):
"""Log a received packet."""
if state['ignore_packets']:
return
content = {'description': text, 'remote': addr, 'entity': entity,
'inout': 'in'}
log(content, level, state)


def log_send(addr, entity, text, state, level=0):
"""Log a sent packet."""
if state['ignore_packets']:
return
content = {'description': text, 'remote': addr, 'entity': entity,
'inout': 'out'}
log(content, level, state)


def log_process(addr, entity, text, state, level=0):
"""Log a processed packet."""
if state['ignore_packets']:
return
content = {'description': text, 'remote': addr, 'entity': entity}
log(content, level, state)


def log_cfg(text, state, level=0):
"""Log a configuration message."""
if state['verbosity'] < level:
return
countset_add_element(state['config'], text)


def log_event(text, state, level=0):
"""Log an application event."""
content = {'description': text}
log(content, level, state)


def log_warning(text, state, level=0):
"""Log a warning message."""
if state['verbosity'] < level:
return

countset_add_element(state['warnings'], text)
if state['inline']:
content = {'description': "Warning: " + text, 'kind': 'WARNING'}
log(content, level, state)


def log_error(text, state, level=0):
"""Log an error."""
if state['verbosity'] < level:
return

countset_add_element(state['errors'], text)
if state['inline']:
content = {'description': "Error: " + text, 'kind': 'ERROR'}
log(content, level, state)


def countset_add_element(countset, el):
"""Add an element to the countset."""
if el not in countset:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are doing this because you don't want have "el" repeated in the countset, true? So, we should use a set, not a list:
https://docs.python.org/2/library/stdtypes.html#set

(countset --> count-set --> count set. Oh! Wait!! It is a set!) 💃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CountSet is a class that count the number of times that a given entry is added. Every entry is in the object once and it has an ID and count. We will refactor that code for #12.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect

countset[el] = [len(countset), 0]
countset[el][1] += 1


def dict_regex_search(content, regex):
"""Apply the regex over all the fields of the dictionary."""
match = False
for field in content:
match = match if match else regex.search(field)
return match
Loading