210 lines
6.9 KiB
GDScript
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
|