hat.syslog.server.main

Syslog Server main module

  1"""Syslog Server main module"""
  2
  3from pathlib import Path
  4import argparse
  5import asyncio
  6import contextlib
  7import logging.config
  8import sys
  9import typing
 10
 11import appdirs
 12
 13from hat import aio
 14from hat import json
 15
 16from hat.syslog.server import common
 17from hat.syslog.server.backend import create_backend
 18from hat.syslog.server.syslog import create_syslog_server
 19from hat.syslog.server.ui import create_web_server
 20
 21
 22mlog: logging.Logger = logging.getLogger('hat.syslog.server.main')
 23"""Module logger"""
 24
 25package_path: Path = Path(__file__).parent
 26"""Python package path"""
 27
 28user_data_dir: Path = Path(appdirs.user_data_dir('hat'))
 29"""User data directory path"""
 30
 31user_conf_dir: Path = Path(appdirs.user_config_dir('hat'))
 32"""User configuration directory path"""
 33
 34default_syslog_addr: str = 'tcp://0.0.0.0:6514'
 35"""Default syslog listening address"""
 36
 37default_ui_addr: str = 'http://0.0.0.0:23020'
 38"""Default UI listening address"""
 39
 40default_db_path: Path = user_data_dir / 'syslog.db'
 41"""Default DB file path"""
 42
 43default_db_low_size: int = int(1e6)
 44"""Default DB low size count"""
 45
 46default_db_high_size: int = int(1e7)
 47"""Default DB high size count"""
 48
 49
 50def create_argument_parser() -> argparse.ArgumentParser:
 51    """Create argument parser"""
 52    parser = argparse.ArgumentParser()
 53    parser.add_argument(
 54        '--conf', metavar='PATH', type=Path,
 55        help="configuration defined by hat-syslog://server.yaml# "
 56             "(default $XDG_CONFIG_HOME/hat/syslog.{yaml|yml|json})")
 57    parser.add_argument(
 58        '--log', metavar='LEVEL', type=str,
 59        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
 60        help="console log level")
 61    parser.add_argument(
 62        '--syslog-addr', metavar='ADDR', type=str,
 63        help=f"syslog listening address (default {default_syslog_addr})")
 64    parser.add_argument(
 65        '--syslog-pem', metavar='PATH', type=Path,
 66        help="pem file path - mandatory for ssl communication")
 67    parser.add_argument(
 68        '--ui-addr', metavar='ADDR', type=str,
 69        help=f"UI listening address (default {default_ui_addr})")
 70    parser.add_argument(
 71        '--db-path', metavar='PATH', type=Path,
 72        help="sqlite database file path "
 73             "(default $XDG_DATA_HOME/hat/syslog.db)")
 74    parser.add_argument(
 75        '--db-low-size', metavar='N', type=int,
 76        help=f"number of messages kept in database after database "
 77             f"cleanup (default {default_db_low_size})")
 78    parser.add_argument(
 79        '--db-high-size', metavar='N', type=int,
 80        help=f"number of messages that will trigger database cleanup "
 81             f"(default {default_db_high_size})")
 82    parser.add_argument(
 83        '--db-enable-archive', action='store_true',
 84        help="should messages, deleted during database cleanup, be kept "
 85             "in archive files")
 86    parser.add_argument(
 87        '--db-disable-journal', action='store_true',
 88        help="disable sqlite jurnaling")
 89    return parser
 90
 91
 92def main():
 93    """Syslog Server"""
 94    parser = create_argument_parser()
 95    args = parser.parse_args()
 96
 97    conf_path = args.conf
 98    if not conf_path:
 99        for suffix in ('.yaml', '.yml', '.json'):
