the ability to search is done.

modified:   .gitignore
	modified:   app.py
	modified:   templates/base.html.j2
	modified:   templates/view_namespace_logs.html.j2
	modified:   test_agent.sh
This commit is contained in:
Russell Ballestrini 2025-01-12 06:57:06 -05:00
parent 638d19fd77
commit 65df3368f3
5 changed files with 131 additions and 32 deletions

4
.gitignore vendored
View file

@ -2,4 +2,8 @@
*.swp
env/
__pycache__/
cookies.txt
jwt_secret.txt
pyralogs_secret.txt

38
app.py
View file

@ -36,6 +36,7 @@ from sqlalchemy import (
ForeignKey,
Table,
Text,
or_,
)
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import sessionmaker
@ -143,11 +144,11 @@ def get_gravatar_url(email, size=100):
def send_email(to_email, subject, body):
log.debug("======= Email Sent =======")
log.debug(f"To: {to_email}")
log.debug(f"Subject: {subject}")
log.debug(f"Body:\n{body}")
log.debug("==========================")
log.info("======= Email Sent =======")
log.info(f"To: {to_email}")
log.info(f"Subject: {subject}")
log.info(f"Body:\n{body}")
log.info("==========================")
msg = MIMEText(body)
msg["Subject"] = subject
@ -1039,18 +1040,39 @@ def log_webhook_view(request):
return Response("Log entry created.", status=201)
@view_config(route_name="view_namespace_logs", renderer="view_namespace_logs.html.j2")
@view_config(
route_name="view_namespace_logs", renderer="view_namespace_logs.html.j2"
)
@reader_required
def view_namespace_logs_view(request):
namespace = request.namespace
namespace_dbsession = request.namespace_dbsession
logs = namespace_dbsession.query(LogEntry).order_by(LogEntry.timestamp.desc()).all()
# Get the search query from the request parameters
search_query = request.params.get('q', '').strip()
# Start building the query
query = namespace_dbsession.query(LogEntry)
if search_query:
# Create a search pattern for case-insensitive search
search_pattern = f"%{search_query}%"
query = query.filter(
or_(
LogEntry.message.ilike(search_pattern),
LogEntry.level.ilike(search_pattern),
LogEntry.log_metadata.ilike(search_pattern),
)
)
logs = query.order_by(LogEntry.timestamp.desc()).all()
return {
"request": request,
"namespace": namespace,
"logs": logs,
"search_query": search_query,
}

View file

@ -39,6 +39,11 @@
margin: 0;
}
mark {
background-color: yellow;
color: black;
}
/* Hamburger Menu Button */
#mobile-menu-button {
display: none;

View file

@ -1,33 +1,100 @@
{% extends 'base.html.j2' %}
{% block title %}Namespace Logs{% endblock %}
{% block title %}Logs for {{ namespace.name }}{% endblock %}
{% block content %}
<h1>Logs for Namespace: {{ namespace.name }}</h1>
{% if logs %}
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>Level</th>
<th>Message</th>
<th>Metadata</th>
</tr>
</thead>
<tbody>
{% for log in logs %}
<!-- Search Form -->
<form method="get" action="{{ request.current_route_url() }}">
<input type="text" name="q" value="{{ search_query }}" placeholder="Search logs">
<button type="submit">Search</button>
{% if search_query %}
<a href="{{ request.route_url('view_namespace_logs', namespace_short_id=namespace.short_id) }}">Clear Search</a>
{% endif %}
</form>
<!-- Logs Table -->
<div class="logs-container">
{% if logs %}
<table class="logs-table">
<thead>
<tr>
<td>{{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td>
<td>{{ log.level }}</td>
<td>{{ log.message }}</td>
<td>{{ log.log_metadata }}</td>
<th>Timestamp</th>
<th>Level</th>
<th>Message</th>
<th>Metadata</th>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No logs available.</p>
</thead>
<tbody>
{% for log in logs %}
<tr class="log-entry">
<td class="log-timestamp">{{ log.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td>
<td class="log-level">{{ log.level }}</td>
<td class="log-message">{{ log.message }}</td>
<td class="log-metadata">
{% if log.log_metadata %}
<pre>{{ log.log_metadata }}</pre>
{% else %}
N/A
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No logs found.</p>
{% endif %}
</div>
<!-- Include JavaScript for highlighting -->
{% if search_query %}
<script>
(function() {
// Function to escape special characters in regex
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
var searchQuery = {{ search_query|tojson }};
if (!searchQuery) {
return;
}
var regex = new RegExp(escapeRegExp(searchQuery), 'gi');
// Function to highlight text
function highlightText(element) {
element.childNodes.forEach(function(node) {
if (node.nodeType === Node.TEXT_NODE && node.parentNode.nodeName !== 'MARK') {
var text = node.textContent;
var html = text.replace(regex, function(match) {
return '<mark>' + match + '</mark>';
});
if (html !== text) {
var temp = document.createElement('span');
temp.innerHTML = html;
// Replace the text node with the new HTML
var fragment = document.createDocumentFragment();
while (temp.firstChild) {
fragment.appendChild(temp.firstChild);
}
node.parentNode.replaceChild(fragment, node);
}
} else if (node.nodeType === Node.ELEMENT_NODE && node.nodeName !== 'SCRIPT') {
highlightText(node);
}
});
}
// Highlight search terms in log entries
document.querySelectorAll('.log-entry td').forEach(function(element) {
highlightText(element);
});
})();
</script>
{% endif %}
{% endblock %}

View file

@ -9,7 +9,7 @@
# Variables
BASE_URL="http://127.0.0.1:6544"
JWT_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZ2VudF9pZCI6IjcxZmQ0ZjA0LTQ2NzgtNGEyNi05ZmU4LTkxNjU2OTNmOTBmOSIsImFnZW50X25hbWUiOiJ0ZXN0LTkwMDAiLCJuYW1lc3BhY2VfaWQiOiJhZGQwYWQ1YS0zNjE0LTRhODgtOTRmZC0xNGQ4ZDBkYjIxYjUiLCJ0b2tlbl92ZXJzaW9uIjozLCJpYXQiOjE3MzY2NDYxNzV9.BzTXP6W6dcKQEwB970zTT5JPaJJsb0rybQv774wQy-s"
JWT_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZ2VudF9pZCI6IjcxZmQ0ZjA0LTQ2NzgtNGEyNi05ZmU4LTkxNjU2OTNmOTBmOSIsImFnZW50X25hbWUiOiJ0ZXN0LTkwMDAiLCJuYW1lc3BhY2VfaWQiOiJhZGQwYWQ1YS0zNjE0LTRhODgtOTRmZC0xNGQ4ZDBkYjIxYjUiLCJ0b2tlbl92ZXJzaW9uIjo1LCJpYXQiOjE3MzY2ODIxMTV9.p2HQmjFBtL9XLePtVCu3HyPhw89993BO4b6E82SwyEM"
# Log message from the first argument
LOG_MESSAGE="$1"
@ -26,7 +26,8 @@ RESPONSE=$(curl -s -X POST "$BASE_URL/webhook" \
-H "Authorization: Bearer $JWT_TOKEN" \
-d "{
\"message\": \"$LOG_MESSAGE\",
\"level\": \"INFO\"
\"level\": \"INFO\",
\"metadata\": {\"w\": 1}
}")
# Output the server's response