added logging
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,5 +5,5 @@ node_modules
|
||||
config.py
|
||||
slides
|
||||
exports
|
||||
log
|
||||
logs
|
||||
nohup.out
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 &
|
||||
|
||||
Reference in New Issue
Block a user