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

Add logging structure #30

Merged
merged 2 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ __pycache__
# graphviz flowchart files
*.gv
*.pdf

# logfiles
*.log
26 changes: 23 additions & 3 deletions rtdp/python/rtdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
Entry point of rdtp.
"""

import argparse
# Logging cookboook: https://docs.python.org/3/howto/logging-cookbook.html
# import logging
import logging
import argparse

from rtdp_config_parser import ERSAPReader
from rtdp_dash_cyto import get_dash_app
Expand All @@ -15,6 +15,7 @@
"rtdp: JLab's streaming readout RealTime Development and testing Platform."
RTDP_CLI_APP_URL_STR = "https://github.com/JeffersonLab/SRO-RTDP"
RTDP_CLI_APP_VERSION_STR = "0.0"
RTDP_CLI_DEFAULT_LOGFILE = "rtdp.log"

def get_parser():
"""Define the application arguments. Create the ArgumentParser object and return it.
Expand All @@ -29,21 +30,40 @@ def get_parser():
)
parser.add_argument('-v', '--version', action='version',
version=f'%(prog)s {RTDP_CLI_APP_VERSION_STR}')
parser.add_argument('--log_file', type=str,
default=RTDP_CLI_DEFAULT_LOGFILE, help='log file name')
parser.add_argument('--log_level', type=str, default='debug',
choices=['debug', 'info', 'warning', 'error', 'critical'],
help='log level: debug, info, warning, error, critical')
parser.add_argument('config_file', nargs='?',
help='path to your YAML configuration file')
return parser


def setup_logging(log_file, log_level):
"""Setup the logging instance."""
numeric_level = getattr(logging, log_level.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError(f'Invalid log level: {log_level}')

logging.basicConfig(filename=log_file, level=numeric_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.info("Start application rtdp")



def run_rtdp(parser):
"""Proocess the cli inputs.

Args:
- parser (argparse.ArgumentParser): The created argument parser.
"""
args = parser.parse_args()

setup_logging(args.log_file, args.log_level)

if args.config_file:
# TODO: using ERSAP reader here. Should be generalized.
# TODO: not a service launching yet.
configurations = ERSAPReader(args.config_file)
ersap_nodes = configurations.get_flowchart_nodes()

Expand Down
14 changes: 11 additions & 3 deletions rtdp/python/rtdp_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@

from abc import ABC, abstractmethod
import sys
import logging
import json
import yaml
from graphviz import Digraph


logger = logging.getLogger(__name__)


class ConfigReader(ABC):
"""Abstract base class for reading ERSAP/RTDP configuration YAML files."""

def __init__(self, filepath):
self.config_data = self._get_config(filepath)
print(f"\nStarting the platform using the specified YAML configuration file: {filepath}")
logger.info("Parsed the yaml configuration file at %s", filepath)
# self._pprint_config()

def _get_config(self, filepath):
Expand All @@ -28,7 +32,7 @@ def _get_config(self, filepath):
Returns:
- config_data : A dictionary containing the configuration data.
"""
with open(filepath, 'r') as file:
with open(filepath, 'r', encoding="utf-8") as file:
try:
config_data = yaml.safe_load(file)
return config_data
Expand All @@ -48,7 +52,7 @@ def get_flowchart_nodes(self):
def graphviz_flowchart(self):
"""Visualize the configuration file that the name of each service
is treated as a node in a DAG."""
# TODO: this function is not called by the main now.
# This function is not called by the main now.
node_list = self.get_flowchart_nodes()

# graphviz examples: https://graphviz.readthedocs.io/en/stable/examples.html
Expand All @@ -67,6 +71,8 @@ def graphviz_flowchart(self):


class ERSAPFlowchartNode:
"""Definition of an ERSAP node."""

def __init__(self, item):
"""Definition of an ERSAP flowchart node. Currently it's only for visulization.
It may support services launching in the future (needs to figure out parameter
Expand All @@ -90,6 +96,7 @@ def _validate_required(self, item):


class ERSAPReader(ConfigReader):
"""Rules to load and parse ERSAP yaml configuration file."""

def _validate_ioservices(self):
"""Validate the config yaml file has "io-services" and its sub "reader" and "writer."""
Expand Down Expand Up @@ -127,6 +134,7 @@ def get_flowchart_nodes(self):
return node_list

def print_nodes(self):
"""Print all the ERSAP service nodes."""
node_list = self.get_flowchart_nodes()

print("\n\nThe ERSAP services:\n")
Expand Down
12 changes: 8 additions & 4 deletions rtdp/python/rtdp_dash_cyto.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
Layouts and callbacks of the Dash application using dash_cytoscape library.
"""

import logging
# Dash modules. Ref: https://dash.plotly.com
from dash import html, Dash, Input, Output, callback
# Dash cytoscape. Ref: https://dash.plotly.com/cytoscape
import dash_cytoscape as cyto

logger = logging.getLogger(__name__)

# dash_cytoscape stylesheets
cyto_display_stylesheet_config_flowchart=[
Expand Down Expand Up @@ -65,7 +67,7 @@ def get_cytoscape_elements(node_list):
'data': {'source': str(i), 'target': str(i + 1)},
'selectable': False
})

logger.info("Cytoscape elements created")
return r


Expand Down Expand Up @@ -98,22 +100,24 @@ def get_dash_app(nodes):
stylesheet=cyto_display_stylesheet_config_flowchart
)
]),
# Ref: https://dash.plotly.com/cytoscape/events

# Display the info about the selected ERSAP flowchart node using the callback function.
html.Div([
html.Pre(id='cyto-tapNode-resp', style=styles['pre'])
])
])


# Ref: https://dash.plotly.com/cytoscape/events
@callback(Output('cyto-tapNode-resp', 'children'),
Input('cyto-display-config-flowchart', 'tapNodeData'))
def display_tap_config_node(data):
if not data:
return html.H4("No service selected.")

logger.debug("On config flowchart, node-%s selected", data['id'])
node_id = int(data['id'])
node_info = nodes[node_id]
# TODO: better CSS style

return html.Div([
html.H4(f"Selected service: {data['label']}"),
html.P(f" class: {node_info.cls}\n language: {node_info.lan}\n")
Expand Down