Logs API - GDScript SDK Documentation
Overview
The Logs API provides endpoints for viewing and analyzing application logs. All operations require superuser authentication and allow you to query request logs, filter by various criteria, and get aggregated statistics.
Key Features:
- List and paginate logs
- View individual log entries
- Filter logs by status, URL, method, IP, etc.
- Sort logs by various fields
- Get hourly aggregated statistics
- Filter statistics by criteria
Backend Endpoints:
GET /api/logs- List logsGET /api/logs/{id}- View logGET /api/logs/stats- Get statistics
Note: All Logs API operations require superuser authentication.
Authentication
All Logs API operations require superuser authentication:
var BosBase = preload("res://gdscript-sdk/src/bosbase.gd")
var pb = BosBase.new("http://127.0.0.1:8090")
# Authenticate as superuser
var auth = await pb.admins().auth_with_password("admin@example.com", "password")
if auth is ClientResponseError:
push_error("Authentication failed: " + auth.to_string())
return
List Logs
Returns a paginated list of logs with support for filtering and sorting.
Basic Usage
# Basic list
var result = await pb.logs.get_list(1, 30)
print(result.page) # 1
print(result.perPage) # 30
print(result.totalItems) # Total logs count
print(result.items) # Array of log entries
Log Entry Structure
Each log entry contains:
{
"id": "ai5z3aoed6809au",
"created": "2024-10-27 09:28:19.524Z",
"level": 0,
"message": "GET /api/collections/posts/records",
"data": {
"auth": "_superusers",
"execTime": 2.392327,
"method": "GET",
"referer": "http://localhost:8090/_/",
"remoteIP": "127.0.0.1",
"status": 200,
"type": "request",
"url": "/api/collections/posts/records?page=1",
"userAgent": "Mozilla/5.0...",
"userIP": "127.0.0.1"
}
}
Filtering Logs
# Filter by HTTP status code
var error_logs = await pb.logs.get_list(1, 50, {
"filter": "data.status >= 400"
})
# Filter by method
var get_logs = await pb.logs.get_list(1, 50, {
"filter": "data.method = \"GET\""
})
# Filter by URL pattern
var api_logs = await pb.logs.get_list(1, 50, {
"filter": "data.url ~ \"/api/\""
})
# Filter by IP address
var ip_logs = await pb.logs.get_list(1, 50, {
"filter": "data.remoteIP = \"127.0.0.1\""
})
# Filter by execution time (slow requests)
var slow_logs = await pb.logs.get_list(1, 50, {
"filter": "data.execTime > 1.0"
})
# Filter by log level
var error_level_logs = await pb.logs.get_list(1, 50, {
"filter": "level > 0"
})
# Filter by date range
var recent_logs = await pb.logs.get_list(1, 50, {
"filter": "created >= \"2024-10-27 00:00:00\""
})
Complex Filters
# Multiple conditions
var complex_filter = await pb.logs.get_list(1, 50, {
"filter": "data.status >= 400 && data.method = \"POST\" && data.execTime > 0.5"
})
# Exclude superuser requests
var user_logs = await pb.logs.get_list(1, 50, {
"filter": "data.auth != \"_superusers\""
})
# Specific endpoint errors
var endpoint_errors = await pb.logs.get_list(1, 50, {
"filter": "data.url ~ \"/api/collections/posts/records\" && data.status >= 400"
})
# Errors or slow requests
var problems = await pb.logs.get_list(1, 50, {
"filter": "data.status >= 400 || data.execTime > 2.0"
})
Sorting Logs
# Sort by creation date (newest first)
var recent = await pb.logs.get_list(1, 50, {
"sort": "-created"
})
# Sort by execution time (slowest first)
var slowest = await pb.logs.get_list(1, 50, {
"sort": "-data.execTime"
})
# Sort by status code
var by_status = await pb.logs.get_list(1, 50, {
"sort": "data.status"
})
# Sort by rowid (most efficient)
var by_rowid = await pb.logs.get_list(1, 50, {
"sort": "-rowid"
})
# Multiple sort fields
var multi_sort = await pb.logs.get_list(1, 50, {
"sort": "-created,level"
})
Get Full List
# Get all logs (be careful with large datasets)
var all_logs = await pb.logs.get_list(1, 1000, {
"filter": "created >= \"2024-10-27 00:00:00\"",
"sort": "-created"
})
View Log
Retrieve a single log entry by ID:
# Get specific log
var log = await pb.logs.get_one("ai5z3aoed6809au")
if log is ClientResponseError:
push_error("Failed to get log: " + log.to_string())
return
print(log.message)
print(log.data.status)
print(log.data.execTime)
Log Details
func analyze_log(log_id: String) -> void:
var log = await pb.logs.get_one(log_id)
if log is ClientResponseError:
push_error("Failed to get log: " + log.to_string())
return
print("Log ID: ", log.id)
print("Created: ", log.created)
print("Level: ", log.level)
print("Message: ", log.message)
if log.data.get("type", "") == "request":
print("Method: ", log.data.method)
print("URL: ", log.data.url)
print("Status: ", log.data.status)
print("Execution Time: ", log.data.execTime, " ms")
print("Remote IP: ", log.data.remoteIP)
print("User Agent: ", log.data.get("userAgent", ""))
print("Auth Collection: ", log.data.get("auth", ""))
Logs Statistics
Get hourly aggregated statistics for logs:
Basic Usage
# Get all statistics
var stats = await pb.logs.get_stats()
if stats is ClientResponseError:
push_error("Failed to get stats: " + stats.to_string())
return
# Each stat entry contains:
# { "total": 4, "date": "2022-06-01 19:00" }
for stat in stats:
print("Date: ", stat.date, ", Total: ", stat.total)
Filtered Statistics
# Statistics for errors only
var error_stats = await pb.logs.get_stats({
"filter": "data.status >= 400"
})
# Statistics for specific endpoint
var endpoint_stats = await pb.logs.get_stats({
"filter": "data.url ~ \"/api/collections/posts/records\""
})
# Statistics for slow requests
var slow_stats = await pb.logs.get_stats({
"filter": "data.execTime > 1.0"
})
# Statistics excluding superuser requests
var user_stats = await pb.logs.get_stats({
"filter": "data.auth != \"_superusers\""
})
Filter Syntax
Logs support filtering with a flexible syntax similar to records filtering.
Supported Fields
Direct Fields:
id- Log IDcreated- Creation timestampupdated- Update timestamplevel- Log level (0 = info, higher = warnings/errors)message- Log message
Data Fields (nested):
data.status- HTTP status codedata.method- HTTP method (GET, POST, etc.)data.url- Request URLdata.execTime- Execution time in secondsdata.remoteIP- Remote IP addressdata.userIP- User IP addressdata.userAgent- User agent stringdata.referer- Referer headerdata.auth- Auth collection IDdata.type- Log type (usually “request”)
Filter Operators
| Operator | Description | Example |
|---|---|---|
= |
Equal | data.status = 200 |
!= |
Not equal | data.status != 200 |
> |
Greater than | data.status > 400 |
>= |
Greater than or equal | data.status >= 400 |
< |
Less than | data.execTime < 0.5 |
<= |
Less than or equal | data.execTime <= 1.0 |
~ |
Contains/Like | data.url ~ "/api/" |
!~ |
Not contains | data.url !~ "/admin/" |
Logical Operators
&&- AND||- OR()- Grouping
Filter Examples
# Simple equality
"filter": "data.method = \"GET\""
# Range filter
"filter": "data.status >= 400 && data.status < 500"
# Pattern matching
"filter": "data.url ~ \"/api/collections/\""
# Complex logic
"filter": "(data.status >= 400 || data.execTime > 2.0) && data.method = \"POST\""
# Exclude patterns
"filter": "data.url !~ \"/admin/\" && data.auth != \"_superusers\""
# Date range
"filter": "created >= \"2024-10-27 00:00:00\" && created <= \"2024-10-28 00:00:00\""
Sort Options
Supported sort fields:
@random- Random orderrowid- Row ID (most efficient, use negative for DESC)id- Log IDcreated- Creation dateupdated- Update datelevel- Log levelmessage- Message textdata.*- Any data field (e.g.,data.status,data.execTime)
# Sort examples
"sort": "-created" # Newest first
"sort": "data.execTime" # Fastest first
"sort": "-data.execTime" # Slowest first
"sort": "-rowid" # Most efficient (newest)
"sort": "level,-created" # By level, then newest
Complete Examples
Example 1: Error Monitoring Dashboard
func get_error_metrics() -> Dictionary:
# Get error logs from last 24 hours
var yesterday = Time.get_unix_time_from_system() - (24 * 60 * 60)
var yesterday_str = Time.get_datetime_string_from_unix_time(yesterday, false)
var date_filter = "created >= \"" + yesterday_str.split("T")[0] + " 00:00:00\""
# 4xx errors
var client_errors = await pb.logs.get_list(1, 100, {
"filter": date_filter + " && data.status >= 400 && data.status < 500",
"sort": "-created"
})
# 5xx errors
var server_errors = await pb.logs.get_list(1, 100, {
"filter": date_filter + " && data.status >= 500",
"sort": "-created"
})
# Get hourly statistics
var error_stats = await pb.logs.get_stats({
"filter": date_filter + " && data.status >= 400"
})
return {
"clientErrors": client_errors.items if not client_errors is ClientResponseError else [],
"serverErrors": server_errors.items if not server_errors is ClientResponseError else [],
"stats": error_stats if not error_stats is ClientResponseError else []
}
Example 2: Performance Analysis
func analyze_performance() -> Dictionary:
# Get slow requests
var slow_requests = await pb.logs.get_list(1, 50, {
"filter": "data.execTime > 1.0",
"sort": "-data.execTime"
})
if slow_requests is ClientResponseError:
push_error("Failed to get slow requests: " + slow_requests.to_string())
return {}
# Analyze by endpoint
var endpoint_stats = {}
for log in slow_requests.items:
var url = log.data.url.split("?")[0] # Remove query params
if not endpoint_stats.has(url):
endpoint_stats[url] = {
"count": 0,
"totalTime": 0.0,
"maxTime": 0.0,
}
var stats = endpoint_stats[url]
stats.count += 1
stats.totalTime += log.data.execTime
stats.maxTime = max(stats.maxTime, log.data.execTime)
# Calculate averages
for url in endpoint_stats:
var stats = endpoint_stats[url]
stats.avgTime = stats.totalTime / stats.count
return endpoint_stats
Example 3: Security Monitoring
func monitor_security() -> Dictionary:
# Failed authentication attempts
var auth_failures = await pb.logs.get_list(1, 100, {
"filter": "data.url ~ \"/api/collections/\" && data.url ~ \"/auth-with-password\" && data.status >= 400",
"sort": "-created"
})
if auth_failures is ClientResponseError:
push_error("Failed to get auth failures: " + auth_failures.to_string())
return {}
# Suspicious IPs (multiple failed attempts)
var ip_counts = {}
for log in auth_failures.items:
var ip = log.data.get("remoteIP", "")
if ip_counts.has(ip):
ip_counts[ip] += 1
else:
ip_counts[ip] = 1
var suspicious_ips = []
for ip in ip_counts:
if ip_counts[ip] >= 5:
suspicious_ips.append({"ip": ip, "attempts": ip_counts[ip]})
return {
"totalFailures": auth_failures.totalItems,
"suspiciousIPs": suspicious_ips
}
Example 4: Log Viewer Component
class_name LogViewer
var pb: BosBase
var current_page: int = 1
var per_page: int = 50
var filter_text: String = ""
var sort_text: String = "-created"
func load_logs() -> Dictionary:
var options = {}
if filter_text != "":
options["filter"] = filter_text
if sort_text != "":
options["sort"] = sort_text
return await pb.logs.get_list(current_page, per_page, options)
func search_logs(search_term: String) -> Dictionary:
filter_text = "message ~ \"" + search_term + "\" || data.url ~ \"" + search_term + "\""
current_page = 1
return await load_logs()
func filter_by_status(status: int) -> Dictionary:
filter_text = "data.status = " + str(status)
current_page = 1
return await load_logs()
func get_error_rate() -> Dictionary:
var today = Time.get_datetime_string_from_system(false).split("T")[0]
var date_filter = "created >= \"" + today + " 00:00:00\""
var stats = await pb.logs.get_stats({
"filter": date_filter
})
var error_stats = await pb.logs.get_stats({
"filter": date_filter + " && data.status >= 400"
})
if stats is ClientResponseError or error_stats is ClientResponseError:
return {"total": 0, "errors": 0, "rate": 0.0}
var total = 0
for stat in stats:
total += stat.get("total", 0)
var errors = 0
for stat in error_stats:
errors += stat.get("total", 0)
return {
"total": total,
"errors": errors,
"rate": (float(errors) / float(total) * 100.0) if total > 0 else 0.0
}
Error Handling
var logs = await pb.logs.get_list(1, 50, {
"filter": "data.status >= 400"
})
if logs is ClientResponseError:
if logs.status == 401:
push_error("Not authenticated")
elif logs.status == 403:
push_error("Not a superuser")
elif logs.status == 400:
push_error("Invalid filter: ", logs.data)
else:
push_error("Unexpected error: ", logs.to_string())
Best Practices
- Use Filters: Always use filters to narrow down results, especially for large log datasets
- Paginate: Use pagination instead of fetching all logs at once
- Efficient Sorting: Use “-rowid” for default sorting (most efficient)
- Filter Statistics: Always filter statistics for meaningful insights
- Monitor Errors: Regularly check for 4xx/5xx errors
- Performance Tracking: Monitor execution times for slow endpoints
- Security Auditing: Track authentication failures and suspicious activity
- Archive Old Logs: Consider deleting or archiving old logs to maintain performance
Limitations
- Superuser Only: All operations require superuser authentication
- Data Fields: Only fields in the
dataobject are filterable - Statistics: Statistics are aggregated hourly
- Performance: Large log datasets may be slow to query
- Storage: Logs accumulate over time and may need periodic cleanup
Log Levels
- 0: Info (normal requests)
- > 0: Warnings/Errors (non-200 status codes, exceptions, etc.)
Higher values typically indicate more severe issues.
Related Documentation
- Authentication - User authentication
- API Records - Record operations
- Collection API - Collection management