-
-
Save bil-bas/0daff3fd31411488fe1b to your computer and use it in GitHub Desktop.
# GodotLogger by Spooner | |
# ====================== | |
# | |
# logger.gd is a simple logging system. It allows for more formatted logging, | |
# logging levels and logging to a file. | |
# | |
# Installation | |
# ------------ | |
# | |
# Place this file somewhere (for example, 'res://root/logger.gd') | |
# and autoload it (in project settings) to make it into a globally accessible singleton. | |
# | |
# Logger levels | |
# ------------- | |
# | |
# Level.DEBUG - Show all log messages | |
# Level.INFO - Show info(), warning(), error() and critical() log messages [DEFAULT] | |
# Level.WARNING - Show warning(), error() and critical() log messages | |
# Level.ERROR - Show error() and critical() log messages | |
# Level.CRITICAL - Show only critical() log messages | |
# | |
# Time formats | |
# ------------ | |
# | |
# TimeFormat.NONE | |
# TimeFormat.DATETIME [YYYY-MM-DD HH:MM:SS.mmm] | |
# TimeFormat.TIME [HH:MM:SS.mmm] | |
# TimeFormat.ELAPSED [H:MM:SS.mmm] | |
# | |
# Examples | |
# -------- | |
# | |
# Getting a reference to the global logger object with: | |
# var logger = get_node('/root/logger') | |
# | |
# Setting the logger level (default is Level.INFO): | |
# logger.level = logger.Level.DEBUG | |
# | |
# Setting whether to print() message (default is to print): | |
# logger.print_std = false | |
# | |
# Setting showing the current elapsed time (defaults to show TimeFormat.DATETIME): | |
# logger.time_format = TimeFormat.ELAPSED | |
# | |
# Setting time formatter to use your own function (which would normally be called as my_instance.time_formatter()): | |
# logger.time_format_func = funcref(my_instance, "time_formatter") | |
# | |
# Logging to a file (set to 'null' to close the file): | |
# logger.filename = 'user://log.txt' | |
# | |
# Logging messages of various types (will use var2str() to output any non-string being logged): | |
# logger.info("Creating a new fish object") | |
# logger.debug([my_vector3, my_vector2, my_list]) | |
# logger.warning("Tried to take over the moon!") | |
# logger.error("File doesn't exist, so I can't go on") | |
# logger.critical("Divided by an ocelot error! Segfault immanent") | |
# | |
# License: MIT | |
extends Node | |
# Levels of debugging available | |
class Level: | |
const DEBUG = 0 | |
const INFO = 1 # default | |
const WARNING = 2 | |
const ERROR = 3 | |
const CRITICAL = 4 | |
# Built in time formatters | |
class TimeFormat: | |
const NONE = 0 | |
const ELAPSED = 1 | |
const TIME = 2 | |
const DATETIME = 3 # default | |
# Print to stdout? | |
var print_stdout = true setget set_print_stdout, get_print_stdout | |
func get_print_stdout(): | |
return print_stdout | |
func set_print_stdout(value): | |
assert(value in [true, false]) | |
print_stdout = value | |
# Logging level. | |
var level = Level.INFO setget set_level, get_level | |
func get_level(): | |
return level | |
func set_level(value): | |
assert(level in [Level.DEBUG, Level.INFO, Level.WARNING, Level.ERROR, Level.CRITICAL]) | |
level = value | |
# Logging to file. | |
var file = null | |
var filename = null setget set_filename, get_filename | |
func get_filename(): | |
return filename | |
func set_filename(value): | |
if file != null: | |
info("Stopped logging to file: %s" % filename) | |
file.close() | |
if value != null: | |
file = File.new() | |
filename = value | |
file.open(filename, File.WRITE) | |
info("Started logging to file: %s" % filename) | |
else: | |
file = null | |
filename = null | |
# Log timer | |
var time_format_func = funcref(self, "format_time_datetime") setget set_time_format_func | |
func set_time_format_func(value): | |
time_format_func = value | |
var time_format = TimeFormat.DATETIME setget set_time_format | |
func set_time_format(value): | |
if value == TimeFormat.NONE: | |
self.time_format_func = funcref(self, "format_time_none") | |
elif value == TimeFormat.DATETIME: | |
self.time_format_func = funcref(self, "format_time_datetime") | |
elif value == TimeFormat.TIME: | |
self.time_format_func = funcref(self, "format_time_time") | |
elif value == TimeFormat.ELAPSED: | |
self.time_format_func = funcref(self, "format_time_elapsed") | |
else: | |
assert(false) # Bad time format used. | |
# --- Time formatters for use by the logger. | |
func format_time_none(): | |
return "" | |
func format_time_elapsed(): | |
return "[%s] " % _format_elapsed() | |
func format_time_time(): | |
return "[%s] " % _format_time() | |
func format_time_datetime(): | |
return "[%s %s] " % [_format_date(), _format_time()] | |
# --- General time formatting functions | |
func _format_time(): | |
"""Not used directly, but might come in useful""" | |
var time = OS.get_time() | |
# This is not "correct", but gives impression of ms moving on! | |
var ms = OS.get_ticks_msec() % 1000 | |
return "%02d:%02d:%02d.%03d" % [time["hour"], time["minute"], time["second"], ms] | |
func _format_elapsed(time): | |
"""Not used directly, but might come in useful""" | |
var time = OS.get_ticks_msec() | |
var ms = time % 1000 | |
var s = int(time / 1000) % 60 | |
var m = int(time / 60000) % 60 | |
var h = int(time / 3600000) | |
return "%d:%02d:%02d.%03d " % [h, m, s, ms] | |
func _format_date(): | |
"""Not used directly, but might come in useful""" | |
var date = OS.get_date() | |
return "%d-%02d-%02d" % [date["year"], date["month"], date["day"]] | |
# --- Message writing methods | |
func debug(data): | |
"""Debugging message""" | |
if level == Level.DEBUG: | |
_write('DEBUG:', data) | |
func info(data): | |
"""Informational message""" | |
if level <= Level.INFO: | |
_write('INFO:', data) | |
func warning(data): | |
"""Warning message""" | |
if level <= Level.WARNING: | |
_write('WARN:', data) | |
func error(data): | |
"""Error message""" | |
if level <= Level.ERROR: | |
_write('ERROR:', data) | |
func critical(data): | |
"""Critical error message""" | |
_write('CRIT:', data) | |
func _write(type, data): | |
"""Actually write out the message string""" | |
if typeof(data) != TYPE_STRING: | |
data = var2str(data) | |
var message = '%s%5s %s' % [time_format_func.call_func(), type, data] | |
if print_stdout: | |
print(message) | |
if file != null: | |
file.store_line(message) |
Thanks, I've updated the code to use TYPE_STRING (though I am not using godot at all any more, so I am sure it is out of date in other ways too).
Thanks for your logger implementation! I was just reading it over and saw the TODO and thought I'd point it out :)
The rest of the code looks good as of Godot version 3.1. The only syntactic thing that can be updated is class enums -> enums, but it's good as is.
Have a good one!
Thank you for this, it's exactly what I was looking for. Just FYI, I got a couple of errors on Godot 3.2.3.stable.mono. I'm assuming that this works for an earlier version.
res://addons/logger.gd:156 - Parse Error: Variable "time" already defined in the scope (at line 154).
res://addons/logger.gd:95 - Parse Error: The member "filename" already exists in a parent class.
I made a fork and fixed these for myself. I wasn't 100% sure about the first error, but it seems to be working with the edit I made. If you want those changes, feel free to pull them. (I'm haven't used gist.github before, so I'll leave that to you :-) ) Thanks!
Hi!
- The 'var time' in the function tries to recreate the 'time' parameter. You are correct to remove the parameter. If you look, the call to tge function already doesn't pass a parameter. Bad testing on my part, rather than changes to godot...
- Filename/file_name seems to be new.
Honestly, I'm amazed that there isn't a decent logging system built into godot by now!
Use the builtin
TYPE_STRING
in place ofconst STRING_TYPE = typeof("")
astypeof("") == TYPE_STRING
.