Skip to content

Commit db9ce0c

Browse files
committed
Add runfolder name to logger, remove timestamp
1 parent 02b1f5b commit db9ce0c

File tree

5 files changed

+67
-56
lines changed

5 files changed

+67
-56
lines changed

samplesheet_validator/__main__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import os
2+
import sys
3+
import logging
24
import argparse
35
from .samplesheet_validator import SamplesheetCheck
46
from .ss_logger import set_root_logger
7+
from .config import LOGGING_FORMATTER
58

69

710
def get_arguments():
@@ -98,10 +101,10 @@ def is_valid_dir(parser: argparse.ArgumentParser, dir: str) -> str:
98101
return dir
99102

100103

104+
101105
if __name__ == "__main__":
102106
parsed_args = get_arguments()
103-
if not parsed_args.no_stream_handler:
104-
set_root_logger() # Adds stream handler
107+
logger = set_root_logger(parsed_args.no_stream_handler)
105108
sscheck_obj = SamplesheetCheck(
106109
parsed_args.samplesheet_path,
107110
parsed_args.sequencer_ids,
@@ -111,3 +114,4 @@ def is_valid_dir(parser: argparse.ArgumentParser, dir: str) -> str:
111114
parsed_args.logdir,
112115
)
113116
sscheck_obj.ss_checks() # Carry out samplesheeet validation
117+

samplesheet_validator/config.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
TIMESTAMP = str(f"{datetime.datetime.now():%Y%m%d_%H%M%S}")
44

55
# Specifies the layout of log records in the final output
6-
LOGGING_FORMATTER = "%(asctime)s - SAMPLESHEET_VALIDATOR - %(levelname)s - %(message)s"
6+
LOGGING_FORMATTER = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
77

88
LOG_MSGS = {
99
"ss_present": "Samplesheet with supplied name exists (%s)",
@@ -14,11 +14,11 @@
1414
"sequencer_id_invalid": "Sequencer id not in allowed list (%s, %s)",
1515
"ss_not_empty": "Samplesheet is (>10 bytes)",
1616
"ss_empty": "Samplesheet empty (<10 bytes)",
17-
"found_header_line": "Line in samplesheet identified as a header line",
18-
"found_sample_line": "Line in samplesheet identified as containing a sample",
19-
"error_extracting_headers": "An error was encountered when extracting headers from the samplesheet: %s",
20-
"found_empty_line": "Line in samplesheet is an empty line",
21-
"col_extraction_error": "Exception raised while attempting to extract %s from sample line %s: %s",
17+
"found_header_line": "Line %s in samplesheet identified as a header line",
18+
"found_sample_line": "Line %s in samplesheet identified as containing a sample",
19+
"error_extracting_headers": "An error was encountered when extracting headers from the samplesheet, from line %s: %s",
20+
"found_empty_line": "Line %s in samplesheet is an empty line",
21+
"col_extraction_error": "Exception raised while attempting to extract %s from sample line %s, %s: %s",
2222
"headers_as_expected": "Expected headers present in samplesheet",
2323
"headers_err": "Header(/s) missing from [Data] section: '%s'",
2424
"samplenames_match": "All sample names and sample IDS match",

samplesheet_validator/samplesheet_validator.py

+30-21
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
"""
1313
import os
1414
import re
15+
import logging
1516
from typing import Union
1617
from . import config
1718
from .ss_logger import SSLogger
1819
from seglh_naming.sample import Sample
1920
from seglh_naming.samplesheet import Samplesheet
21+
import logging_tree
2022

2123

2224
class SamplesheetCheck:
@@ -42,6 +44,9 @@ class SamplesheetCheck:
4244
panels (list): Valid pan numbers
4345
tso_panels (list): Valid TSO pannumbers
4446
development_panels (list): Development pan numbers
47+
runfolder_name (str): Name of runfolder
48+
logfile_path (str): Path to use for logfile
49+
logger (logging.Logger): Logger
4550
4651
Methods:
4752
get_logger()
@@ -115,24 +120,24 @@ def __init__(
115120
self.panels = panels
116121
self.tso_panels = tso_panels
117122
self.dev_panno = dev_panno
123+
self.runfolder_name = (self.samplesheet_path.split("/")[-1]).split("_SampleSheet.csv")[0]
124+
self.logfile_path = f"{os.path.join(logdir, self.runfolder_name)}_samplesheet_validator.log"
125+
self.logger = self.get_logger()
126+
118127

119-
def get_logger(self):
128+
def get_logger(self) -> logging.Logger:
120129
"""
121130
Get logger for the class
122-
:return (object): Logger
131+
:return (object): Logger
123132
"""
124-
runfolder_name = (self.samplesheet_path.split("/")[-1]).split(
125-
"_SampleSheet.csv"
126-
)[0]
127-
logfile_path = f"{os.path.join(self.logdir, runfolder_name)}_{config.TIMESTAMP}_samplesheet_validator.log"
128-
return SSLogger(logfile_path).get_logger()
133+
return SSLogger(self.logfile_path, self.runfolder_name).get_logger(__name__)
134+
129135

130136
def ss_checks(self) -> None:
131137
"""
132138
Run checks at samplesheet and sample level. Performs required extra checks for
133139
checks not included in seglh-naming
134140
"""
135-
self.logger = self.get_logger()
136141
if self.check_ss_present():
137142
setattr(self, "ss_obj", self.check_ss_name())
138143
if self.ss_obj:
@@ -274,41 +279,45 @@ def get_data_section(self) -> None:
274279
:return None:
275280
"""
276281
with open(self.samplesheet_path, "r") as samplesheet_stream:
277-
for line in reversed(samplesheet_stream.readlines()):
282+
samplesheet_contents = samplesheet_stream.readlines()
283+
for line in reversed(samplesheet_contents):
284+
line_index = samplesheet_contents.index(line)
278285
# If line contains table headers, stop looping through the file
279286
if any(header in line for header in self.expected_data_headers):
280-
self.extract_headers(line)
287+
self.extract_headers(line, line_index)
281288
break
282289
elif len(line.split(",")[0]) < 2:
283-
self.logger.info(self.logger.log_msgs["found_empty_line"])
290+
self.logger.info(self.logger.log_msgs["found_empty_line"], line_index)
284291
pass # Skip empty lines
285292
else: # Contains sample
286-
self.extract_sample_name_id(line)
293+
self.extract_sample_name_id(line, line_index)
287294

288-
def extract_headers(self, line: str) -> None:
295+
def extract_headers(self, line: str, line_index: int) -> None:
289296
"""
290297
Extract headers from line
291-
:param line (str): Line containing samplesheet headers
298+
:param line (str): Line containing samplesheet headers
299+
:param line_index (int): Index of line
292300
"""
293301
try:
294-
self.logger.info(self.logger.log_msgs["found_header_line"])
302+
self.logger.info(self.logger.log_msgs["found_header_line"], line_index)
295303
self.data_headers = line.split(",")
296304
except Exception as exception:
297305
self.errors = True
298306
self.logger.warning(
299-
self.logger.log_msgs["error_extracting_headers"], exception
307+
self.logger.log_msgs["error_extracting_headers"], line_index, exception
300308
)
301309
self.add_msg_to_error_dict(
302310
"Error extracting headers",
303-
self.logger.log_msgs["error_extracting_headers"] % exception,
311+
self.logger.log_msgs["error_extracting_headers"], line_index, exception,
304312
)
305313

306-
def extract_sample_name_id(self, line: str) -> None:
314+
def extract_sample_name_id(self, line: str, line_index: int) -> None:
307315
"""
308316
Extract sample name and sample id from samplesheet line
309317
:param line (str): Line containing sample details
318+
:param line_index (int): Index of line
310319
"""
311-
self.logger.info(self.logger.log_msgs["found_sample_line"])
320+
self.logger.info(self.logger.log_msgs["found_sample_line"], line_index)
312321
for column_details in [("Sample_ID", 0), ("Sample_Name", 1)]:
313322
col_name, index = column_details
314323
try:
@@ -318,13 +327,13 @@ def extract_sample_name_id(self, line: str) -> None:
318327
self.logger.warning(
319328
self.logger.log_msgs["col_extraction_error"],
320329
col_name,
330+
line_index,
321331
line,
322332
exception,
323333
)
324334
self.add_msg_to_error_dict(
325335
"Error extracting sample name and ID",
326-
self.logger.log_msgs["col_extraction_error"]
327-
% (col_name, line, exception),
336+
self.logger.log_msgs["col_extraction_error"], col_name, line_index, line, exception
328337
)
329338

330339
def check_expected_headers(self) -> None:

samplesheet_validator/ss_logger.py

+24-26
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,36 @@
88
import logging.handlers
99

1010

11-
def set_root_logger():
11+
def set_root_logger(no_stream_handler: bool):
1212
"""
13-
Set up root logger and add stream handler - we only want to add stream handler once
14-
else it will duplicate log messages to the terminal
13+
Set up root logger and add stream handler and syslog handler - we only want to add these once
14+
else it will duplicate log messages to the terminal. All loggers named with the same stem
15+
as the root logger will use these same syslog handler and stream handler
16+
:param no_stream_handler (bool): True if no stream handler specified as command line input
1517
"""
18+
formatter = logging.Formatter(config.LOGGING_FORMATTER)
1619
logger = logging.getLogger()
1720
logger.setLevel(logging.DEBUG)
18-
stream_handler = logging.StreamHandler(sys.stdout)
19-
stream_handler.setLevel(logging.DEBUG)
20-
stream_handler.setFormatter(logging.Formatter(config.LOGGING_FORMATTER))
21-
stream_handler.name = "stream_handler"
22-
logger.addHandler(stream_handler)
21+
syslog_handler = logging.handlers.SysLogHandler(address="/dev/log")
22+
syslog_handler.setFormatter(formatter)
23+
syslog_handler.name = "syslog_handler"
24+
logger.addHandler(syslog_handler)
25+
if not no_stream_handler:
26+
stream_handler = logging.StreamHandler(sys.stdout)
27+
stream_handler.setFormatter(formatter)
28+
stream_handler.name = "stream_handler"
29+
logger.addHandler(stream_handler)
30+
return logger
31+
2332

2433

2534
class SSLogger:
2635
"""
2736
Creates a python logging object with a file handler and syslog handler
2837
2938
Attributes
30-
timestamp (str): Timestamp from config
3139
logfile_path (str): Name of filepath to provide to _file_handler()
40+
runfolder_name (str): Runfolder name
3241
logging_formatter (logging.Formatter): Specifies the layout of log records in the final output
3342
3443
Methods
@@ -40,27 +49,27 @@ class SSLogger:
4049
Get syslog handler for the logger
4150
"""
4251

43-
def __init__(self, logfile_path: str):
52+
def __init__(self, logfile_path: str, runfolder_name: str):
4453
"""
4554
Constructor for the Logger class
4655
:param logfile_path (str): Path to logfile location
56+
:param runfolder_name (str): Runfolder name
4757
"""
4858
# Timestamp used for naming log files with datetime, format %Y%m%d_%H%M%S
49-
self.timestamp = config.TIMESTAMP
5059
self.logfile_path = logfile_path
60+
self.runfolder_name = runfolder_name
5161
self.logging_formatter = logging.Formatter(config.LOGGING_FORMATTER)
5262

53-
def get_logger(self) -> logging.Logger:
63+
def get_logger(self, logger_name: str) -> logging.Logger:
5464
"""
5565
Returns a Python logging object, and give it a name
66+
:param logger_name (str): Logger name string
5667
:return logger (object): Python logging object with custom attributes
5768
"""
58-
logger = logging.getLogger()
69+
logger = logging.getLogger(f"{logger_name}.{self.runfolder_name}")
5970
logger.filepath = self.logfile_path
6071
logger.setLevel(logging.DEBUG)
6172
logger.addHandler(self._get_file_handler())
62-
logger.addHandler(self._get_syslog_handler())
63-
logger.timestamp = self.timestamp
6473
logger.log_msgs = config.LOG_MSGS
6574
return logger
6675

@@ -74,14 +83,3 @@ def _get_file_handler(self) -> logging.FileHandler:
7483
file_handler.setFormatter(self.logging_formatter)
7584
file_handler.name = "file_handler"
7685
return file_handler
77-
78-
def _get_syslog_handler(self) -> logging.handlers.SysLogHandler:
79-
"""
80-
Get syslog handler for the logger, and give it a name
81-
:return syslog_handler (logging.SysLogHandler): SysLogHandler
82-
"""
83-
syslog_handler = logging.handlers.SysLogHandler(address="/dev/log")
84-
syslog_handler.setLevel(logging.DEBUG)
85-
syslog_handler.setFormatter(self.logging_formatter)
86-
syslog_handler.name = "syslog_handler"
87-
return syslog_handler

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
setup(
55
name="samplesheet_validator",
6-
version=git_tag(),
6+
version="TEST",
77
description="Python library for samplesheet validation",
88
url="https://github.com/moka-guys/samplesheet_validator",
99
author="Rachel Duffin",

0 commit comments

Comments
 (0)