# tecs.log Named, leveled logging. Create one named logger per subsystem. Logger names form the stable filtering surface: ```teal local logger = tecs.log.get("game.combat") logger:info("wave %d spawned %d enemies", 3, 12) logger:setLevel(tecs.log.DEBUG) ``` Messages go to the platform log destination, including logcat on Android and Console on Apple platforms. [`Logger`](/modules/log/#tecs.log.Logger) methods check priority before formatting. Pass raw arguments rather than formatting first. Guard work that exists only to build a message: ```teal local snapshot = {frame = 12, player = "Alice"} if logger:enabled(tecs.log.DEBUG) then logger:debug("snapshot: %s", tecs.data.encodeJSON(snapshot)) end ``` ## JSON Lines output Add a queryable file beside the platform destination: ```teal local path = tecs.io.files.writablePath("game.jsonl") if not tecs.log.openFile(path) then logger:error("could not open %s", path) end ``` With file output open, the earlier `info` call writes a line like: ```json {"time":12.345,"level":"INFO","logger":"game.combat","message":"wave 3 spawned 12 enemies"} ``` Each line records elapsed platform time, level, logger, and message. `closeFile` stops file output and leaves the platform destination active. ## Module contents ### Types | Type | Kind | Description | | --- | --- | --- | | [`Logger`](/modules/log/#tecs.log.Logger) | record | Represents a named log category. | ### Functions | Function | Kind | Description | | --- | --- | --- | | [`categoryName`](/modules/log/#tecs.log.categoryName) | Static | Returns the name registered for a category. | | [`closeFile`](/modules/log/#tecs.log.closeFile) | Static | Stops writing to the file. | | [`filePath`](/modules/log/#tecs.log.filePath) | Static | Returns the open log file path, or nil when no file is open. | | [`get`](/modules/log/#tecs.log.get) | Static | Returns the logger for name, creating it the first time. | | [`loggers`](/modules/log/#tecs.log.loggers) | Static | Returns every registered logger name. | | [`openFile`](/modules/log/#tecs.log.openFile) | Static | Writes every accepted line to path as JSON Lines and truncates the file. | | [`setLevel`](/modules/log/#tecs.log.setLevel) | Static | Sets the minimum priority for every category. | ### Values | Value | Type | Description | | --- | --- | --- | | [`CRITICAL`](/modules/log/#tecs.log.CRITICAL) | `integer` | Read-only. Contains the critical priority. | | [`DEBUG`](/modules/log/#tecs.log.DEBUG) | `integer` | Read-only. Contains the debug priority. | | [`ERROR`](/modules/log/#tecs.log.ERROR) | `integer` | Read-only. Contains the error priority. | | [`INFO`](/modules/log/#tecs.log.INFO) | `integer` | Read-only. Contains the informational priority. | | [`TRACE`](/modules/log/#tecs.log.TRACE) | `integer` | Read-only. Contains the trace priority. | | [`VERBOSE`](/modules/log/#tecs.log.VERBOSE) | `integer` | Read-only. Contains the verbose priority. | | [`WARN`](/modules/log/#tecs.log.WARN) | `integer` | Read-only. Contains the warning priority. | ## Types ### tecs.log.Logger record Represents a named log category. ```teal record tecs.log.Logger name: string category: integer critical: function(self, message: string, ...: any) debug: function(self, message: string, ...: any) enabled: function(self, priority: integer): boolean error: function(self, message: string, ...: any) info: function(self, message: string, ...: any) level: function(self): integer setLevel: function(self, priority: integer) trace: function(self, message: string, ...: any) verbose: function(self, message: string, ...: any) warn: function(self, message: string, ...: any) end ``` #### tecs.log.Logger.name field Read-only. Contains the registered logger name written to the platform log. ```teal tecs.log.Logger.name: string ``` #### tecs.log.Logger.category field Engine-owned. Contains the category allocated during this run. The number may change across runs, so game code should filter by `name`. ```teal tecs.log.Logger.category: integer ``` #### tecs.log.Logger:critical Instance Logs at `CRITICAL`. See `trace` for the formatting rules. ```teal function tecs.log.Logger.critical(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. #### tecs.log.Logger:debug Instance Logs at `DEBUG`. See `trace` for the formatting rules. ```teal function tecs.log.Logger.debug(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. #### tecs.log.Logger:enabled Instance Reports whether the logger would emit a message at `priority`. Call this directly to guard work that only exists to build a log message, such as serializing a table. The level methods already guard themselves. ```teal function tecs.log.Logger.enabled(self, priority: integer): boolean ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `priority` | `integer` | | ##### Returns | Type | Description | | --- | --- | | `boolean` | | #### tecs.log.Logger:error Instance Logs at `ERROR`. See `trace` for the formatting rules. Logging is all this does; it neither raises nor returns anything. ```teal function tecs.log.Logger.error(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. #### tecs.log.Logger:info Instance Logs at `INFO`. See `trace` for the formatting rules. ```teal function tecs.log.Logger.info(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. #### tecs.log.Logger:level Instance Returns the minimum priority this logger emits. ```teal function tecs.log.Logger.level(self): integer ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | ##### Returns | Type | Description | | --- | --- | | `integer` | Returns the platform priority until something sets one. | #### tecs.log.Logger:setLevel Instance Sets the minimum priority this logger emits. ```teal function tecs.log.Logger.setLevel(self, priority: integer) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `priority` | `integer` | One of the constants on `log`. The logger emits messages at this priority and drops anything lower. | ##### Returns None. #### tecs.log.Logger:trace Instance Logs at `TRACE` and applies `string.format` specifiers only when emitted. The method checks the level before formatting, so a filtered call costs a load and a compare and the arguments are never touched. Building those arguments is still the caller's cost; guard that with `enabled`. ```teal function tecs.log.Logger.trace(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | A `string.format` template when arguments follow, and a literal otherwise, so a `%` in a plain message is safe. | | `...` | `any` | Formatted into `message`. A wrong count or a wrong specifier raises from `string.format`, and only on the calls that are actually emitted. | ##### Returns None. #### tecs.log.Logger:verbose Instance Logs at `VERBOSE`. See `trace` for the formatting rules. ```teal function tecs.log.Logger.verbose(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. #### tecs.log.Logger:warn Instance Logs at `WARN`. See `trace` for the formatting rules. ```teal function tecs.log.Logger.warn(self, message: string, ...: any) ``` ##### Arguments | Name | Type | Description | | --- | --- | --- | | `self` | `Logger` | | | `message` | `string` | | | `...` | `any` | | ##### Returns None. ## Functions ### tecs.log.categoryName Static Returns the name registered for a category. ```teal function tecs.log.categoryName(category: integer): string ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `category` | `integer` | | #### Returns | Type | Description | | --- | --- | | `string` | Never returns nil. An unregistered category comes back as `"category:"` rather than as an error. | ### tecs.log.closeFile Static Stops writing to the file. Does nothing when no file is open, so it is safe to call on a shutdown path that does not know whether one was ever asked for. ```teal function tecs.log.closeFile() ``` #### Arguments None. #### Returns None. ### tecs.log.filePath Static Returns the open log file path, or nil when no file is open. ```teal function tecs.log.filePath(): string ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `string` | The path as it was given, not resolved against the working directory, so a relative one is only meaningful to a reader in the same place the game ran. | ### tecs.log.get Static Returns the logger for `name`, creating it the first time. Names are the unit of filtering, so they are what a subsystem should use: `tecs.gfx`, `tecs.debug.events`. Each maps to one SDL category, which is what `SDL_SetLogPriority` takes. ```teal function tecs.log.get(name: string): log.Logger ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `name` | `string` | Compared byte for byte, so two spellings are two categories with two levels. | #### Returns | Type | Description | | --- | --- | | [`log.Logger`](/modules/log/#tecs.log.Logger) | Returns the same object on every call for a name. A new logger starts at the platform's default priority. | ### tecs.log.loggers Static Returns every registered logger name. ```teal function tecs.log.loggers(): {string} ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `{string}` | A fresh array each call, sorted, holding only names something has already asked `get` for. A subsystem that has not run yet is absent. | ### tecs.log.openFile Static Writes every accepted line to `path` as JSON Lines and truncates the file. The platform destination keeps receiving the human-readable form, so The platform still sends its human-readable stream to `tail -f`, logcat, or Console.app. The file makes a log queryable after the fact: seek to an offset, read to the end. Calling this while another file is open closes the previous file and moves subsequent lines to `path`. ```teal function tecs.log.openFile(path: string): boolean ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `path` | `string` | Truncated if it exists, created if it does not, including when it is the file already open, so this is not a way to check one is. Directories are not made. | #### Returns | Type | Description | | --- | --- | | `boolean` | False when the file cannot open. In that case nothing else changes and a file already open keeps receiving lines. | ### tecs.log.setLevel Static Sets the minimum priority for every category. ```teal function tecs.log.setLevel(priority: integer) ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `priority` | `integer` | Overrides every per-logger level already set, so this is a reset and not a floor. | #### Returns None. ## Values ### tecs.log.CRITICAL variable Read-only. Contains the critical priority. ```teal tecs.log.CRITICAL: integer ``` ### tecs.log.DEBUG variable Read-only. Contains the debug priority. ```teal tecs.log.DEBUG: integer ``` ### tecs.log.ERROR variable Read-only. Contains the error priority. ```teal tecs.log.ERROR: integer ``` ### tecs.log.INFO variable Read-only. Contains the informational priority. ```teal tecs.log.INFO: integer ``` ### tecs.log.TRACE variable Read-only. Contains the trace priority. ```teal tecs.log.TRACE: integer ``` ### tecs.log.VERBOSE variable Read-only. Contains the verbose priority. ```teal tecs.log.VERBOSE: integer ``` ### tecs.log.WARN variable Read-only. Contains the warning priority. ```teal tecs.log.WARN: integer ```