Files
uarep-ctl/addons/godot-logger/scripts/logger.gd

266 lines
7.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2020-2023 Mansur Isaev and contributors - MIT License
# See `LICENSE.md` included in the source distribution for details.
## Класс журнал событий.
class_name Logger extends Node
## Выдается, когда журнал событий создаёт сообщение.
signal logged(message: Dictionary)
enum {
INFO = 1, ## Информационный уровень.
DEBUG = 1<<1, ## Уровень предупреждения.
WARNING = 1<<2, ## Уровень отладки.
ERROR = 1<<3, ## Уровень ошибок.
FATAL = 1<<4, ## Критический уровень.
MAX = FATAL, ## Побитовый сдвиг влево для пользовательских уровней.
}
var _level : int
var _log_enabled : bool
var _default_output_enabled : bool
var _file_write_enabled : bool
var _file : FileAccess
var _file_path : String
var _format_default_output : String
var _format_file : String
var _names = {
INFO: "ИНФО",
DEBUG: "ОТЛАДКА",
WARNING: "ВНИМАНИЕ",
ERROR: "ОШИБКА",
FATAL: "АВАРИЯ"
}
var _msg_array: Array
var _filters: Array
## Преопределение пользовательских значений по умолчанию.
func _enter_tree() -> void:
set_log_enabled(ProjectSettings.get_setting("plugins/logger/log_enabled", true))
set_default_output_enabled(ProjectSettings.get_setting("plugins/logger/default_output", false))
_level = ProjectSettings.get_setting("plugins/logger/level", INFO | DEBUG | WARNING | ERROR | FATAL)
set_file_path(ProjectSettings.get_setting("plugins/logger/file/file_path", "user://uarep-ctl.log"))
set_file_write_enabled(ProjectSettings.get_setting("plugins/logger/file/log_file_write", true))
_format_default_output = ProjectSettings.get_setting("plugins/logger/default_output_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
_format_file = ProjectSettings.get_setting("plugins/logger/file_format", "[{year}-{month}-{day} {hour}:{minute}:{second}][{level}]{text}")
func _exit_tree() -> void:
_close_file()
## Возвращает [param true], если у журнала события есть уровень.
func has_level(level: int) -> bool:
return _names.has(level)
## Установить включенный уровень.
func set_level(level: int, enabled: bool) -> void:
assert(has_level(level), "Invalid level.")
if not has_level(level):
return
if enabled:
_level |= level
else:
_level &= ~level
## Добавить пользовательский уровень журнала. Значение уровня должно быть уникальным и превышать [member MAX].
## Для вызова пользовательского уровня используйте метод - [method message].
func add_level(level: int, nm: String) -> void:
assert(not has_level(level), "Has level.")
assert(level > MAX and not level % 2, "Invalid level.")
assert(name, "Invalid name.")
if not has_level(level) and level > MAX and not level % MAX and not name.is_empty():
_names[level] = nm
set_level(level, true)
## Возвращает имя уровня.
func get_level_name(level: int) -> String:
return _names[level]
## Установить включенный журнал. Отключить ВСЕ сообщения.
func set_log_enabled(enabled: bool) -> void:
_log_enabled = enabled
## Возвращает [param true], если журнал включен.
func is_log_enabled() -> bool:
return _log_enabled
## Установка выхода по умолчанию включена.
func set_default_output_enabled(enabled: bool) -> void:
_default_output_enabled = enabled
## Возвращает [param true], если вывод по умолчанию включен.
func is_default_output_enabled() -> bool:
return _default_output_enabled
## Установить разрешение на запись файла.
func set_file_write_enabled(enabled: bool) -> void:
if _file_write_enabled == enabled:
return
_file_write_enabled = enabled
if enabled:
_open_file()
else:
_close_file()
## Возвращает [param true], если запись в файл разрешена.
func is_file_write_enabled() -> bool:
return _file_write_enabled
## Задать путь к файлу журнала.
func set_file_path(path: String) -> void:
assert(path.is_absolute_path(), "Invalid path.")
if not path.is_absolute_path() or _file_path == path:
return
_file_path = path
if is_file_write_enabled():
_open_file()
## Возвращает путь к файлу журнала.
func get_file_path() -> String:
return _file_path
## Создать информационное сообщение.
func info(text: String) -> void:
message(INFO, text)
## Создать отладочное сообщение. Только для отладочной сборки.
func debug(text: String) -> void:
if OS.is_debug_build():
message(DEBUG, text)
## Создать предупреждающее сообщение.
func warning(text: String) -> void:
message(WARNING, text)
## Создать сообщение об ошибке.
func error(text: String) -> void:
message(ERROR, text)
## Создать сообщение о критической ошибке.
func fatal(text: String) -> void:
message(FATAL, text)
## Форматировать строку для файла.
func format_file(msg: Dictionary) -> String:
return _format_file.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
## Форматировать строку для вывода редактора.
func format_stdout(msg: Dictionary) -> String:
return _format_default_output.format(
{
"year": "%02d" % msg["year"],
"month": "%02d" % msg["month"],
"day": "%02d" % msg["day"],
"hour": "%02d" % msg["hour"],
"minute":"%02d" % msg["minute"],
"second":"%02d" % msg["second"],
"level": _names[msg["level"]],
"text": msg["text"],
}
)
## Создать сообщение с пользовательским уровнем.
func message(level: int, text: String) -> void:
assert(has_level(level), "Has not level.")
assert(text, "Invalid message text.")
if _log_enabled and _level & level:
var msg : Dictionary = Time.get_datetime_dict_from_system()
msg["level"] = level
msg["level_name"] = _names[level]
msg["text"] = " " + text
_msg_array.append(msg)
if (_flt_msg(msg)):
logged.emit(msg)
if _file_write_enabled:
_file.store_line(format_file(msg))
_file.call_deferred('flush')
if _default_output_enabled:
print(format_stdout(msg))
func _open_file() -> void:
if is_instance_valid(_file) and _file.is_open():
_file.close()
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
var err := FileAccess.get_open_error()
if err:
_file = FileAccess.open(get_file_path(), FileAccess.WRITE)
_file.close()
_file = FileAccess.open(get_file_path(), FileAccess.READ_WRITE)
return print_debug(error_string(err))
err = _file.get_error()
if err:
return print_debug(error_string(err))
_file.seek_end(0)
func _close_file() -> void:
if is_instance_valid(_file) and _file.is_open():
_file = null
func save_file(path):
var f = FileAccess.open(path, FileAccess.WRITE)
var err := FileAccess.get_open_error()
if err:
print_debug(error_string(err))
var log_text = _file.get_as_text()
f.store_line(log_text)
f.close()
func set_filtrs(flt_array: Array):
_filters = flt_array
func _flt_msg(msg):
var flt_arr: Array = []
for item in _filters:
if item.button_pressed:
flt_arr.append(item.get_meta('filter'))
if msg['text'].find(item.get_meta('filter')) != -1:
return true
if msg['level_name'] in flt_arr:
return true
return false
func add_msgs():
for msg in _msg_array:
if (_flt_msg(msg)):
logged.emit(msg)