refac: make WGDashboard logging level configurable (#915)

* feat: init configurable logging level

* refac: correct some logging functions to debug from info. They were not that informational

* fix: logging somehow was broken due to disablement of external loggers

* refac: set default to info

---------

Co-authored-by: Daan Selen <dselen@systemec.nl>
This commit is contained in:
DaanSelen
2025-09-22 18:20:22 +02:00
committed by GitHub
parent b7e65f7caf
commit 8b541229d8
11 changed files with 55 additions and 30 deletions

View File

@@ -104,6 +104,7 @@ ARG wg_port="51820"
ENV TZ="Europe/Amsterdam" \
global_dns="9.9.9.9" \
wgd_port="10086" \
log_level="INFO" \
public_ip="" \
WGDASH=/opt/wgdashboard

View File

@@ -102,6 +102,7 @@ Updating the WGDashboard container should be through 'The Docker Way' - by pulli
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NATd. |
| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
| `log_level` | `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL` | `INFO` | `WARNING` | Sets the severity of the logs being displayed. |
| `username` | Any nonempty string | `-` | `admin` | Username for the WGDashboard web interface account. |
| `password` | Any nonempty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTPbased twofactor authentication for the account. |

View File

@@ -12,7 +12,8 @@ services:
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
#environment:
environment:
- log_level=WARNING
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.

View File

@@ -137,6 +137,7 @@ set_envvars() {
set_ini Peers remote_endpoint "${public_ip}"
set_ini Server app_port "${wgd_port}"
set_ini Server log_level "${log_level}"
# Account settings - process all parameters
[[ -n "$username" ]] && echo "Configuring user account:"

View File

@@ -72,7 +72,6 @@ def ResponseObject(status=True, message=None, data=None, status_code = 200) -> F
'''
Flask App
'''
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"))
def peerInformationBackgroundThread():
global WireguardConfigurations
@@ -120,7 +119,8 @@ def peerJobScheduleBackgroundThread():
def gunicornConfig():
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
return app_ip, app_port
return app_ip, app_port, log_level
def ProtocolsEnabled() -> list[str]:
from shutil import which
@@ -172,16 +172,7 @@ def startThreads():
scheduleJobThread = threading.Thread(target=peerJobScheduleBackgroundThread, daemon=True)
scheduleJobThread.start()
dictConfig({
'version': 1,
'formatters': {'default': {
'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s',
}},
'root': {
'level': 'INFO'
}
})
app = Flask("WGDashboard", template_folder=os.path.abspath("./static/dist/WGDashboardAdmin"))
WireguardConfigurations: dict[str, WireguardConfiguration] = {}
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
@@ -189,6 +180,7 @@ CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
app.secret_key = secrets.token_urlsafe(32)
app.json = CustomJsonEncoder(app)
with app.app_context():
SystemStatus = SystemStatus()
DashboardConfig = DashboardConfig()
@@ -199,19 +191,44 @@ with app.app_context():
DashboardPlugins: DashboardPlugins = DashboardPlugins(app, WireguardConfigurations)
DashboardWebHooks: DashboardWebHooks = DashboardWebHooks(DashboardConfig)
NewConfigurationTemplates: NewConfigurationTemplates = NewConfigurationTemplates()
InitWireguardConfigurationsList(startup=True)
DashboardClients: DashboardClients = DashboardClients(WireguardConfigurations)
app.register_blueprint(createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients))
app.register_blueprint(
createClientBlueprint(WireguardConfigurations, DashboardConfig, DashboardClients)
)
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
_, log_level = DashboardConfig.GetConfig("Server", "log_level")
cors = CORS(app, resources={rf"{APP_PREFIX}/api/*": {
"origins": "*",
"methods": "DELETE, POST, GET, OPTIONS",
"allow_headers": ["Content-Type", "wg-dashboard-apikey"]
}})
_, app_ip = DashboardConfig.GetConfig("Server", "app_ip")
_, app_port = DashboardConfig.GetConfig("Server", "app_port")
_, WG_CONF_PATH = DashboardConfig.GetConfig("Server", "wg_conf_path")
app.logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '[%(asctime)s] [%(levelname)s] in [%(module)s] %(message)s',
},
},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default',
},
}
})
'''
API Routes

View File

@@ -1,7 +1,7 @@
import dashboard
from datetime import datetime
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger, Dash
app_host, app_port = dashboard.gunicornConfig()
app_host, app_port, log_level = dashboard.gunicornConfig()
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
def post_worker_init(worker):
@@ -16,7 +16,7 @@ daemon = True
pidfile = './gunicorn.pid'
wsgi_app = "dashboard:app"
accesslog = f"./log/access_{date}.log"
loglevel = "info"
loglevel = f"{log_level}"
capture_output = True
errorlog = f"./log/error_{date}.log"
pythonpath = "., ./modules"

View File

@@ -174,7 +174,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
def getPeers(self):
self.Peers.clear()
current_app.logger.info(f"Refreshing {self.Name} peer list")
current_app.logger.debug(f"Refreshing {self.Name} peer list")
if self.configurationFileChanged():
with open(self.configPath, 'r') as configFile:
@@ -183,7 +183,7 @@ class AmneziaWireguardConfiguration(WireguardConfiguration):
content = configFile.read().split('\n')
try:
if "[Peer]" not in content:
current_app.logger.info(f"{self.Name} config has no [Peer] section")
current_app.logger.debug(f"{self.Name} config has no [Peer] section")
return
peerStarts = content.index("[Peer]")

View File

@@ -35,10 +35,13 @@ def ConnectionString(database_name: str) -> str:
host = parser.get("Database", "host")
cn = f"mysql+pymysql://{username}:{password}@{host}/{database_name}"
else:
cn = f"sqlite:///{os.path.join(SQLITE_PATH, f'{database_name}.db')}"
cn = f'sqlite:///{os.path.join(sqlitePath, f"{database}.db")}'
# Ensure database exists
try:
if not database_exists(cn):
create_database(cn)
except Exception as e:
current_app.logger.critical("Database error. Terminating...", e)
exit(1)
return cn

View File

@@ -47,7 +47,8 @@ class DashboardConfig:
"dashboard_sort": "status",
"dashboard_theme": "dark",
"dashboard_api_key": "false",
"dashboard_language": "en-US"
"dashboard_language": "en-US",
"log_level": "INFO"
},
"Peers": {
"peer_global_DNS": "1.1.1.1",

View File

@@ -141,7 +141,7 @@ class PeerJobs:
def runJob(self):
current_app.logger.info("Running scheduled jobs")
current_app.logger.debug("Running scheduled jobs")
needToDelete = []
self.__getJobs()
for job in self.Jobs:

View File

@@ -396,7 +396,7 @@ class WireguardConfiguration:
def getPeers(self):
tmpList = []
current_app.logger.info(f"Refreshing {self.Name} peer list")
current_app.logger.debug(f"Refreshing {self.Name} peer list")
if self.configurationFileChanged():
with open(self.configPath, 'r') as configFile:
@@ -405,7 +405,7 @@ class WireguardConfiguration:
content = configFile.read().split('\n')
try:
if "[Peer]" not in content:
current_app.logger.info(f"{self.Name} config has no [Peer] section")
current_app.logger.debug(f"{self.Name} config has no [Peer] section")
return
peerStarts = content.index("[Peer]")