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:
parent
638d19fd77
commit
65df3368f3
5 changed files with 131 additions and 32 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -2,4 +2,8 @@
|
|||
*.swp
|
||||
env/
|
||||
__pycache__/
|
||||
|
||||
cookies.txt
|
||||
jwt_secret.txt
|
||||
pyralogs_secret.txt
|
||||
|
||||
|
|
|
|||
38
app.py
38
app.py
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
mark {
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* Hamburger Menu Button */
|
||||
#mobile-menu-button {
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue