mirror of
				https://git.dn42.dev/dn42/registry.git
				synced 2025-10-31 03:30:49 +08:00 
			
		
		
		
	add schema checker
This commit is contained in:
		
							parent
							
								
									50227f89ac
								
							
						
					
					
						commit
						a8c8f6124b
					
				
					 3 changed files with 583 additions and 0 deletions
				
			
		
							
								
								
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | _MTN | ||||||
|  | lib/ | ||||||
|  | whoisd/ | ||||||
|  | utils/ | ||||||
|  | !utils/schema-check/*.py | ||||||
							
								
								
									
										401
									
								
								utils/schema-check/dn42-schema.py
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										401
									
								
								utils/schema-check/dn42-schema.py
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,401 @@ | ||||||
|  | #!/usr/bin/env python3 | ||||||
|  | 
 | ||||||
|  | from __future__ import print_function | ||||||
|  | 
 | ||||||
|  | import re | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import argparse | ||||||
|  | import log | ||||||
|  | import glob | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | SCHEMA_NAMESPACE = "dn42." | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SchemaDOM: | ||||||
|  |     src = None | ||||||
|  |     schema = None | ||||||
|  |     name = None | ||||||
|  |     ref = None | ||||||
|  | 
 | ||||||
|  |     def __init__(self, fn): | ||||||
|  |         self.src = fn | ||||||
|  |         f = FileDOM(fn) | ||||||
|  |         self.schema = self.__parse_schema(f) | ||||||
|  | 
 | ||||||
|  |     def __parse_schema(self, f): | ||||||
|  |         schema = {} | ||||||
|  |         for k, v, l in f.dom: | ||||||
|  |             if k == 'ref': | ||||||
|  |                 self.ref = v | ||||||
|  |             elif k == 'schema': | ||||||
|  |                 self.name = v | ||||||
|  | 
 | ||||||
|  |             if k != 'key': | ||||||
|  |                 continue | ||||||
|  | 
 | ||||||
|  |             v = v.split() | ||||||
|  |             key = v.pop(0) | ||||||
|  |             schema[key] = set() | ||||||
|  |             for i in v: | ||||||
|  |                 if i == ">": | ||||||
|  |                     break | ||||||
|  | 
 | ||||||
|  |                 schema[key].add(i) | ||||||
|  | 
 | ||||||
|  |             for k, v in schema.items(): | ||||||
|  |                 if 'primary' in v: | ||||||
|  |                     schema[k].add("oneline") | ||||||
|  |                     if "multiline" in v: | ||||||
|  |                         schema[k].remove("multiline") | ||||||
|  |                     schema[k].add("single") | ||||||
|  |                     if "multiple" in v: | ||||||
|  |                         schema[k].remove("multiple") | ||||||
|  |                     schema[k].add("required") | ||||||
|  |                     if "optional" in v: | ||||||
|  |                         schema[k].remove("optional") | ||||||
|  |                     if "recommend" in v: | ||||||
|  |                         schema[k].remove("recommend") | ||||||
|  |                     if "deprecate" in v: | ||||||
|  |                         schema[k].remove("deprecate") | ||||||
|  | 
 | ||||||
|  |                 if 'oneline' not in v: | ||||||
|  |                     schema[k].add("multiline") | ||||||
|  |                 if 'single' not in v: | ||||||
|  |                     schema[k].add("multiple") | ||||||
|  | 
 | ||||||
|  |         return schema | ||||||
|  | 
 | ||||||
|  |     def check_file(self, f, lookups=None): | ||||||
|  |         status = "PASS" | ||||||
|  | 
 | ||||||
|  |         for k, v in self.schema.items(): | ||||||
|  |             if 'required' in v and k not in f.keys: | ||||||
|  |                 log.error( | ||||||
|  |                     "%s Line 0: Key [%s] not found and is required." % (f.src, k)) | ||||||
|  |                 status = "FAIL" | ||||||
|  |             elif 'recommend' in v and k not in f.keys: | ||||||
|  |                 log.notice( | ||||||
|  |                     "%s Line 0: Key [%s] not found and is recommended." % (f.src, k)) | ||||||
|  |                 status = "NOTE" | ||||||
|  | 
 | ||||||
|  |             if 'schema' in v and SCHEMA_NAMESPACE + f.dom[0][0] != self.ref: | ||||||
|  |                 log.error( | ||||||
|  |                     "%s Line 1: Key [%s] not found and is required as the first line." % (f.src, k)) | ||||||
|  |                 status = "FAIL" | ||||||
|  | 
 | ||||||
|  |             if 'single' in v and k in f.keys and len(f.keys[k]) > 1: | ||||||
|  |                 log.warning("%s Line %d: Key [%s] first defined here and has repeated keys." % ( | ||||||
|  |                     f.src, f.keys[k][0], k)) | ||||||
|  |                 for l in f.keys[k][1:]: | ||||||
|  |                     log.error( | ||||||
|  |                         "%s Line %d: Key [%s] can only appear once." % (f.src, l, k)) | ||||||
|  |                     status = "FAIL" | ||||||
|  | 
 | ||||||
|  |             if 'oneline' in v and k in f.multi: | ||||||
|  |                 for l in f.keys[k]: | ||||||
|  |                     log.error( | ||||||
|  |                         "%s Line %d: Key [%s] can not have multiple lines." % (f.src, l, k)) | ||||||
|  |                     status = "FAIL" | ||||||
|  | 
 | ||||||
|  |         for k, v, l in f.dom: | ||||||
|  |             if k not in self.schema: | ||||||
|  |                 log.error("%s Line %d: Key [%s] not in schema." % (f.src, l, k)) | ||||||
|  |                 status = "FAIL" | ||||||
|  |                 continue | ||||||
|  |             else: | ||||||
|  |                 if 'deprecate' in self.schema[k]: | ||||||
|  |                     log.info( | ||||||
|  |                         "%s Line %d: Key [%s] was found and is deprecated." % (f.src, l, k)) | ||||||
|  |                     status = "INFO" | ||||||
|  | 
 | ||||||
|  |                 if lookups is not None: | ||||||
|  |                     for o in self.schema[k]: | ||||||
|  |                         if o.startswith("lookup="): | ||||||
|  |                             refs = o.split("=", 2)[1].split(",") | ||||||
|  |                             val = v.split()[0] | ||||||
|  |                             found = False | ||||||
|  |                             for ref in refs: | ||||||
|  |                                 if (ref, val) in lookups: | ||||||
|  |                                     found = True | ||||||
|  |                             if not found: | ||||||
|  |                                 log.error("%s Line %d: Key %s references object %s in %s but does not exist." % ( | ||||||
|  |                                     f.src, l, k, val, refs)) | ||||||
|  |                                 status = "FAIL" | ||||||
|  | 
 | ||||||
|  |         print("CHECK\t%-54s\t%s\tMNTNERS: %s" %(f.src, status, ','.join(f.mntner))) | ||||||
|  |         return status | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class FileDOM: | ||||||
|  | 
 | ||||||
|  |     def __init__(self, fn): | ||||||
|  |         dom = [] | ||||||
|  |         keys = {} | ||||||
|  |         multi = {} | ||||||
|  |         mntner = [] | ||||||
|  |         last_multi = None | ||||||
|  |         schema = None | ||||||
|  |         src = fn | ||||||
|  | 
 | ||||||
|  |         with open(fn, "r") as f: | ||||||
|  |             for lineno, i in enumerate(f.readlines(), 1): | ||||||
|  | 
 | ||||||
|  |                 if re.match(r'[ \t]', i): | ||||||
|  |                     dom[-1][1] += "\n" + i.strip() | ||||||
|  | 
 | ||||||
|  |                     if dom[-1][0] not in multi: | ||||||
|  |                         multi[dom[-1][0]] = [] | ||||||
|  | 
 | ||||||
|  |                     if last_multi is None: | ||||||
|  |                         multi[dom[-1][0]].append(lineno) | ||||||
|  |                         last_multi = dom[-1][0] | ||||||
|  | 
 | ||||||
|  |                 else: | ||||||
|  |                     i = i.split(":") | ||||||
|  |                     if len(i) < 2: | ||||||
|  |                         continue | ||||||
|  | 
 | ||||||
|  |                     dom.append([i[0].strip(), ':'.join(i[1:]).strip(), lineno]) | ||||||
|  | 
 | ||||||
|  |                     if i[0].strip() not in keys: | ||||||
|  |                         keys[i[0].strip()] = [] | ||||||
|  | 
 | ||||||
|  |                     keys[i[0].strip()].append(lineno) | ||||||
|  | 
 | ||||||
|  |                     last_multi = None | ||||||
|  | 
 | ||||||
|  |                 if dom[-1][0] == 'use-schema': | ||||||
|  |                     schema = dom[-1][1] | ||||||
|  | 
 | ||||||
|  |                 if dom[-1][0] == 'mnt-by': | ||||||
|  |                     mntner.append(dom[-1][1]) | ||||||
|  | 
 | ||||||
|  |         self.dom = dom | ||||||
|  |         self.keys = keys | ||||||
|  |         self.multi = multi | ||||||
|  |         self.mntner = mntner | ||||||
|  |         self.schema = schema | ||||||
|  |         self.src = src | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def main(infile, schema): | ||||||
|  | 
 | ||||||
|  |     log.debug("Check File: %s" % (infile)) | ||||||
|  |     f = FileDOM(infile) | ||||||
|  | 
 | ||||||
|  |     if schema is not None: | ||||||
|  |         f.schema = schema | ||||||
|  |     else: | ||||||
|  |         f.schema = "schema/" + f.schema | ||||||
|  | 
 | ||||||
|  |     if f.schema is None: | ||||||
|  |         log.error("Schema is not defined for file") | ||||||
|  |         return False | ||||||
|  | 
 | ||||||
|  |     log.debug("Use Schema: %s" % (f.schema)) | ||||||
|  | 
 | ||||||
|  |     s = SchemaDOM(f.schema) | ||||||
|  |     return s.check_file(f) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def check_schemas(path): | ||||||
|  |     schemas = {} | ||||||
|  |     for fn in glob.glob(path+"/*"): | ||||||
|  |         s = SchemaDOM(fn) | ||||||
|  |         log.info("read schema: %s" % (s.name)) | ||||||
|  |         schemas[s.ref] = s | ||||||
|  | 
 | ||||||
|  |     ok = True | ||||||
|  |     c = schemas[SCHEMA_NAMESPACE + "schema"] | ||||||
|  |     for s in schemas.keys(): | ||||||
|  |         ck = c.check_file(s) | ||||||
|  |         if not ck: | ||||||
|  |             ok = False | ||||||
|  | 
 | ||||||
|  |     return ok | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def scan_index(infile, mntner=None): | ||||||
|  |     idx = {} | ||||||
|  |     schemas = {} | ||||||
|  | 
 | ||||||
|  |     with open(infile, 'r') as f: | ||||||
|  |         for line in f.readlines(): | ||||||
|  |             line = line.split() | ||||||
|  |             idx[(line[0], line[1])] = line[2:] | ||||||
|  |             if line[0] == SCHEMA_NAMEPACE + 'schema': | ||||||
|  |                 s = SchemaDOM(line[2]) | ||||||
|  |                 log.info("read schema: %s" % (s.name)) | ||||||
|  |                 schemas[s.ref] = s | ||||||
|  | 
 | ||||||
|  |     return __scan_index(idx, schemas, mntner) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def scan_files(path, mntner=None): | ||||||
|  |     arr = __index_files(path) | ||||||
|  | 
 | ||||||
|  |     idx = {} | ||||||
|  |     schemas = {} | ||||||
|  | 
 | ||||||
|  |     for line in arr: | ||||||
|  |         idx[(line[0], line[1])] = line[2:] | ||||||
|  |         if line[0] == SCHEMA_NAMESPACE + 'schema': | ||||||
|  |             s = SchemaDOM(line[2]) | ||||||
|  |             log.info("read schema: %s" % (s.name)) | ||||||
|  |             schemas[s.ref] = s | ||||||
|  | 
 | ||||||
|  |     return __scan_index(idx, schemas, mntner) | ||||||
|  | 
 | ||||||
|  | def __scan_index(idx, schemas, mntner): | ||||||
|  |     ok = True | ||||||
|  |     for k, v in idx.items(): | ||||||
|  |         log.debug(k) | ||||||
|  |         mlist = [] | ||||||
|  |         if len(v) > 1: | ||||||
|  |             mlist = v[1].split(",") | ||||||
|  | 
 | ||||||
|  |         if mntner is not None and mntner not in mlist: | ||||||
|  |             continue | ||||||
|  | 
 | ||||||
|  |         s = schemas.get(k[0], None) | ||||||
|  |         if s is None: | ||||||
|  |             log.error("No schema found for %s" % (k[1])) | ||||||
|  |             ok = False | ||||||
|  |             continue | ||||||
|  |         c = FileDOM(v[0]) | ||||||
|  |         ck = s.check_file(c, idx.keys()) | ||||||
|  |         if not ck: | ||||||
|  |             ok = False | ||||||
|  | 
 | ||||||
|  |     return ok | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def __index_files(path): | ||||||
|  |     xlat = { | ||||||
|  |         "dns/":          SCHEMA_NAMESPACE + "domain", | ||||||
|  |         "inetnum/":      SCHEMA_NAMESPACE + "inetnum", | ||||||
|  |         "inet6num/":     SCHEMA_NAMESPACE + "inet6num", | ||||||
|  |         "route/":        SCHEMA_NAMESPACE + "route", | ||||||
|  |         "route6/":       SCHEMA_NAMESPACE + "route6", | ||||||
|  |         "aut-num/":      SCHEMA_NAMESPACE + "aut-num", | ||||||
|  |         "as-set/":       SCHEMA_NAMESPACE + "as-set", | ||||||
|  |         "as-block/":     SCHEMA_NAMESPACE + "as-block", | ||||||
|  |         "organisation/": SCHEMA_NAMESPACE + "organisation", | ||||||
|  |         "mntner/":       SCHEMA_NAMESPACE + "mntner", | ||||||
|  |         "person/":       SCHEMA_NAMESPACE + "person", | ||||||
|  |         "role/":         SCHEMA_NAMESPACE + "role", | ||||||
|  |         "tinc-keyset/":  SCHEMA_NAMESPACE + "tinc-keyset", | ||||||
|  |         "schema/":       SCHEMA_NAMESPACE + "schema", | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for root, dirs, files in os.walk(path): | ||||||
|  |         ignore = True | ||||||
|  |         for t in xlat.keys(): | ||||||
|  |             if root+"/" == os.path.join(path, t): | ||||||
|  |                ignore = False | ||||||
|  |                break | ||||||
|  |         if ignore: | ||||||
|  |            continue | ||||||
|  | 
 | ||||||
|  |         for f in files: | ||||||
|  | 
 | ||||||
|  |             dom = FileDOM(os.path.join(root, f)) | ||||||
|  | 
 | ||||||
|  |             for t, s in xlat.items(): | ||||||
|  |                 if dom.src.startswith(os.path.join(path, t)): | ||||||
|  |                     dom.schema = s | ||||||
|  | 
 | ||||||
|  |             yield (dom.schema, dom.src.split("/")[-1].replace("_", "/"), dom.src, ",".join(dom.mntner)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def index_files(path): | ||||||
|  |     idx = __index_files(path) | ||||||
|  |     for i in idx: | ||||||
|  |         print("%s\t%s\t%s\t%s" % i) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def get_args(): | ||||||
|  |     """Get and parse command line arguments""" | ||||||
|  | 
 | ||||||
|  |     parser = argparse.ArgumentParser( | ||||||
|  |         description='Check Schema. Checks Schema of file for validity') | ||||||
|  |     parser.add_argument('--merge-output', | ||||||
|  |                         help="Merge stderr into stdout (helps when reading output with pagers) [Default OFF]",     action="store_true") | ||||||
|  |     parser.add_argument('-v',  '--verbose', | ||||||
|  |                         help="Enable verbose output [Default OFF]",        action="store_true") | ||||||
|  |     parser.add_argument('-vv', '--doubleVerbose', | ||||||
|  |                         help="Enable full verbose output [Default OFF]",   action="store_true") | ||||||
|  | 
 | ||||||
|  |     subparsers = parser.add_subparsers(help='sub-command help', dest="command") | ||||||
|  | 
 | ||||||
|  |     parser_file = subparsers.add_parser( | ||||||
|  |         'check-file', help='Process a specific file') | ||||||
|  |     parser_file.add_argument('-s',  '--use-schema',    nargs='?', | ||||||
|  |                              help="Override schema to validate [Default None]", action="store") | ||||||
|  |     parser_file.add_argument( | ||||||
|  |         'infile',                 nargs="?", help="File to check", type=str) | ||||||
|  | 
 | ||||||
|  |     parser_schema = subparsers.add_parser( | ||||||
|  |         'check-schemas', help='Validate all schemas') | ||||||
|  |     parser_schema.add_argument( | ||||||
|  |         'path',          nargs="?", help="Path for schemas", type=str) | ||||||
|  | 
 | ||||||
|  |     parser_index = subparsers.add_parser('index', help='Generate index') | ||||||
|  |     parser_index.add_argument( | ||||||
|  |         'path',  nargs="?", help="Path for dn42 data", type=str) | ||||||
|  | 
 | ||||||
|  |     parser_scanindex = subparsers.add_parser( | ||||||
|  |         'scan-index', help='Validate files in index') | ||||||
|  |     parser_scanindex.add_argument( | ||||||
|  |         'infile',  nargs="?", help="Index file to scan", type=str) | ||||||
|  |     parser_scanindex.add_argument('-m',  '--use-mntner', nargs='?', | ||||||
|  |                                   help="Only scan files that has MNT [Default None]", action="store") | ||||||
|  | 
 | ||||||
|  |     parser_scan = subparsers.add_parser('scan', help='Validate files in index') | ||||||
|  |     parser_scan.add_argument( | ||||||
|  |         'path',  nargs="?", help="Path for dn42 data", type=str) | ||||||
|  |     parser_scan.add_argument('-m',  '--use-mntner', nargs='?', | ||||||
|  |                              help="Only scan files that has a matching MNT [Default None]", action="store") | ||||||
|  | 
 | ||||||
|  |     return vars(parser.parse_args()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     args = get_args() | ||||||
|  | 
 | ||||||
|  |     if args["merge_output"]: | ||||||
|  |         log.OUTPUT = sys.stdout | ||||||
|  | 
 | ||||||
|  |     if args["doubleVerbose"]: | ||||||
|  |         log.default.level_console = log.VERB_DEBUG | ||||||
|  |         log.default.level_full = True | ||||||
|  | 
 | ||||||
|  |     if args["verbose"]: | ||||||
|  |         log.default.level_console = log.VERB_INFO | ||||||
|  | 
 | ||||||
|  |     log.debug(args) | ||||||
|  | 
 | ||||||
|  |     valid = True | ||||||
|  |     if args["command"] == "check-file": | ||||||
|  |         valid = main(args["infile"], args["use_schema"]) | ||||||
|  |         if valid: | ||||||
|  |             log.notice("Check %s: PASS" % (args["infile"])) | ||||||
|  |         else: | ||||||
|  |             log.fatal("Check %s: FAIL" % (args["infile"])) | ||||||
|  | 
 | ||||||
|  |     elif args["command"] == "check-schemas": | ||||||
|  |         valid = check_schemas(args["path"]) | ||||||
|  | 
 | ||||||
|  |     elif args["command"] == "index": | ||||||
|  |         index_files(args["path"]) | ||||||
|  | 
 | ||||||
|  |     elif args["command"] == "scan-index": | ||||||
|  |         scan_index(args["infile"], args["use_mntner"]) | ||||||
|  | 
 | ||||||
|  |     elif args["command"] == "scan": | ||||||
|  |         import time | ||||||
|  |         log.notice("## Scan Started at %s" %(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))) | ||||||
|  |         scan_files(args["path"], args["use_mntner"]) | ||||||
|  |         log.notice("## Scan Completed at %s" %(time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()))) | ||||||
							
								
								
									
										177
									
								
								utils/schema-check/log.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								utils/schema-check/log.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,177 @@ | ||||||
|  | from __future__ import print_function | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | import sys | ||||||
|  | import inspect | ||||||
|  | import datetime | ||||||
|  | 
 | ||||||
|  | OUTPUT = sys.stderr | ||||||
|  | 
 | ||||||
|  | LEVEL = ["CRIT", "ERR ", "WARN", "NOTE", "INFO", "DBUG", "...."] | ||||||
|  | CLEVEL = ["\x1B[41mCRIT\x1B[0m", | ||||||
|  |           "\x1B[31mERR \x1B[0m", | ||||||
|  |           "\x1B[33mWARN\x1B[0m", | ||||||
|  |           "\x1B[32mNOTE\x1B[0m", | ||||||
|  |           "\x1B[34mINFO\x1B[0m", | ||||||
|  |           "\x1B[90mDBUG\x1B[0m", | ||||||
|  |           "\x1B[90m....\x1B[0m"] | ||||||
|  | 
 | ||||||
|  | MSG = "{0}  {1}  {2}  {3}  {4}  {5}  ::  {6}" | ||||||
|  | CMSG = "[{1}]\x1B[90m {2}  {3}:{5} [{4}]\x1B[0m {6}\x1B[0m" | ||||||
|  | CMULTI = "[{1}]\x1B[90m {2}\x1B[0m" | ||||||
|  | 
 | ||||||
|  | VERB_CRITICAL = 0 | ||||||
|  | VERB_ERROR = 1 | ||||||
|  | VERB_WARN = 2 | ||||||
|  | VERB_NOTICE = 3 | ||||||
|  | VERB_INFO = 4 | ||||||
|  | VERB_DEBUG = 5 | ||||||
|  | VERB_NONE = -1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Log: | ||||||
|  |     log_dir = "" | ||||||
|  |     log_pfx = "main" | ||||||
|  | 
 | ||||||
|  |     level_console = VERB_ERROR | ||||||
|  |     level_file = VERB_NONE | ||||||
|  |     level_full = False | ||||||
|  | 
 | ||||||
|  |     count = [0, 0, 0, 0, 0, 0] | ||||||
|  | 
 | ||||||
|  |     def __init__(self): | ||||||
|  |         self.prog_name = sys.argv[0].rsplit("/", 1)[-1] | ||||||
|  |         self.prog_name = self.prog_name.split(".", 1)[0] | ||||||
|  |         self.log_pfx = self.prog_name | ||||||
|  | 
 | ||||||
|  |     def __del__(self): | ||||||
|  |         if self.level_console >= 5: | ||||||
|  |             os.write(1, b"[\x1B[90m\x1B[90mDBUG\x1B[90m] Log Counters crit:%d err:%d warn:%d note:%d info:%d dbug:%d\x1B[0m\n" % tuple(self.count)) | ||||||
|  | 
 | ||||||
|  |     def set_dir(self, name): | ||||||
|  |         if not os.path.isdir(name): | ||||||
|  |             os.makedirs(name) | ||||||
|  |         self.log_dir = name | ||||||
|  | 
 | ||||||
|  |     #  Write a message to console or log, conditionally. | ||||||
|  |     def output(self, level, message, frame=1): | ||||||
|  |         if level < 0 or level > 5: | ||||||
|  |             level = 5 | ||||||
|  | 
 | ||||||
|  |         self.count[level] += 1 | ||||||
|  | 
 | ||||||
|  |         # function_name = inspect.stack()[1][3] | ||||||
|  |         cur_date = datetime.datetime.now() | ||||||
|  | 
 | ||||||
|  |         (frame, file, ln, fn, lines, index) = inspect.getouterframes( | ||||||
|  |             inspect.currentframe())[frame] | ||||||
|  | 
 | ||||||
|  |         message = str(message).split("\n") | ||||||
|  |         cmsg = CMSG if self.level_full else CMULTI | ||||||
|  | 
 | ||||||
|  |         if self.level_console >= level: | ||||||
|  | 
 | ||||||
|  |             if len(message) == 1: | ||||||
|  |                 if self.level_full: | ||||||
|  |                     arg = str(cur_date), CLEVEL[ | ||||||
|  |                         level], self.prog_name, file, fn, ln, message[0] | ||||||
|  |                 else: | ||||||
|  |                     arg = str(cur_date), CLEVEL[level], message[0] | ||||||
|  | 
 | ||||||
|  |                 print(cmsg.format(*arg), file=OUTPUT) | ||||||
|  |             else: | ||||||
|  |                 if self.level_full: | ||||||
|  |                     arg = str(cur_date), CLEVEL[ | ||||||
|  |                         level], self.prog_name, file, fn, ln, "" | ||||||
|  |                     print(cmsg.format(*arg), file=OUTPUT) | ||||||
|  | 
 | ||||||
|  |                 for line in message: | ||||||
|  |                     print(CMULTI.format(str(cur_date), CLEVEL[ | ||||||
|  |                           VERB_NONE], line), file=OUTPUT) | ||||||
|  | 
 | ||||||
|  |         if self.level_file >= level: | ||||||
|  |             self.set_dir("./logs") | ||||||
|  |             log_file_name = os.path.join( | ||||||
|  |                 self.log_dir, self.log_pfx + str(cur_date.strftime('%Y-%m-%d')) + ".txt") | ||||||
|  | 
 | ||||||
|  |             with open(log_file_name, "a") as logger: | ||||||
|  |                 logger.write(MSG.format(str(cur_date), LEVEL[ | ||||||
|  |                              level], self.prog_name, file, fn, ln, message[0]) + "\n") | ||||||
|  |                 for line in message[1:]: | ||||||
|  |                     logger.write(MSG.format(str(cur_date), LEVEL[ | ||||||
|  |                                  VERB_NONE], self.prog_name, file, fn, ln, line) + "\n") | ||||||
|  | 
 | ||||||
|  |     def fatal(self, message): | ||||||
|  |         self.output(VERB_CRITICAL, message, 2) | ||||||
|  |         sys.exit(1) | ||||||
|  | 
 | ||||||
|  |     def critical(self, message): | ||||||
|  |         self.output(VERB_CRITICAL, message, 2) | ||||||
|  | 
 | ||||||
|  |     def error(self, message): | ||||||
|  |         self.output(VERB_ERROR, message, 2) | ||||||
|  | 
 | ||||||
|  |     def warning(self, message): | ||||||
|  |         self.output(VERB_WARN, message, 2) | ||||||
|  | 
 | ||||||
|  |     def notice(self, message): | ||||||
|  |         self.output(VERB_NOTICE, message, 2) | ||||||
|  | 
 | ||||||
|  |     def info(self, message): | ||||||
|  |         self.output(VERB_INFO, message, 2) | ||||||
|  | 
 | ||||||
|  |     def debug(self, message): | ||||||
|  |         self.output(VERB_DEBUG, message, 2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def fmt_exception(exc_type, exc_value, exc_traceback): | ||||||
|  |     import traceback | ||||||
|  | 
 | ||||||
|  |     lines = traceback.format_exception(exc_type, exc_value, exc_traceback) | ||||||
|  |     log_string = ''.join(line for line in lines) | ||||||
|  |     email_string = ''.join('<br />' + line for line in lines) | ||||||
|  | 
 | ||||||
|  |     return log_string, email_string | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | default = Log() | ||||||
|  | 
 | ||||||
|  | fatal = default.fatal | ||||||
|  | critical = default.critical | ||||||
|  | error = default.error | ||||||
|  | warning = default.warning | ||||||
|  | notice = default.notice | ||||||
|  | info = default.info | ||||||
|  | debug = default.debug | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class LogException: | ||||||
|  |     stop = None | ||||||
|  | 
 | ||||||
|  |     def __init__(self, stop=True): | ||||||
|  |         self.stop = stop | ||||||
|  | 
 | ||||||
|  |     def __enter__(self, stop=True): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     def __exit__(self, exc_type, value, traceback): | ||||||
|  | 
 | ||||||
|  |         if exc_type is None: | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
|  |         if exc_type is SystemExit and value.args == (0,): | ||||||
|  |             return True | ||||||
|  | 
 | ||||||
|  |         log_string, email_string = fmt_exception(exc_type, value, traceback) | ||||||
|  |         default.output(VERB_CRITICAL, 'Failure\n\n' + log_string, 2) | ||||||
|  | 
 | ||||||
|  |         if self.stop is False: | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|  |         from . import email | ||||||
|  |         email.send(default.prog_name + ' FAILURE', email_string) | ||||||
|  | 
 | ||||||
|  |         fatal("ABORTING EXECUTION") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | exception = LogException | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jon Lundy
						Jon Lundy