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 time
 10
 11import appdirs
 12
 13from hat import aio
 14
 15from hat.syslog.server.backend import create_backend
 16from hat.syslog.server.syslog import create_syslog_server
 17from hat.syslog.server.ui import create_web_server
 18
 19
 20mlog: logging.Logger = logging.getLogger('hat.syslog.server.main')
 21"""Module logger"""
 22
 23user_data_dir: Path = Path(appdirs.user_data_dir('hat'))
 24"""User data directory path"""
 25
 26default_log_level: str = 'INFO'
 27"""Default console log level"""
 28
 29default_ui_addr: str = 'http://0.0.0.0:23020'
 30"""Default UI listening address"""
 31
 32default_db_path: Path = user_data_dir / 'syslog.db'
 33"""Default DB file path"""
 34
 35default_db_low_size: int = int(1e6)
 36"""Default DB low size count"""
 37
 38default_db_high_size: int = int(1e7)
 39"""Default DB high size count"""
 40
 41default_syslog_addrs: list[str] = ['tcp://0.0.0.0:6514',
 42                                   'udp://0.0.0.0:6514']
 43"""Default syslog listening addresses"""
 44
 45
 46def create_argument_parser() -> argparse.ArgumentParser:
 47    """Create argument parser"""
 48    parser = argparse.ArgumentParser(
 49        description="Syslog Server listening for TCP and/or UDP messages. "
 50                    "If listening addresses are not provided, Syslog Server "
 51                    "listens on 'tcp://0.0.0.0:6514' and "
 52                    "'udp://0.0.0.0:6514'.")
 53    parser.add_argument(
 54        '--log-level', metavar='LEVEL', default=default_log_level,
 55        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
 56        help=f"console log level (default {default_log_level})")
 57    parser.add_argument(
 58        '--ui-addr', metavar='ADDR', default=default_ui_addr,
 59        help=f"UI listening address (default {default_ui_addr})")
 60    parser.add_argument(
 61        '--db-path', metavar='PATH', type=Path, default=default_db_path,
 62        help="sqlite database file path "
 63             "(default $XDG_DATA_HOME/hat/syslog.db)")
 64    parser.add_argument(
 65        '--db-low-size', metavar='N', type=int, default=default_db_low_size,
 66        help=f"number of messages kept in database after database "
 67             f"cleanup (default {default_db_low_size})")
 68    parser.add_argument(
 69        '--db-high-size', metavar='N', type=int, default=default_db_high_size,
 70        help=f"number of messages that will trigger database cleanup "
 71             f"(default {default_db_high_size})")
 72    parser.add_argument(
 73        '--db-enable-archive', action='store_true',
 74        help="should messages, deleted during database cleanup, be kept "
 75             "in archive files")
 76    parser.add_argument(
 77        '--db-disable-journal', action='store_true',
 78        help="disable sqlite journaling")
 79    parser.add_argument(
 80        '--syslog-pem-path', metavar='PATH', type=Path, default=None,
 81        help="certificate PEM path used in case of tls syslog")
 82    parser.add_argument(
 83        'syslog_addrs', metavar='ADDR', nargs='*',
 84        default=default_syslog_addrs,
 85        help="syslog listening address formated as <prot>://<host>:<port> "
 86             "(<prot> is 'tcp', 'udp' or 'tls'; <host> is host name or IP "
 87             "address; <port> is UDP/TCP port)")
 88    return parser
 89
 90
 91def main():
 92    """Syslog Server"""
 93    parser = create_argument_parser()
 94    args = parser.parse_args()
 95
 96    logging.config.dictConfig({
 97        'version': 1,
 98        'formatters': {
 99            'console_formater': {
100                'format': '[%(asctime)s %(levelname)s %(name)s] %(message)s'}},
101        'handlers': {
102            'console_handler': {
103                'class': 'logging.StreamHandler',
104                'formatter': 'console_formater',
105                'level': args.log_level}},
106        'root': {
107            'level': args.log_level,
108            'handlers': ['console_handler']},
109        'disable_existing_loggers': False})
110
111    aio.init_asyncio()
112    with contextlib.suppress(asyncio.CancelledError):
113        aio.run_asyncio(async_main(ui_addr=args.ui_addr,
114                                   db_path=args.db_path,
115                                   db_low_size=args.db_low_size,
116                                   db_high_size=args.db_high_size,
117                                   db_enable_archive=args.db_enable_archive,
118                                   db_disable_journal=args.db_disable_journal,
119                                   syslog_pem_path=args.syslog_pem_path,
120                                   syslog_addrs=args.syslog_addrs))
121
122
123async def async_main(ui_addr: str,
124                     db_path: Path,
125                     db_low_size: int,
126                     db_high_size: int,
127                     db_enable_archive: bool,
128                     db_disable_journal: bool,
129                     syslog_pem_path: Path | None,
130                     syslog_addrs: list[str]):
131    """Syslog Server async main"""
132    async_group = aio.Group()
133
134    async def on_msg(msg):
135        await backend.register(time.time(), msg)
136
137    async def async_close():
138        await async_group.async_close()
139        await asyncio.sleep(0.1)
140
141    try:
142        mlog.debug("creating backend...")
143        backend = await _create_resource(async_group, create_backend,
144                                         db_path, db_low_size, db_high_size,
145                                         db_enable_archive, db_disable_journal)
146
147        mlog.debug("creating web server...")
148        await _create_resource(async_group, create_web_server, ui_addr,
149                               backend)
150
151        mlog.debug("creating syslog servers...")
152        for syslog_addr in syslog_addrs:
153            await _create_resource(async_group, create_syslog_server,
154                                   syslog_addr, on_msg, syslog_pem_path)
155
156        mlog.debug("initialization done")
157        await async_group.wait_closing()
158
159    finally:
160        mlog.debug("closing...")
161        await aio.uncancellable(async_close())
162
163
164async def _create_resource(async_group, fn, *args):
165    resource = await async_group.spawn(fn, *args)
166    async_group.spawn(aio.call_on_cancel, resource.async_close)
167    async_group.spawn(aio.call_on_done, resource.wait_closing(),
168                      async_group.close)
169    return resource
170
171
172if __name__ == '__main__':
173    sys.argv[0] = 'hat-syslog-server'
174    sys.exit(main())
mlog: logging.Logger = <Logger hat.syslog.server.main (WARNING)>