100            conf_path = (user_conf_dir / 'syslog').with_suffix(suffix)
101            if conf_path.exists():
102                break
103        else:
104            conf_path = None
105
106    if conf_path == Path('-'):
107        conf = json.decode_stream(sys.stdin)
108    elif conf_path:
109        conf = json.decode_file(conf_path)
110    else:
111        conf = None
112
113    if conf:
114        common.json_schema_repo.validate('hat-syslog://server.yaml#', conf)
115
116    if args.log:
117        log_conf = _get_console_log_conf(args.log)
118    elif conf:
119        log_conf = conf['log']
120    else:
121        log_conf = {'version': 1}
122
123    logging.config.dictConfig(log_conf)
124
125    syslog_addr = (args.syslog_addr if args.syslog_addr else
126                   conf['syslog_addr'] if conf else
127                   default_syslog_addr)
128
129    syslog_pem = (args.syslog_pem if args.syslog_pem else
130                  Path(conf['syslog_pem']) if conf and 'syslog_pem' in conf else  # NOQA
131                  None)
132
133    ui_addr = (args.ui_addr if args.ui_addr else
134               conf['ui_addr'] if conf else
135               default_ui_addr)
136
137    db_path = (args.db_path if args.db_path else
138               Path(conf['db_path']) if conf else
139               default_db_path)
140
141    db_low_size = (args.db_low_size if args.db_low_size is not None else
142                   conf['db_low_size'] if conf else
143                   default_db_low_size)
144
145    db_high_size = (args.db_high_size if args.db_high_size is not None else
146                    conf['db_high_size'] if conf else
147                    default_db_high_size)
148
149    db_enable_archive = (True if args.db_enable_archive else
150                         conf['db_enable_archive'] if conf else
151                         False)
152
153    db_disable_journal = (True if args.db_disable_journal else
154                          conf['db_disable_journal'] if conf else
155                          False)
156
157    aio.init_asyncio()
158    with contextlib.suppress(asyncio.CancelledError):
159        aio.run_asyncio(async_main(syslog_addr=syslog_addr,
160                                   syslog_pem=syslog_pem,
161                                   ui_addr=ui_addr,
162                                   db_path=db_path,
163                                   db_low_size=db_low_size,
164                                   db_high_size=db_high_size,
165                                   db_enable_archive=db_enable_archive,
166                                   db_disable_journal=db_disable_journal))
167
168
169async def async_main(syslog_addr: str,
170                     syslog_pem: typing.Optional[Path],
171                     ui_addr: str,
172                     db_path: Path,
173                     db_low_size: int,
174                     db_high_size: int,
175                     db_enable_archive: bool,
176                     db_disable_journal: bool):
177    """Syslog Server async main"""
178    async_group = aio.Group()
179
180    async def async_close():
181        await async_group.async_close()
182        await asyncio.sleep(0.1)
183
184    try:
185        mlog.debug("creating backend...")
186        backend = await _create_resource(async_group, create_backend,
187                                         db_path, db_low_size, db_high_size,
188                                         db_enable_archive, db_disable_journal)
189
190        mlog.debug("creating web server...")
191        await _create_resource(async_group, create_web_server, ui_addr,
192                               backend)
193
194        mlog.debug("creating syslog server...")
195        await _create_resource(async_group, create_syslog_server, syslog_addr,
196                               syslog_pem, backend)
197
198        mlog.debug("initialization done")
199        await async_group.wait_closing()
200
201    finally:
202        mlog.debug("closing...")
203        await aio.uncancellable(async_close())
204
205
206def _get_console_log_conf(level):
207    return {
208        'version': 1,
209        'formatters': {
210            'syslog_server_console': {
211                'format': '[%(asctime)s %(levelname)s %(name)s] %(message)s'}},
212        'handlers': {
213            'syslog_server_console': {
214                'class': 'logging.StreamHandler',
215                'formatter': 'syslog_server_console',
216                'level': level}},
217        'loggers': {
218            'hat.syslog': {
219                'level': level}},
220        'root': {
221            'level': 'INFO' if level == 'DEBUG' else level,
222            'handlers': ['syslog_server_console']},
223        'disable_existing_loggers': False}
224
225
226async def _create_resource(async_group, fn, *args):
227    resource = await async_group.spawn(fn, *args)
228    async_group.spawn(aio.call_on_cancel, resource.async_close)
229    async_group.spawn(aio.call_on_done, resource.wait_closing(),
230                      async_group.close)
231    return resource
232
233
234if __name__ == '__main__':
235    sys.argv[0] = 'hat-syslog'
236    sys.exit(main())
mlog: logging.Logger = <Logger hat.syslog.server.main (WARNING)>

Module logger

package_path: pathlib.Path = PosixPath('/home/bozo/repos/hat/hat-syslog/src_py/hat/syslog/server')

Python package path

user_data_dir: pathlib.Path = PosixPath('/home/bozo/.local/share/hat')

User data directory path

user_conf_dir: pathlib.Path = PosixPath('/home/bozo/.config/hat')

User configuration directory path

default_syslog_addr: str = 'tcp://0.0.0.0:6514'

Default syslog listening address

default_ui_addr: str = 'http://0.0.0.0:23020'

Default UI listening address

default_db_path: pathlib.Path = PosixPath('/home/bozo/.local/share/hat/syslog.db')

Default DB file path

default_db_low_size: int = 1000000

Default DB low size count

default_db_high_size: int = 10000000

Default DB high size count

