Logging
You can enable server side logging in Kittox, which will allow you to see trace data useful for debugging or log your own data from your server side code (such as custom controllers or rules, for example). The logging system is pluggable and multiple loggers can be active at once.
Logging to file
In order to enable logging to file, you write something like this in your Config.yaml:
Log:
# 1 = Minimal, 5 = Debug.
Level: 5
TextFile:
FileName: %APP_PATH%log.txt
# Set this to false to disable this logger without
# deleting its configuration.
IsEnabled: TrueThe code above uses the TextFile logger, which you also need to enable in your application by referencing the unit EF.Logger.TextFile (usually in your UseKitto.pas file). If you fail to include the relevant unit, your logging configuration will be ineffective.
Log levels
The Level parameter controls the verbosity of log output:
| Level | Description |
|---|---|
1 | Minimal — errors and critical events only |
2 | Warnings |
3 | Informational messages |
4 | Verbose |
5 | Debug — full trace output |
WARNING
Level: 5 generates huge amounts of data; use it only while debugging.
Available loggers
| Logger | Unit | Config section | Description |
|---|---|---|---|
| TextFile | EF.Logger.TextFile | TextFile | Writes to a plain text file |
| CodeSite | EF.Logger.CodeSite | CodeSite | Sends log data to the CodeSite viewer (any edition) |
Each logger section must contain at least IsEnabled: True. You can disable a logger without removing its configuration by setting IsEnabled: False. To log to file and CodeSite at the same time, just add both sections under the Log node.
Passwords in the log
Credentials never reach the log: before a message is written, the value assigned to anything whose name contains password is replaced with ***. All the shapes a message can carry one in are covered — Password=value from a query string, "Password":"value" from JSON, PASSWORD_HASH: value from a SQL trace.
The message itself is kept. A line that merely mentions the word — a marker, an error such as Old Password is wrong, the name of a method — is logged unchanged, so authentication stays diagnosable.
Why this matters when you are debugging
Earlier versions dropped the whole line whenever it contained the word. Anything you added to trace an authentication problem disappeared silently, which made the execution look as if it stopped between two consecutive statements. If you are reading logs written by an older build, keep that in mind.
Saving errors
When a save fails, the message shown in the browser is also written to the log, prefixed with Save failed: and carrying the exception class. You therefore have a record of the failure once the dialog has been closed, and something to look at when a user reports the problem second-hand.
It is logged at level 3, so it appears whenever Level is 3 or above — you do not have to turn the log up to 5 to see it.
Unhandled request exceptions
An exception that escapes every handler while a request is being served is logged at level 3 as Unhandled <ExceptionClass> on <path>: <message>, next to the modal dialog the browser gets.
Why this matters
Until 4.0.15 the dialog was the only trace: nothing reached the log, not even at Level: 5. Under the desktop host you at least saw the dialog; a Windows service, an ISAPI module or an Apache module left no record at all. Any diagnosis that started from "the log shows nothing between these two statements" was reading a log that could not show it.
Why a login was refused
Every refusal that is not simply a wrong password says so in the log, so a support call does not turn into guesswork. Nothing here prints a credential — see Passwords in the log.
| Message | Level | Meaning |
|---|---|---|
Authentication refused for user X: empty password. | 4 | A blank password was submitted. The password box is required on the page, so this normally means a scripted call |
Authentication refused for user X: the user record carries no password … | 3 | The account exists but its password column is empty or NULL: it cannot be logged into. Send a temporary password with the reset flow |
Authentication refused: user X is not in the user list file. | 4 | Auth: TextFile, unknown or #-disabled user |
User X has no password in the user list file … | 3 | Auth: TextFile, the line carries nothing after the = — a broken user list |
PIN login refused: the user record carries no SECRET_CODE … | 3 | LoginType: PIN without a per-user secret, see PIN login |
Passepartout ignored: IsPassepartoutEnabled is True but PassepartoutPassword is empty … | 3 | Misconfiguration: with a value it is a master password, without one it would accept a blank password for everybody |
Request blocked: the user must first complete ChangePassword. Endpoint: … | 4 | The imposed-step gate refused an endpoint outside the required view |
Unauthenticated request to protected endpoint: … | 4 | The authentication gate answered 404 |
The rows at level 3 are the ones that mean somebody has to fix something — a user record or a configuration — rather than somebody typed the wrong password.
Check your Level before concluding that nothing happened
A message is written only when the configured Level is greater than or equal to the message's own level, and the framework default is 1. The shipped examples set detailed (4) or 5, so in practice these lines are there; an application that leaves Log/Level at the default sees none of them. Set it to at least 3 before diagnosing a login problem.
Two startup refusals are logged at level 3 and also stop the application, so they are hard to miss: an Auth: class id that nobody registered, and Auth: DBServer whose Connection block does not read the typed credentials. See Startup checks.
Logging from your own code
If you need to log information from your server-side code (custom controllers, rules, tools), include the unit EF.Logger and call the methods of TEFLogger.Instance. Kittox takes care of thread synchronization.
Remember to enable at least one logger (e.g. reference EF.Logger.TextFile in your UseKitto.pas), otherwise the logged strings have nowhere to go.
Custom loggers
You can add your own custom loggers; just have a look at the predefined ones and use them as examples and starting points.
Basically you need to create a log endpoint: create a new class inherited from TEFLogEndpoint and override the DoLog method (don't forget the initialization and finalization sections in your unit, which will make your new class available to the system).
