Squid 🦑 saveable logging and errors and crashes handling

Hello!

I wanted to present to you:

:squid: SQUID!

Squid is a standalone injectable system for saveable logging of user logs, errors and crashes for Defold. It is inspired by Log and Err by @Pkeod and Defold Log by @Insality and tries to be compatible with both. It’s MIT and even though it’s used by me in most of my projects, I refactored it recently, gave it an “inky” name and released it as open source in hope it might get stretched further. :wink:

Squid 1.1:

Check out README for quick usage reference and documentation.

:link: Dependency

You can use it in your Defold project by adding a link to it in Dependencies. Current is 1.1:`

https://github.com/paweljarosz/squid/archive/refs/tags/1.1.zip

:rainbow: Colored logs

Squid utilises colored console output in Defold:

console

Each log entry is structured like this:

LOG_LEVEL: [tag]:  [timestamp] code/address:line: message
 Data: --if provided

It also pretty prints tables if provided as data parameter. Tables are also saved in log file up to a defined length and depth.

:floppy_disk: Auto-saving, auto error and crash handling

If configured to do so, it saves logs also to a log file in user save file directory automatically or explicitly. It also automatically saves Errors and Crashes. It also can automatically remove log files older than X days based on the timestamp in the file name.

:computer: Cross-platform

Squid is designed to be cross-platform, but tested mostly on Windows, Linux and HTML5. It should work on Mac, iOS and Android too, but wasn’t tested there yet. If there are any system specific issues, please report.

:gear: Configuration

Squid is configurable - you can use your own squid_config.lua file following the convention or configure it from game.project - check out documentation for more details on it.

:bookmark: Quick Reference

Squid offers a very simple static API:

-- If squid is initialized with init() it will handle errors and crashes automatically too
squid.init()

-- Define new tag strings and enable/disable them:
squid.set_allowed("main", true)

-- Use static logging functions with or without optional non-string data and/or tag string:
squid.trace("My Trace Message")								-- no data and no tag, uses default tag ("none")
squid.debug("My Debug Message  ", { my_test_data = 1 })		-- with optional non-string data (default tag used)
squid.info( "My Info Message   ", "main")					-- with string data only (used as tag too)
squid.warn( "My Warning Message", "Hello World", "main")	-- with string data and tag ("main" tag is used as tag here)
squid.error("My Error Message  ", vmath.vector3(1), "main")	-- with non-string data and tag
-- Or generic logging function with own logging level:
squid.log("My Other Message", squid.DEBUG)

-- Explicitly save any unsaved logs and check for crashes in final():
--(logs are saved automatically too, in batch every X logs as configured in game.project)
squid.final()

Instantiating

Squid can be conveniently used as internal logger module for various other Defold modules, e.g.:

-- Create new instance with optional tag assigned (`none` by default) initially allowed or not
self.player_logger = squid.new("player", true)

-- Use all API function with the created instance
self.player_logger:info("Logger with 'player' tag")
17 Likes

Nice! Thanks for the sharing, looks amazing!

First time name Squid make me thinking this is something UI relative :smiley:

2 Likes

I like naming modules by animals, because those are very fast to memorize imho :smiley: And squid is the one with ink, so can “write” :sweat_smile: Nevermind, octopus is reserved for the next module I’m working on xD

And this has saving inside - I thought that it might be useful for Defold Log as well, but thought that perhaps you want to somehow bind it with own Saver, because I was thinking about it as well :smiley:

Example log file:

squid_log_file_2024-12-29_19_43.txt (1.0 KB)

At the beginning it stores info about engine, user system and device and then appends logs. It also stores crashes information if there occur any.

2 Likes

Yea, this logs looks handy and also a good example of “how to implement that”

I had plans about external log storage, but still didn’t meet the any use cases for my projects before. Even it will be implemented - it will be the same inner way as you did, to escape additional dependencies

About dependencies:

From first look the additional dependency of Immutable seems unnecessary. It’s used only for inner settings, which only you as developer have the access. It can bring more friction to start using or try this module.

1 Like

You’re right about it, it’s mainly for my convenience as of now, as I use it in my projects like this. I also don’t want users to copy/paste the config module, you know, like you would need if you want to modify the custom configuration, instead of just adding dependency, but I want to allow to modify the configuration. It was my first time, when I tried to bound configuration with game.project. Maybe I’ll figure out some better ways for such config too :smiley:

But on the other hand - a module like logger is injected into other modules for loggin purposes and it’s pretty natural (like .set_logger()) . So I was thinking of making the same with saving purposes - any module that wants to save something to files could use an easy injected saver module :smiley:

For Immutable itself - I can’t figure out a way of checking if such a module is present in project - because if so, it could be used silently without user’s interaction. Require will always raise error if there is no file under given path and it doesn’t matter if it’s inside pcall.

My use case was client’s work and users weird issues, that I couldn’t reproduce, hence collecting full logs from them helped a bit :smiley: I imagine a great saver module that can save to local file, but also have an option to store it somewhere online too, it would be a blast!

1 Like

This is awesome, but I kinda managed to break it. :sweat_smile:

I have squid.init() and squid.final() set in my main.script, which is the first thing that loads when I start the game.

WARNING:CRASH: Crashdump version or format does not match: Crash version: 1330007625.538976314  Tool Version: 2.51144
INFO:   [crash]: [17:36:41] squid/squid.lua:121: CRASH: No crash dump found
DEBUG:  [none]: [17:36:41] main/main.script:71: final()

I see this in the defold editor console when I close the game (ESC) too quickly. When this happens squid writes its things into the newest existing log file, instead of creating a new one.
This could be a non-issue, but if the engine crashes too early then the log files might get messy.

I also noticed that when the log directory is empty, the first log, instead of the system info text wall, it has a warning: No files found in directory.

Lastly, when I close the game via ESC (when it has properly loaded etc.), squid seems to think there was a crash. The “Escape Quits Game” editor preference is enabled, but I presume it quits the game gracefully instead of nuking it. :stuck_out_tongue:

INFO:   [crash]: [17:44:38] squid/squid.lua:121: CRASH: No crash dump found
1 Like

Thank you for the first reports! :smiley: I’ll look into those :wink:

If this is only what you see in log:

INFO:   [crash]: [17:44:38] squid/squid.lua:121: CRASH: No crash dump found

this is normal, it’s an INFO trace, it means there were no crashes, perhaps I should change the message, but somehow I find it a useful information, so I don’t want to remove it :smiley:

I also bumped into this, in different circumstances:

But I don’t know what it is and why there is such a warning.

Yeah, this is an issue :confused:

EDIT:
@NaakkaDev maybe you are opening and closing game in the very same minute twice or more?

If so it will be written in the same log file, because squid appends logs to a file with a name with a time stamp in format YYYY-MM-DD_hh_mm, so if you call squid.init() in the very same minute, e.g. 18:51, all logs will be written into one file. I think narrowing it down to a minute is enough for usual use cases, so I won’t be adding seconds to the time stamp :sweat_smile:

If you configure is_adding_timestamp in game.project to 0 it will be resulting in all logs being written to a single file always, but also bear in mind in such case, squid will not automatically remove old logs (because it is based on those time stamps in file names)

Yeah, I had a feeling that was the case, but the CRASH: part is tripping me up and making me think it is trying to report a crash but didn’t find the dump file. :smiley:

Could perhaps append the message with (OK) or such.

Yes that seems to be the case. I didn’t notice the filename had no seconds.

1 Like

@NaakkaDev such a message could be ok? [crash] is a tag here (for easier searching):

image

Yes. That one is much clearer. I do wonder if the “OK:” part is needed now, since “No crashes,” already says it all.

2 Likes

Updated to 1.1 :smiley:

  • Removed Immutable dependency.
  • Changed final message about no crashes to more friendly:
    (INFO: [squid]: [14:47:48] squid/squid.lua:122: No crashes, no dump found.)
  • Added functions to get and set new squid configuration.
  • Improved documentation.
3 Likes

Guess what happens when I set the app_catalog to PROJECTNAME/logs and the PROJECTNAME directory does not exist. :laughing:

Spoiler
ERROR:SCRIPT: squid/squid_impl.lua:48: Unable to locate application support path for "PROJECTNAME/logs": (-2)

Since Squid can create the log root directory, in my opinion it should also be able to create any missing directory regardless of the depth.

Btw. I did try to find the code that creates the log directory but seems I’m blind. :sunglasses:

1 Like

Should be in save_logs in squid_impl :grin: I never considered nested dirs :sweat_smile:

1 Like