def create_argument_parser() -> argparse.ArgumentParser:
51def create_argument_parser() -> argparse.ArgumentParser:
52    """Create argument parser"""
53    parser = argparse.ArgumentParser()
54    parser.add_argument(
55        '--conf', metavar='PATH', type=Path,
56        help="configuration defined by hat-syslog://server.yaml# "
57             "(default $XDG_CONFIG_HOME/hat/syslog.{yaml|yml|json})")
58    parser.add_argument(
59        '--log', metavar='LEVEL', type=str,
60        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
61        help="console log level")
62    parser.add_argument(
63        '--syslog-addr', metavar='ADDR', type=str,
64        help=f"syslog listening address (default {default_syslog_addr})")
65    parser.add_argument(
66        '--syslog-pem', metavar='PATH', type=Path,
67        help="pem file path - mandatory for ssl communication")
68    parser.add_argument(
69        '--ui-addr', metavar='ADDR', type=str,
70        help=f"UI listening address (default {default_ui_addr})")
71    parser.add_argument(
72        '--db-path', metavar='PATH', type=Path,
73        help="sqlite database file path "
74             "(default $XDG_DATA_HOME/hat/syslog.db)")
75    parser.add_argument(
76        '--db-low-size', metavar='N', type=int,
77        help=f"number of messages kept in database after database "
78             f"cleanup (default {default_db_low_size})")
79    parser.add_argument(
80        '--db-high-size', metavar='N', type=int,
81        help=f"number of messages that will trigger database cleanup "
82             f"(default {default_db_high_size})")
83    parser.add_argument(
84        '--db-enable-archive', action='store_true',
85        help="should messages, deleted during database cleanup, be kept "
86             "in archive files")
87    parser.add_argument(
88        '--db-disable-journal', action='store_true',
89        help="disable sqlite jurnaling")
90    return parser

Create argument parser

def main():
 93def main():
 94    """Syslog Server"""
 95    parser = create_argument_parser()
 96    args = parser.parse_args()
 97
 98    conf_path = args.conf
 99    if not conf_path:
100        for suffix in ('.yaml', '.yml', '.json'):
101            conf_path = (user_conf_dir / 'syslog').with_suffix(suffix)
102            if conf_path.exists():
103                break
104        else:
105            conf_path = None
106
107    if conf_path == Path('-'):
108        conf = json.decode_stream(sys.stdin)
109    elif conf_path:
110        conf = json.decode_file(conf_path)
111    else:
112        conf = None
113
114    if conf:
115        common.json_schema_repo.validate('hat-syslog://server.yaml#', conf)
116
117    if args.log:
118        log_conf = _get_console_log_conf(args.log)
119    elif conf:
120        log_conf = conf['log']
121    else:
122        log_conf = {'version': 1}
123
124    logging.config.dictConfig(log_conf)
125
126    syslog_addr = (args.syslog_addr if args.syslog_addr else
127                   conf['syslog_addr'] if conf else
128                   default_syslog_addr)
129
130    syslog_pem = (args.syslog_pem if args.syslog_pem else
131                  Path(conf['syslog_pem']) if conf and 'syslog_pem' in conf else  # NOQA
132                  None)
133
134    ui_addr = (args.ui_addr if args.ui_addr else
135               conf['ui_addr'] if conf else
136               default_ui_addr)
137
138    db_path = (args.db_path if args.db_path else
139               Path(conf['db_path']) if conf else
140               default_db_path)
141
142    db_low_size = (args.db_low_size if args.db_low_size is not None else
143                   conf['db_low_size'] if conf else
144                   default_db_low_size)
145
146    db_high_size = (args.db_high_size if args.db_high_size is not None else
147                    conf['db_high_size'] if conf else
148                    default_db_high_size)
149
150    db_enable_archive = (True if args.db_enable_archive else
151                         conf['db_enable_archive'] if conf else
152                         False)
153
154    db_disable_journal = (True if args.db_disable_journal else
155                          conf['db_disable_journal'] if conf else
156                          False)
157
158    aio.init_asyncio()
159    with contextlib.suppress(asyncio.CancelledError):
160        aio.run_asyncio(async_main(syslog_addr=syslog_addr,
161                                   syslog_pem=syslog_pem,
162                                   ui_addr=ui_addr,
163                                   db_path=db_path,
164                                   db_low_size=db_low_size,
165                                   db_high_size=db_high_size,
166                                   db_enable_archive=db_enable_archive,
167                                   db_disable_journal=db_disable_journal))

Syslog Server

async def async_main( syslog_addr: str, syslog_pem: Optional[pathlib.Path], ui_addr: str, db_path: pathlib.Path, db_low_size: int, db_high_size: int, db_enable_archive: bool, db_disable_journal: bool):
170async def async_main(syslog_addr: str,
171                     syslog_pem: typing.Optional[Path],
172                     ui_addr: str,
173                     db_path: Path,
174                     db_low_size: int,
175                     db_high_size: int,
176                     db_enable_archive: bool,
177                     db_disable_journal: bool):
178    """Syslog Server async main"""
179    async_group = aio.Group()
180
181    async def async_close():
182        await async_group.async_close()
183        await asyncio.sleep(0.1)
184
185    try:
186        mlog.debug("creating backend...")
187        backend = await _create_resource(async_group, create_backend,
188                                         db_path, db_low_size, db_high_size,
189                                         db_enable_archive, db_disable_journal)
190
191        mlog.debug("creating web server...")
192        await _create_resource(async_group, create_web_server, ui_addr,
193                               backend)
194
195        mlog.debug("creating syslog server...")
196        await _create_resource(async_group, create_syslog_server, syslog_addr,
197                               syslog_pem, backend)
198
199        mlog.debug("initialization done")
200        await async_group.wait_closing()
201
202    finally:
203        mlog.debug("closing...")
204        await aio.uncancellable(async_close())

Syslog Server async main