added logging

This commit is contained in:
2026-02-17 02:57:44 +01:00
parent daffc4f458
commit 5445a5155d
5 changed files with 55 additions and 69 deletions

2
.gitignore vendored
View File

@@ -5,5 +5,5 @@ node_modules
config.py
slides
exports
log
logs
nohup.out

View File

@@ -1,4 +1,6 @@
from logging.config import dictConfig
import os
import logging
from logging.handlers import WatchedFileHandler
from flask import Flask
from flask_socketio import SocketIO
from .models import db
@@ -9,62 +11,22 @@ from .seed_db import seed_courses
socketio = SocketIO(async_mode='eventlet', cors_allowed_origins="*")
def create_app():
# Configure logging BEFORE creating the application object
dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
},
'plain': {
'format': '%(message)s',
}
},
'handlers': {
# Console handler (keeps logs visible in terminal)
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
},
# File handler for Application Logs (slides.log)
'slides_file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'slides.log',
'maxBytes': 1024 * 1024 * 10, # 10 MB
'backupCount': 5,
'formatter': 'default',
'level': 'INFO',
'encoding': 'utf8'
},
# File handler for Request Logs (requests.log)
'requests_file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'requests.log',
'maxBytes': 1024 * 1024 * 10, # 10 MB
'backupCount': 5,
'formatter': 'plain', # Requests already have timestamps/formatting
'level': 'INFO',
'encoding': 'utf8'
}
},
'root': {
'level': 'INFO',
'handlers': ['wsgi', 'slides_file']
},
'loggers': {
# Isolate the Werkzeug request logger
'werkzeug': {
'level': 'INFO',
'handlers': ['wsgi', 'requests_file'],
'propagate': False # Prevent requests from leaking into slides.log
}
}
})
# 1. Setup Log Directory
log_dir = os.path.join(os.getcwd(), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
app = Flask(__name__, static_folder='static', static_url_path='/static')
app.config.from_object('config.Config')
file_handler = WatchedFileHandler(log_dir + '/app.log', mode='a', encoding='UTF-8')
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s [%(module)s:%(lineno)d]: %(message)s'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
db.init_app(app)
with app.app_context():

View File

@@ -100,15 +100,16 @@ def inject_confused_button(response):
def index():
token = get_token()
if not token:
current_app.logger.debug("User has no token. Redirecting to set_token.")
return redirect(url_for("main.set_token"))
else:
# Use current_app.logger for debug info
current_app.logger.debug(f"User {token.name} is viewing index with {len(token.decks)} decks unlocked.")
return render_template('index.html', active_banner=get_active_banner(), decks=get_unlocked_decks(token), user_name=token.name, active_course_name=token.active_course.name, courses=get_all_courses())
def set_cookie(token):
next_url = session.pop('next_url', None)
if not next_url:
current_app.logger.debug("next_url not present redirecting to index")
next_url = url_for("main.index")
resp = make_response(redirect(next_url))
@@ -130,21 +131,22 @@ def set_token():
"""
if request.method == 'POST':
# Get form data
user_name = request.form.get('username')
username = request.form.get('username')
# Simple validation
if not user_name:
if not username:
current_app.logger.debug("Login failed: No username supplied")
flash('Username is required.', 'error')
return render_template('set_token.html')
token = retrieve_token(user_name)
token = retrieve_token(username)
if not token:
flash(f'The username "{user_name}" does not exist.', 'error')
current_app.logger.warning(f"Login failed: Username '{user_name}' not found.")
flash(f'The username "{username}" does not exist.', 'error')
current_app.logger.warning(f"Login failed: Username '{username}' not found.")
return render_template('set_token.html')
current_app.logger.info(f"User logged in: {user_name}")
current_app.logger.info(f"User logged in: {username}")
return set_cookie(token)
return render_template('set_token.html')
@@ -165,14 +167,17 @@ def register():
# Simple validation
if not user_name:
flash('Username is required.', 'error')
current_app.logger.debug("Username is required for registering")
return render_template('register.html')
if retrieve_token(user_name):
flash(f'The username "{user_name}" is already taken.', 'error')
current_app.logger.debug(f"Username {user_name} is already registered")
return render_template('register.html')
if not acknowledge:
flash('You must acknowledge the important information to proceed.', 'error')
current_app.logger.debug("User did not acknowledge register information")
return render_template('register.html')
# If all validation passes, "register" the user
@@ -282,10 +287,6 @@ def access_get(access_code: str):
@main.route('/confused', methods=['POST'])
def confused():
token = get_token()
user = token.name if token else "Anonymous"
current_app.logger.info(f"Confused button clicked by: {user}")
socketio.emit(
"confused",
{
@@ -376,7 +377,7 @@ def serve_export(deck, course_folder, path):
def admin():
active_course = get_course(flask_login.current_user.active_course_id)
if active_course is None:
current_app.logger.error(f"Admin user {flask_login.current_user.id} has invalid active_course_id.")
current_app.logger.error(f"Admin user {flask_login.current_user.id} has no active_course_id.")
raise Exception
deck_form = DeckForm()
password_form = PasswordForm()

View File

@@ -171,7 +171,10 @@
{% for password in passwords %}
<tr class="border-b border-border hover:bg-background/30">
<td class="px-4 py-2 font-medium">{{ password.value }}</td>
<td class="px-4 py-2">{{ password.expires_at }}</td>
<td class="px-4 py-2 local-time"
data-utc="{{ password.expires_at.strftime('%Y-%m-%dT%H:%M:%SZ') if password.expires_at }}">
{{ password.expires_at }}
</td>
<td class="px-4 py-2">
{% if password.decks %}
<ul class="list-disc list-inside text-muted">
@@ -202,6 +205,25 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/flowbite@3.1.2/dist/flowbite.min.js"></script>
<script>
document.querySelectorAll('.local-time').forEach(el => {
const utcStr = el.getAttribute('data-utc');
if (utcStr) {
// Now utcStr is "2026-02-17T02:44:23Z"
const date = new Date(utcStr);
if (!isNaN(date)) {
el.textContent = date.toLocaleString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
}
});
</script>
</body>
</html>

View File

@@ -2,8 +2,9 @@
npm run build:css
nohup .venv/bin/gunicorn \
nohup uv run gunicorn \
--bind 127.0.0.1:5000 \
--worker-class eventlet \
--workers 1 \
--access-logfile logs/traffic.log \
run:app &