forked from mirrors/thatmattlove-hyperglass
improve log handling
This commit is contained in:
parent
fee998f168
commit
90725ed67f
3 changed files with 63 additions and 39 deletions
|
|
@ -53,7 +53,7 @@ The following global settings can be set in `hyperglass.yaml`:
|
|||
| `request_timeout` | Integer | `30` | Global timeout in seconds for all requests. The UI uses this field's exact value when submitting queries. The backend uses this field's value, minus one second, for its own timeout handling. This is to ensure a contextual timeout error is presented to the end user in the event of a backend application timeout. |
|
||||
| `listen_address` | String | `'localhost'` | Local IPv4/IPv6 Address the hyperglass application listens on to serve web traffic. |
|
||||
| `listen_port` | Integer | `8001` | Local TCP port the hyperglass application listens on to serve web traffic. |
|
||||
| `log_file` | String | | Path to a log file to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started. |
|
||||
| `log_directory` | String | | Path to a directory, to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started. |
|
||||
| `cors_origins` | List | `[]` | Allowed [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) hosts. By default, no CORS hosts are allowed. |
|
||||
| `netmiko_delay_factor` | Integer \| Float | `0.1` | Override the [Netmiko global delay factor](https://ktbyers.github.io/netmiko/docs/netmiko/index.html). |
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import copy
|
|||
import json
|
||||
import math
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# Third Party
|
||||
import yaml
|
||||
|
|
@ -16,12 +17,10 @@ from pydantic import ValidationError
|
|||
from hyperglass.util import log, check_path, set_app_path
|
||||
from hyperglass.constants import (
|
||||
CREDIT,
|
||||
LOG_LEVELS,
|
||||
LOG_HANDLER,
|
||||
DEFAULT_HELP,
|
||||
DEFAULT_TERMS,
|
||||
DEFAULT_DETAILS,
|
||||
LOG_HANDLER_FILE,
|
||||
SUPPORTED_QUERY_TYPES,
|
||||
__version__,
|
||||
)
|
||||
|
|
@ -83,7 +82,7 @@ STATIC_PATH = CONFIG_PATH / "static"
|
|||
CONFIG_MAIN, CONFIG_DEVICES, CONFIG_COMMANDS = _check_config_files(CONFIG_PATH)
|
||||
|
||||
|
||||
def _set_log_level(debug, log_file=None):
|
||||
def _set_log_level(debug):
|
||||
"""Set log level based on debug state.
|
||||
|
||||
Arguments:
|
||||
|
|
@ -93,27 +92,56 @@ def _set_log_level(debug, log_file=None):
|
|||
{bool} -- True
|
||||
"""
|
||||
stdout_handler = LOG_HANDLER.copy()
|
||||
file_handler = LOG_HANDLER_FILE.copy()
|
||||
|
||||
if debug:
|
||||
log_level = "DEBUG"
|
||||
stdout_handler["level"] = log_level
|
||||
file_handler["level"] = log_level
|
||||
os.environ["HYPERGLASS_LOG_LEVEL"] = log_level
|
||||
log.configure(handlers=[stdout_handler])
|
||||
|
||||
if log_file is not None:
|
||||
file_handler.update({"sink": log_file})
|
||||
log_handlers = [stdout_handler, file_handler]
|
||||
else:
|
||||
log_handlers = [stdout_handler]
|
||||
|
||||
log.remove()
|
||||
log.configure(handlers=log_handlers, levels=LOG_LEVELS)
|
||||
if debug:
|
||||
log.debug("Debugging enabled")
|
||||
return True
|
||||
|
||||
|
||||
def _set_file_logging(log_directory, log_format, log_max_size):
|
||||
"""Set up file-based logging from configuration parameters."""
|
||||
|
||||
if log_format == "json":
|
||||
log_file_name = "hyperglass_log.json"
|
||||
structured = True
|
||||
else:
|
||||
log_file_name = "hyperglass_log.log"
|
||||
structured = False
|
||||
|
||||
log_file = log_directory / log_file_name
|
||||
|
||||
if log_format == "text":
|
||||
now_str = "hyperglass logs for " + datetime.utcnow().strftime(
|
||||
"%B %d, %Y beginning at %H:%M:%S UTC"
|
||||
)
|
||||
now_str_y = len(now_str) + 6
|
||||
now_str_x = len(now_str) + 4
|
||||
log_break = (
|
||||
"#" * now_str_y,
|
||||
"\n#" + " " * now_str_x + "#\n",
|
||||
"# ",
|
||||
now_str,
|
||||
" #",
|
||||
"\n#" + " " * now_str_x + "#\n",
|
||||
"#" * now_str_y,
|
||||
)
|
||||
|
||||
with log_file.open("a+") as lf:
|
||||
lf.write(f'\n\n{"".join(log_break)}\n\n')
|
||||
|
||||
log.add(log_file, rotation=log_max_size, serialize=structured)
|
||||
|
||||
log.debug("Logging to file enabled")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _config_required(config_path: Path) -> dict:
|
||||
try:
|
||||
with config_path.open("r") as cf:
|
||||
|
|
@ -219,6 +247,12 @@ except ValidationError as validation_errors:
|
|||
error_msg=error["msg"],
|
||||
)
|
||||
|
||||
# Set up file logging once configuration parameters are initialized.
|
||||
_set_file_logging(
|
||||
log_directory=params.log_directory,
|
||||
log_format=params.log_format,
|
||||
log_max_size=params.log_max_size,
|
||||
)
|
||||
|
||||
# Perform post-config initialization string formatting or other
|
||||
# functions that require access to other config levels. E.g.,
|
||||
|
|
@ -255,7 +289,7 @@ except KeyError:
|
|||
|
||||
|
||||
# Re-evaluate debug state after config is validated
|
||||
_set_log_level(params.debug, params.log_file)
|
||||
_set_log_level(params.debug)
|
||||
|
||||
|
||||
def _build_frontend_networks():
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
# Standard Library
|
||||
from typing import List, Union, Optional
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from ipaddress import ip_address
|
||||
|
||||
# Third Party
|
||||
from pydantic import (
|
||||
Field,
|
||||
FilePath,
|
||||
ByteSize,
|
||||
StrictInt,
|
||||
StrictStr,
|
||||
StrictBool,
|
||||
DirectoryPath,
|
||||
IPvAnyAddress,
|
||||
constr,
|
||||
validator,
|
||||
|
|
@ -98,10 +98,18 @@ class Params(HyperglassModel):
|
|||
title="Listen Port",
|
||||
description="Local TCP port the hyperglass application listens on to serve web traffic.",
|
||||
)
|
||||
log_file: Optional[FilePath] = Field(
|
||||
None,
|
||||
title="Log File",
|
||||
description="Path to a log file to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started.",
|
||||
log_directory: DirectoryPath = Field(
|
||||
Path("/tmp"), # noqa: S108
|
||||
title="Log Directory",
|
||||
description="Path to a directory, to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started.",
|
||||
)
|
||||
log_format: constr(regex=r"(text|json)") = Field(
|
||||
"text", title="Log Format", description="Format for logs written to a file."
|
||||
)
|
||||
log_max_size: ByteSize = Field(
|
||||
"50MB",
|
||||
title="Maximum Log File Size",
|
||||
description="Maximum storage space log file may consume.",
|
||||
)
|
||||
cors_origins: List[StrictStr] = Field(
|
||||
[],
|
||||
|
|
@ -165,24 +173,6 @@ class Params(HyperglassModel):
|
|||
"""
|
||||
return value.format(org_name=values["org_name"])
|
||||
|
||||
@validator("log_file")
|
||||
def validate_log_file(cls, value):
|
||||
"""Set default logfile location if none is configured.
|
||||
|
||||
Arguments:
|
||||
value {FilePath} -- Path to log file
|
||||
|
||||
Returns:
|
||||
{Path} -- Logfile path object
|
||||
"""
|
||||
if value is None:
|
||||
now = datetime.now()
|
||||
now.isoformat
|
||||
value = Path(
|
||||
f'/tmp/hyperglass_{now.strftime(r"%Y%M%d_%H-%M-%S")}.log' # noqa: S108
|
||||
)
|
||||
return value
|
||||
|
||||
@validator("primary_asn")
|
||||
def validate_primary_asn(cls, value):
|
||||
"""Stringify primary_asn if passed as an integer.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue