Files
car-os/addons/loggie-console/scripts/components/log_buffer.gd
T
2026-07-04 18:22:23 -06:00

210 lines
6.9 KiB
GDScript

class_name LogBuffer extends RichTextLabel
## Enhanced message buffer with retroactive filtering capabilities
## Stores all messages and applies filters dynamically without losing data
const LoggieConsoleSettings = preload("res://addons/loggie-console/resources/loggie_console_settings.gd")
const DomainColorManager = preload("res://addons/loggie-console/scripts/domain_color_manager.gd")
# Signals
signal filter_changed(visible_count: int, total_count: int)
# Internal message storage
var _all_messages: Array[LogMessage] = []
var _filtered_messages: Array[LogMessage] = []
var _current_filters: FilterState
# Domain color manager reference
var _color_manager: DomainColorManager
# Memory management constants
const DEFAULT_MAX_MESSAGES: int = 5000
const CLEANUP_PERCENTAGE: float = 0.2 # Remove 20% of messages when limit reached
# Memory management
@export var max_messages: int = DEFAULT_MAX_MESSAGES
## Internal LogMessage class for storing complete message data
class LogMessage:
var msg: LoggieMsg
var preprocessed_content: String
var msg_type: LoggieEnums.MsgType
var log_level: LoggieEnums.LogLevel
var timestamp: float
var enhanced_content: String
func _init(p_msg: LoggieMsg, p_content: String, p_type: LoggieEnums.MsgType, p_level: LoggieEnums.LogLevel) -> void:
msg = p_msg
preprocessed_content = p_content
msg_type = p_type
log_level = p_level
timestamp = Time.get_unix_time_from_system()
## Filter state for retroactive filtering
class FilterState:
var enabled_domains: Array[String] = []
var min_log_level: LoggieEnums.LogLevel = LoggieEnums.LogLevel.DEBUG
var text_search: String = ""
var show_stack_only: bool = false
func _init() -> void:
# Initialize to show ERROR and above by default (lower numbers = higher priority)
min_log_level = LoggieEnums.LogLevel.DEBUG
func _ready() -> void:
_current_filters = FilterState.new()
bbcode_enabled = true
## Set the domain color manager instance
func set_color_manager(color_manager: DomainColorManager) -> void:
_color_manager = color_manager
## Add a new log message to the buffer
func add_message(msg: LoggieMsg, content: String, msg_type: LoggieEnums.MsgType, log_level: LoggieEnums.LogLevel) -> void:
# Create and store the message
var log_msg: LogMessage = LogMessage.new(msg, content, msg_type, log_level)
log_msg.enhanced_content = _enhance_with_metadata(log_msg)
_all_messages.append(log_msg)
# Manage memory if needed
_manage_memory()
# Apply current filters and rebuild display
_reapply_filters()
## Set new filter state and reapply filters
func set_filters(filter_state: FilterState) -> void:
_current_filters = filter_state
_reapply_filters()
## Set text search filter
func set_text_search(search: String) -> void:
if _current_filters.text_search == search:
return
_current_filters.text_search = search
_reapply_filters()
## Clear all messages
func clear_all() -> void:
_all_messages.clear()
_filtered_messages.clear()
text = ""
clear()
# Emit updated counts
filter_changed.emit(0, 0)
## Get current message counts
func get_message_counts() -> Dictionary:
return {
"total": _all_messages.size(),
"filtered": _filtered_messages.size()
}
## Saves current buffer state to persistent settings resource
## Param settings: The settings resource to populate with current state
func save_settings_to_resource(settings: LoggieConsoleSettings) -> void:
settings.filter_state.from_filter_state(_current_filters)
settings.max_messages = max_messages
## Loads buffer state from persistent settings resource
## Note: Does not immediately apply filters to avoid triggering reapply before messages exist
## Param settings: The settings resource containing saved state
func load_settings_from_resource(settings: LoggieConsoleSettings) -> void:
if settings.filter_state:
_current_filters = settings.filter_state.to_filter_state()
max_messages = settings.max_messages
## Applies previously loaded filter state to existing messages
## Call this after load_settings_from_resource() and message loading is complete
func apply_loaded_filters() -> void:
_reapply_filters()
## Private methods
func _reapply_filters() -> void:
_filtered_messages.clear()
for msg in _all_messages:
if msg != null and _message_passes_filters(msg, _current_filters):
_filtered_messages.append(msg)
_rebuild_display()
# Emit filter change signal
filter_changed.emit(_filtered_messages.size(), _all_messages.size())
func _message_passes_filters(msg: LogMessage, filters: FilterState) -> bool:
# Domain check - if no domains are enabled, show no messages
if filters.enabled_domains.size() == 0:
return false
# If domains are enabled, check if this message's domain is in the enabled list
if not filters.enabled_domains.has(msg.msg.domain_name):
return false
# Level check - show messages at or above the minimum level (lower numbers = higher priority)
if msg.log_level > filters.min_log_level:
return false
# Text search
if not filters.text_search.is_empty():
var search_lower: String = filters.text_search.to_lower()
if not msg.preprocessed_content.to_lower().contains(search_lower):
return false
# Stack filter
if filters.show_stack_only and not msg.msg.appends_stack:
return false
return true
func _rebuild_display() -> void:
# Clear current display
text = ""
clear()
# Add all filtered messages
for msg in _filtered_messages:
text += msg.enhanced_content + "\n"
func _enhance_with_metadata(msg: LogMessage) -> String:
var metadata_parts: Array[String] = []
metadata_parts.append("[color=%s]%s [/color]" % [Color.DIM_GRAY.to_html(false), Time.get_time_string_from_unix_time(int(msg.timestamp))])
if msg.msg.domain_name and not msg.msg.domain_name.is_empty():
var domain_color_html: String = _color_manager.get_domain_color_html(msg.msg.domain_name)
metadata_parts.append("[color=%s]%s[/color]" % [domain_color_html, msg.msg.domain_name])
if msg.msg.domain_name and not msg.msg.domain_name.is_empty():
metadata_parts.append("[color=%s][%s] [/color]" % [_get_color_for_log_level(msg.log_level).to_html(false), LoggieEnums.LogLevel.keys()[msg.log_level]])
return "%s %s" % [" ".join(metadata_parts), msg.msg.content[0]]
func _get_color_for_log_level(level: LoggieEnums.LogLevel) -> Color:
match level:
LoggieEnums.LogLevel.ERROR:
return Color.RED
LoggieEnums.LogLevel.WARN:
return Color.ORANGE
LoggieEnums.LogLevel.NOTICE:
return Color.GREEN
LoggieEnums.LogLevel.INFO:
return Color.WHITE
LoggieEnums.LogLevel.DEBUG, _:
return Color.AQUA
## Manages memory by removing oldest messages when buffer limit exceeded
## Uses CLEANUP_PERCENTAGE constant to determine how many messages to remove
func _manage_memory() -> void:
if _all_messages.size() > max_messages:
# Remove oldest messages based on cleanup percentage
var remove_count: int = int(max_messages * CLEANUP_PERCENTAGE)
_all_messages = _all_messages.slice(remove_count)
# Note: This will cause filtered_messages indices to be invalid,
# but _reapply_filters() will be called which rebuilds it