Module logger

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

User data directory path

default_log_level: str = 'INFO'

Default console log level

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

Default UI listening address

default_db_path: pathlib.Path = PosixPath('/home/runner/.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

default_syslog_addrs: list[str] = ['tcp://0.0.0.0:6514', 'udp://0.0.0.0:6514']

Default syslog listening addresses

def create_argument_parser() -> argparse.ArgumentParser:
47def create_argument_parser() -> argparse.ArgumentParser:
48    """Create argument parser"""
49    parser = argparse.ArgumentParser(
50        description="Syslog Server listening for TCP and/or UDP messages. "
51                    "If listening addresses are not provided, Syslog Server "
52                    "listens on 'tcp://0.0.0.0:6514' and "
53                    "'udp://0.0.0.0:6514'.")
54    parser.add_argument(
55        '--log-level', metavar='LEVEL', default=default_log_level,
56        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
57        help=f"console log level (default {default_log_level})")
58    parser.add_argument(
59        '--ui-addr', metavar='ADDR', default=default_ui_addr,
60        help=f"UI listening address (default {default_ui_addr})")
61    parser.add_argument(
62        '--db-path', metavar='PATH', type=Path, default=default_db_path,
63        help="sqlite database file path "
64             "(default $XDG_DATA_HOME/hat/syslog.db)")
65    parser.add_argument(
66        '--db-low-size', metavar='N', type=int, default=default_db_low_size,
67        help=f"number of messages kept in database after database "
68             f"cleanup (default {default_db_low_size})")
69    parser.add_argument(
70        '--db-high-size', metavar='N', type=int, default=default_db_high_size,
71        help=f"number of messages that will trigger database cleanup "
72             f"(default {default_db_high_size})")
73    parser.add_argument(
74        '--db-enable-archive', action='store_true',
75        help="should messages, deleted during database cleanup, be kept "
76             "in archive files")
77    parser.add_argument(
78        '--db-disable-journal', action='store_true',
79        help="disable sqlite journaling")
80    parser.add_argument(
81        '--syslog-pem-path', metavar='PATH', type=Path, default=None,
82        help="certificate PEM path used in case of tls syslog")
83    parser.add_argument(
84        'syslog_addrs', metavar='ADDR', nargs='*',
85        default=default_syslog_addrs,
86        help="syslog listening address formated as <prot>://<host>:<port> "
87             "(<prot> is 'tcp', 'udp' or 'tls'; <host> is host name or IP "
88             "address; <port> is UDP/TCP port)")
89    return parser

Create argument parser

def main():
 92def main():
 93    """Syslog Server"""
 94    parser = create_argument_parser()
 95    args = parser.parse_args()
 96
 97    logging.config.dictConfig({
 98        'version': 1,
 99        'formatters': {
100            'console_formater': {
101                'format': '[%(asctime)s %(levelname)s %(name)s] %(message)s'}},
102        'handlers': {
103            'console_handler': {
104                'class': 'logging.StreamHandler',
105                'formatter': 'console_formater',
106                'level': args.log_level}},
107        'root': {
108            'level': args.log_level,
109            'handlers': ['console_handler']},
110        'disable_existing_loggers': False})
111
112    aio.init_asyncio()
113    with contextlib.suppress(asyncio.CancelledError):
114        aio.run_asyncio(async_main(ui_addr=args.ui_addr,
115                                   db_path=args.db_path,
116                                   db_low_size=args.db_low_size,
117                                   db_high_size=args.db_high_size,
118                                   db_enable_archive=args.db_enable_archive,
119                                   db_disable_journal=args.db_disable_journal,
120                                   syslog_pem_path=args.syslog_pem_path,
121                                   syslog_addrs=args.syslog_addrs))

Syslog Server

async def async_main( ui_addr: str, db_path: pathlib.Path, db_low_size: int, db_high_size: int, db_enable_archive: bool, db_disable_journal: bool, syslog_pem_path: pathlib.Path | None, syslog_addrs: list[str]):
124async def async_main(ui_addr: str,
125                     db_path: Path,
126                     db_low_size: int,
127                     db_high_size: int,
128                     db_enable_archive: bool,
129                     db_disable_journal: bool,
130                     syslog_pem_path: Path | None,
131                     syslog_addrs: list[str]):
132    """Syslog Server async main"""
133    async_group = aio.Group()
134
135    async def on_msg(msg):
136        await backend.register(time.time(), msg)
137
138    async def async_close():
139        await async_group.async_close()
140        await asyncio.sleep(0.1)
141
142    try:
143        mlog.debug("creating backend...")
144        backend = await _create_resource(async_group, create_backend,
145                                         db_path, db_low_size, db_high_size,
146                                         db_enable_archive, db_disable_journal)
147
148        mlog.debug("creating web server...")
149        await _create_resource(async_group, create_web_server, ui_addr,
150                               backend)
151
152        mlog.debug("creating syslog servers...")
153        for syslog_addr in syslog_addrs:
154            await _create_resource(async_group, create_syslog_server,
155                                   syslog_addr, on_msg, syslog_pem_path)
156
157        mlog.debug("initialization done")
158        await async_group.wait_closing()
159
160    finally:
161        mlog.debug("closing...")
162        await aio.uncancellable(async_close())

Syslog Server async main