2024-02-12 19:47:28 +01:00
# CORE FRAMEWORK SOURCE FILE
# Copyright (c) 2024 The StarOpenSource Project & Contributors
2024-03-03 18:53:09 +01:00
# Licensed under the GNU Affero General Public License v3
2024-02-12 19:47:28 +01:00
#
# This program is free software: you can redistribute it and/or modify
2024-03-03 18:53:09 +01:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2024-02-12 19:47:28 +01:00
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2024-03-03 18:53:09 +01:00
# GNU Affero General Public License for more details.
2024-02-12 19:47:28 +01:00
#
2024-03-03 18:53:09 +01:00
# You should have received a copy of the GNU Affero General Public License
2024-02-12 19:47:28 +01:00
# along with this program. If not, see <https://www.gnu.org/licenses/>.
## Your usual basic logger implementation, with some extra features.
##
## Allows for colored output, better newlines, multiple logger levels and a
## large variety of placeholders usable in [param config_format].
2024-02-04 21:36:30 +01:00
extends CoreBaseModule
signal log_event
2024-04-09 20:13:49 +02:00
## Keeps track of all logger instances.
## Unused instances will be cleaned periodically.
## [b]Danger: [i]Don't modify this.[/i][/b]
var instances : Array [ CoreLoggerInstance ] = [ ]
2024-04-08 20:14:38 +02:00
## The minimum log level you want to be displayed.
2024-02-04 21:36:30 +01:00
var config_level : CoreTypes . LoggerLevel
2024-04-08 20:14:38 +02:00
## Determines if the logger's output should be colored.
2024-02-04 21:36:30 +01:00
var config_colored : bool
2024-04-08 20:14:38 +02:00
## The template for all log messages[br]
## Available placeholders are: [code]%time%[/code], [code]%time_ms%[/code], [code]%level%[/code], [code]%color%[/code], [code]%message%[/code], [code]%source%[/code], [code]%source_raw%[/code], [code]%function%[/code] and [code]%line%[/code]
2024-02-04 21:36:30 +01:00
var config_format : String
2024-04-08 20:14:38 +02:00
## Determines if identation should be provided if the logger encounters a newline.
2024-02-04 21:36:30 +01:00
var config_newlines_override : bool
2024-04-08 20:14:38 +02:00
## The maximum amount of characters excluding the message before newlines are no longer idented. Set to [code]-1[/code] to disable this behaviour.
2024-02-04 21:36:30 +01:00
var config_newlines_sizelimit : int
2024-04-08 20:14:38 +02:00
# +++ module +++
2024-04-12 17:06:09 +02:00
func _cleanup ( ) - > void :
_schedule ( )
await get_tree ( ) . process_frame ( )
2024-04-12 17:02:20 +02:00
2024-04-09 20:13:49 +02:00
func _schedule ( ) - > void :
for instance in instances :
if ! is_instance_valid ( instance ) : continue
if ! is_instance_valid ( instance . parent ) :
loggeri . diag ( " Removing instance ' " + instance . name + " ' " )
instance . queue_free ( )
instances . remove_at ( instances . find ( instance ) )
2024-02-04 21:36:30 +01:00
func _pull_config ( ) - > void :
config_level = core . config . logger_level
config_colored = core . config . logger_colored
config_format = core . config . logger_format
config_newlines_override = core . config . logger_newlines_override
config_newlines_sizelimit = core . config . logger_newlines_sizelimit
2024-04-08 20:14:38 +02:00
# +++ logging +++
## The main logging function that does the heavy lifting.[br]
2024-04-08 21:19:24 +02:00
## [b]Danger: [i]Don't call this.[/i][/b]
2024-03-05 21:32:13 +01:00
func _log ( level : CoreTypes . LoggerLevel , origin : String , message : String ) - > void :
2024-02-04 21:36:30 +01:00
if ! is_level_allowed ( level ) :
emit_signal ( " log_event " , false , level , origin , message , " " )
return
var format : String = config_format
format = format . replace ( " % time_ms % " , str ( Time . get_ticks_msec ( ) ) )
format = format . replace ( " % time % " , Time . get_time_string_from_system ( true ) )
2024-03-05 21:32:13 +01:00
format = format . replace ( " %o rigin % " , origin )
2024-02-04 21:36:30 +01:00
var format_newline : String = format . replace ( " %c olor % " , " " ) . replace ( " % message % " , " " )
if ! config_colored : format = format . replace ( " %c olor % " , " " )
match ( level ) :
CoreTypes . LoggerLevel . DIAG :
format = format . replace ( " %le vel % " , " DIAG " )
format_newline = format_newline . replace ( " %le vel % " , " DIAG " )
2024-02-10 17:26:03 +01:00
format = format . replace ( " %c olor % " , " [color=dark_gray] " )
2024-02-04 21:36:30 +01:00
CoreTypes . LoggerLevel . VERB :
format = format . replace ( " %le vel % " , " VERB " )
format_newline = format_newline . replace ( " %le vel % " , " VERB " )
format = format . replace ( " %c olor % " , " [color=gray] " )
CoreTypes . LoggerLevel . INFO :
format = format . replace ( " %le vel % " , " INFO " )
format_newline = format_newline . replace ( " %le vel % " , " INFO " )
format = format . replace ( " %c olor % " , " [color=white] " )
CoreTypes . LoggerLevel . WARN :
format = format . replace ( " %le vel % " , " WARN " )
format_newline = format_newline . replace ( " %le vel % " , " WARN " )
format = format . replace ( " %c olor % " , " [color=yellow] " )
CoreTypes . LoggerLevel . ERROR :
format = format . replace ( " %le vel % " , " ERR! " )
format_newline = format_newline . replace ( " %le vel % " , " ERR! " )
format = format . replace ( " %c olor % " , " [color=red] " )
CoreTypes . LoggerLevel . NONE :
2024-02-09 15:28:42 +01:00
format = format . replace ( " %le vel % " , " CRSH " )
format_newline = format_newline . replace ( " %le vel % " , " CRSH " )
format = format . replace ( " %c olor % " , " [b][color=red] " )
2024-02-04 21:36:30 +01:00
# Replace %message%
if config_newlines_override and config_newlines_sizelimit < = - 1 or format_newline . length ( ) < = config_newlines_sizelimit : message = message . replace ( " \n " , " \n " + " " . repeat ( format_newline . length ( ) ) )
format = format . replace ( " % message % " , message )
emit_signal ( " log_event " , true , level , origin , message , format )
if config_colored : print_rich ( format )
else : print ( format )
2024-04-08 20:14:38 +02:00
# +++ self explanitory +++
## Prints a diagnostic log message.
2024-03-05 21:32:13 +01:00
func diag ( origin : String , message : String ) - > void : _log ( CoreTypes . LoggerLevel . DIAG , origin , message )
2024-04-08 20:14:38 +02:00
## Prints a verbose log message.
2024-03-05 21:32:13 +01:00
func verb ( origin : String , message : String ) - > void : _log ( CoreTypes . LoggerLevel . VERB , origin , message )
2024-04-08 20:14:38 +02:00
## Prints an informational log message.
2024-03-05 21:32:13 +01:00
func info ( origin : String , message : String ) - > void : _log ( CoreTypes . LoggerLevel . INFO , origin , message )
2024-04-08 20:14:38 +02:00
## Prints a warning log message.
2024-03-05 21:32:13 +01:00
func warn ( origin : String , message : String ) - > void : _log ( CoreTypes . LoggerLevel . WARN , origin , message )
2024-04-08 20:14:38 +02:00
## Prints an error log message.
2024-03-05 21:32:13 +01:00
func error ( origin : String , message : String ) - > void : _log ( CoreTypes . LoggerLevel . ERROR , origin , message )
2024-04-08 21:19:24 +02:00
## Handles crashes. Will terminate your game/application immediately.[br]
## [b]Note: [i]Using the [code]await[/code] keyword is required for this function.[/i][/b][br]
## [b]Danger: [i]Don't set [code]framework_crash[/code] to [code]true[/code], thanks![/i][/b]
2024-03-05 21:32:13 +01:00
func crash ( origin : String , message : String , framework_crash : bool = false ) - > void :
2024-02-09 15:28:42 +01:00
# Collect information
var stack : Array [ Dictionary ] = get_stack ( )
var mem_info : Dictionary = OS . get_memory_info ( )
var crash_message : String = """ Generating crash report...
######################################
### CORE CRASH HANDLER ###
######################################
% causer % inflicted a crash !
2024-03-05 21:32:13 +01:00
% origin % says :
2024-02-09 15:28:42 +01:00
% message %
+ + + CORE INFORMATION + + +
VERSION
- > Release % version_release %
- > Type % version_type %
- > Typerelease % version_typerelease %
- > Full % version_full %
- > SemVer % version_semantic %
MODES
- > Development % devmode %
- > Headless % headless %
+ + + SYSTEM INFORMATION + + +
OPERATING SYSTEM
- > Operating System % os %
- > Version % os_version %
- > Distribution % os_distribution %
LOCALE
- > Locale % locale %
- > Language % locale_lang %
MEMORY
- > Free % mem_free %
- > Available % mem_avail %
- > Used ( by engine ) % mem_used %
- > Total ( physical ) % mem_total %
- > Peak % mem_peak %
PROCESSOR
- > Name % processor_name %
- > Count % processor_count %
VIDEO
- > Adapter Information % video_adapter %
+ + + GODOT ENGINE INFORMATION + + +
2024-04-08 21:37:29 +02:00
ENGINE
- > Version % godot_version %
2024-02-09 15:28:42 +01:00
BUILD
- > Debug build % godot_debug %
- > Sandboxed % godot_sandboxed %
USERDATA
- > Persistent % godot_persistance %
STACKTRACE
% godot_stacktrace %
######################################
### CORE CRASH HANDLER ###
######################################"""
# Replace placeholders
if framework_crash : crash_message = crash_message . replace ( " %c auser % " , " The CORE Framework " )
else : crash_message = crash_message . replace ( " %c auser % " , " The running application " )
2024-03-05 21:32:13 +01:00
crash_message = crash_message . replace ( " %o rigin % " , origin )
crash_message = crash_message . replace ( " % message % " , message )
2024-04-08 21:37:29 +02:00
crash_message = crash_message . replace ( " % version_release % " , str ( core . version_version ) )
crash_message = crash_message . replace ( " % version_type % " , await core . get_formatted_string ( " % version_type % " ) )
2024-02-09 15:28:42 +01:00
crash_message = crash_message . replace ( " % version_typerelease % " , str ( core . version_typerelease ) )
2024-04-08 21:37:29 +02:00
crash_message = crash_message . replace ( " % version_full % " , str ( core . version_version ) + await core . get_formatted_string ( " - % version_type_technical % " ) + str ( core . version_typerelease ) )
crash_message = crash_message . replace ( " % version_semantic % " , await core . get_formatted_string ( " % version_semantic % " ) )
2024-02-09 15:28:42 +01:00
crash_message = crash_message . replace ( " %d evmode % " , str ( core . is_devmode ( ) ) )
crash_message = crash_message . replace ( " %he adless % " , str ( core . config . headless ) )
crash_message = crash_message . replace ( " %o s % " , OS . get_name ( ) )
crash_message = crash_message . replace ( " %o s_version % " , OS . get_version ( ) )
crash_message = crash_message . replace ( " %o s_distribution % " , OS . get_distribution_name ( ) )
crash_message = crash_message . replace ( " %lo cale % " , OS . get_locale ( ) )
crash_message = crash_message . replace ( " %lo cale_lang % " , OS . get_locale_language ( ) )
crash_message = crash_message . replace ( " % mem_free % " , str ( core . misc . mib2gib ( core . misc . byte2mib ( mem_info [ " free " ] , false ) ) ) + " GiB " )
crash_message = crash_message . replace ( " % mem_avail % " , str ( core . misc . mib2gib ( core . misc . byte2mib ( mem_info [ " available " ] , false ) ) ) + " GiB " )
crash_message = crash_message . replace ( " % mem_used % " , str ( core . misc . byte2mib ( OS . get_static_memory_usage ( ) ) ) + " MiB " )
crash_message = crash_message . replace ( " % mem_total % " , str ( core . misc . mib2gib ( core . misc . byte2mib ( mem_info [ " physical " ] , false ) ) ) + " GiB " )
crash_message = crash_message . replace ( " % mem_peak % " , str ( core . misc . byte2mib ( OS . get_static_memory_peak_usage ( ) ) ) + " MiB " )
crash_message = crash_message . replace ( " % processor_name % " , OS . get_processor_name ( ) )
crash_message = crash_message . replace ( " % processor_count % " , str ( OS . get_processor_count ( ) ) )
crash_message = crash_message . replace ( " % video_adapter % " , str ( OS . get_video_adapter_driver_info ( ) ) )
2024-04-08 21:37:29 +02:00
crash_message = crash_message . replace ( " %g odot_version % " , str ( Engine . get_version_info ( ) [ " string " ] ) )
2024-02-09 15:28:42 +01:00
crash_message = crash_message . replace ( " %g odot_debug % " , str ( OS . is_debug_build ( ) ) )
crash_message = crash_message . replace ( " %g odot_sandboxed % " , str ( OS . is_sandboxed ( ) ) )
crash_message = crash_message . replace ( " %g odot_persistance % " , str ( OS . is_userfs_persistent ( ) ) )
crash_message = crash_message . replace ( " %g odot_stacktrace % " , str ( stack ) )
# Enable newline overrides
config_newlines_override = true
config_newlines_sizelimit = - 1
# Print crash message
2024-03-05 21:32:13 +01:00
_log ( CoreTypes . LoggerLevel . NONE , origin , crash_message )
2024-02-09 15:28:42 +01:00
# Shutdown
2024-04-08 21:42:03 +02:00
await core . quit_safely ( 69 )
2024-03-05 21:32:13 +01:00
2024-04-08 20:14:38 +02:00
# +++ etc +++
## Checks if the specified log level is allowed by the current configuration.
func is_level_allowed ( level : CoreTypes . LoggerLevel ) - > bool :
if level < = config_level : return true
else : return false
## Returns a [class CoreLoggerInstance], which is a fancy word meaning you don't need to pass [code]origin[/code] every time you want to log something.
2024-04-09 20:13:49 +02:00
func get_instance ( origin : String , parent : Object ) - > CoreLoggerInstance :
var instance : CoreLoggerInstance = CoreLoggerInstance . new ( self , origin , parent )
instance . name = " CoreLoggerInstance -> " + str ( parent ) + " ( " + origin + " ) "
instances . append ( instance )
return instance