Logging is an essential part of software development. Whether you’re debugging issues during development or monitoring application performance in production, a robust logging solution is critical. Xojo provides two excellent built-in methods for logging: System.DebugLog
and System.Log
. These methods are simple to use, highly effective, and suitable for most types of projects.
However, as your applications grow in complexity, you may encounter scenarios where more advanced logging features are required. Developers working with other programming languages often turn to frameworks like Log4J or Log4Net for flexible and powerful logging solutions. Inspired by these tools, let’s create Log4Xojo, a powerful and flexible logging utility designed specifically for the Xojo language.
With Log4Xojo, you can:
- Save logs to files for long-term storage and analysis.
- Automatically rotate log files when they grow too large.
- Log messages to multiple destinations (e.g., Debugger, system logs, and files) simultaneously.
- Filter log messages by severity to reduce noise in production environments.
Log4Xojo complements Xojo’s built-in logging methods by extending their capabilities and making it easier to manage logs in larger or more complex applications. Whether you’re a seasoned Xojo developer or coming from other tools like Log4J or Log4Net, Log4Xojo provides a familiar, robust solution for advanced logging needs.
Xojo’s Built-In Logging Methods
Xojo provides two built-in methods for logging: System.DebugLog
and System.Log
System.DebugLog
– check here for the documentationSystem.Log
– check here for the documentation
These methods are simple to use and provide an effective way to track issues during development or monitor application behavior in production environments.
1. System.DebugLog
The System.DebugLog
method outputs messages to Xojo’s Message Log in the Messages Panel during development. This allows developers to generate their own logging messages to assist with debugging and testing. Additionally, System.DebugLog
writes to the system log, making it viewable in tools such as:
- Console on macOS
- DebugView on Windows
- StdErr on Linux
Example:
System.DebugLog("This is a debug message.")
Key Features:
- Outputs directly to the Messages Panel in the Xojo IDE during debugging, providing real-time insight into your application.
- Also logs to the system log, which is accessible outside of the Xojo IDE for further analysis.
Limitations:
- While
System.DebugLog
is versatile, its output cannot be directed to custom log files within your application without additional handling.
2. System.Log
The System.Log
method writes log messages to the system’s logging mechanism. This is particularly useful for monitoring application behavior in production environments, as it integrates seamlessly with system-level logging tools.
How It Works:
- On macOS and Linux: Messages are written to the system logger, typically located at
/var/log
. These logs can be accessed using tools likeConsole
(macOS) orSyslog
(Linux). - On Windows: Messages are sent to the Event Logger, which can be viewed using the Windows Event Viewer.
Example:
System.Log(System.LogLevelNotice, "This is a notice message.")
Key Features:
- Integrates with the system’s logging infrastructure, allowing log messages to persist outside of the Xojo IDE.
- Useful for production environments, where logs can be reviewed using system tools like Console (macOS), Event Viewer (Windows), or Syslog (Linux).
Limitations:
- Messages may not be portable across platforms due to system-specific log storage locations.
- Not supported in mobile projects.
- On macOS, messages logged with the following levels do not appear in the system log due to a macOS limitation:
LogLevelDebug
LogLevelInformation
LogLevelSuccess
Why Use the Built-In Methods?
The built-in logging methods are excellent for most projects. They are simple, efficient, and integrate seamlessly with Xojo. If your project doesn’t require file logging, log rotation, or multi-destination support, these methods might be all you need.
Introducing our custom class: Log4Xojo
While Xojo’s built-in methods are excellent for many projects, some applications demand more advanced logging features. Log4Xojo is designed to complement Xojo’s built-in methods and extend their functionality to handle more complex requirements.
What is Log4Xojo?
Log4Xojo is a custom-built logging class that expands Xojo’s logging capabilities with features like:
- File Logging: Save logs to files for later analysis.
- Log Rotation: Automatically manage log file sizes and create backups.
- Multi-Destination Logging: Log messages to the Debugger, system logs, and files simultaneously.
- Log Level Filtering: Control which messages are logged based on severity (e.g., only log warnings and errors in production).
- Named Logs: Assign each log instance a unique name, making it easier to organize and differentiate log files.
- Thread-Safe Logging: Use a background thread to process logs efficiently without blocking your application.
These features make Log4Xojo an excellent choice for larger or more complex projects, as well as applications with strict monitoring or reporting requirements.
When Should You Use Log4Xojo?
Consider using Log4Xojo if your project requires:
- Persistent logging to files for long-term storage or analysis.
- Automatic log management to prevent files from growing too large.
- Logging to multiple destinations simultaneously (e.g., Debugger, system logs, and files).
- Filtering logs by severity to reduce noise in production environments.
- Thread-safe, asynchronous logging for improved performance in multi-threaded applications. By processing logs asynchronously in a background thread, Log4Xojo ensures that your application’s performance remains smooth, even when handling a large volume of log messages.
Log4Xojo is not meant to replace the built-in logging methods but to complement them. It builds on the foundation of System.DebugLog
and System.Log
by adding advanced features for developers who need more flexibility and control.
How to Use Log4Xojo
Using Log4Xojo is simple! Follow these steps to get started.
Step 1: Add Log4Xojo to Your Project
- Download the
Log4Xojo
class file from GitHub https://github.com/xojo/log4xojo or click here to download the Xojo example project. - Drag the
Log4Xojo
class file into your Xojo project.
Step 2: Create a Log Instance
Create an instance of the Log4Xojo class. Each log requires a name, which is used to identify it and organize log files.
Var l4x As New Log4Xojo("AppLog")
Step 3: Configure the Log
Set up the logging destinations, file path, log level, and other settings. For example:
// Set destinations (Debugger and File)
l4x.SetLogDestinations(Log4Xojo.LogDestination.DebugLog, Log4Xojo.LogDestination.FileLog)
// Set the base folder for the log file
l4x.SetLogFilePath(SpecialFolder.Desktop)
// Set the log level to Warning and above
l4x.SetLogLevel(Log4Xojo.LogLevel.Warning)
// Configure log rotation
l4x.SetMaxBackupFiles(5)
l4x.SetMaxLogFileSize(1024)
Step 4: Log Messages
Log messages with varying severity levels:
l4x.Log("Application started.", Log4Xojo.LogLevel.Info, CurrentMethodName)
l4x.Log("This is a debug message.", Log4Xojo.LogLevel.Debug, CurrentMethodName)
l4x.Log("Warning: Low disk space.", Log4Xojo.LogLevel.Warning, CurrentMethodName)
l4x.Log("Error: File not found.", Log4Xojo.LogLevel.Error, CurrentMethodName)
The optional location parameter (CurrentMethodName
above) allows you to include the context of the log message, such as the method or class where it originated. This makes it easier to pinpoint the source of issues when reviewing logs.
Step 5: Stop Logging
When your application shuts down, make sure to stop logging to ensure all queued messages are processed:
l4x.StopLogging()
Use Case Scenarios for Log4Xojo
To better understand how Log4Xojo
can enhance your Xojo applications, here are some practical use cases:
1. Monitoring Application Performance in Production
In a production environment, it’s essential to keep track of your application’s behavior without impacting performance. With Log4Xojo
, you can:
- Log important application events (e.g., user activity, API calls).
- Use file logging to store these logs persistently for later analysis.
- Filter logs by severity to avoid unnecessary noise (e.g., only warnings, errors, and critical issues).
Example:
Var l4x As New Log4Xojo("ProductionLog")
l4x.SetLogDestinations(Log4Xojo.LogDestination.FileLog)
l4x.SetLogLevel(Log4Xojo.LogLevel.Warning)
// Log application events
l4x.Log("User logged in", Log4Xojo.LogLevel.Info) // Ignored (below warning level)
l4x.Log("Database connection failed", Log4Xojo.LogLevel.Error) // Logged
l4x.Log("Critical: Payment gateway unreachable", Log4Xojo.LogLevel.Critical) // Logged
Outcome: Only warnings, errors, and critical messages are logged to a file for postmortem analysis without overwhelming the log with lower-priority messages.
2. Creating a Diagnostic Tool for End Users
When troubleshooting issues reported by end users, having detailed logs can be invaluable. With Log4Xojo
, you can:
- Log messages to a file on the user’s machine.
- Include optional location tags to pinpoint where issues occur in your code.
- Use log rotation to prevent log files from consuming too much disk space.
Example:
Var l4x As New Log4Xojo("UserDiagnostics")
l4x.SetLogDestinations(Log4Xojo.LogDestination.FileLog)
l4x.SetLogFilePath(SpecialFolder.Documents)
l4x.SetMaxLogFileSize(1 * 1024 * 1024) // 1 MB
l4x.SetMaxBackupFiles(3)
// Log diagnostic information
l4x.Log("Application launched", Log4Xojo.LogLevel.Info, CurrentMethodName)
l4x.Log("Error: File not found", Log4Xojo.LogLevel.Error, "FileManager.LoadFile")
l4x.Log("User clicked 'Submit'", Log4Xojo.LogLevel.Debug, "MainWindow.HandleSubmit")
Outcome: You can ask users to send the log files stored in their Documents folder for review, helping you quickly diagnose and fix issues.
3. Tracking User Activity in Enterprise Applications
In enterprise applications, logging user activity is often a requirement for auditing or compliance purposes. With Log4Xojo
, you can:
- Use multi-destination logging to send activity logs to both the system logs and a central log file.
- Include relevant context for each log entry (e.g., user ID, method).
Example:
Var l4x As New Log4Xojo("AuditLog")
l4x.SetLogDestinations(Log4Xojo.LogDestination.SystemLog, Log4Xojo.LogDestination.FileLog)
l4x.SetLogFilePath(SpecialFolder.Documents)
// Log user activity
Var userID As String = "User123"
l4x.Log(userID + " logged in", Log4Xojo.LogLevel.Info, "AuthManager.Login")
l4x.Log(userID + " updated profile", Log4Xojo.LogLevel.Info, "ProfileManager.UpdateProfile")
l4x.Log(userID + " attempted unauthorized access", Log4Xojo.LogLevel.Warning, "SecurityManager.CheckPermissions")
Outcome: Both system logs and a persistent file log are updated with the user’s activities, ensuring compliance and easy traceability.
4. Handling Errors and Crashes Gracefully
When an application crashes, logs are often the only way to understand what went wrong. Log4Xojo
can:
- Capture error and critical logs leading up to a crash.
- Rotate logs to avoid losing older, relevant logs.
- Save logs to a file for recovery after a crash.
Example:
Var l4x As New Log4Xojo("CrashLogs")
l4x.SetLogDestinations(Log4Xojo.LogDestination.FileLog)
l4x.SetMaxBackupFiles(5)
l4x.SetMaxLogFileSize(1 * 1024 * 1024) // 1 MB
Try
// Simulate application logic
Raise New RuntimeException("Simulated crash")
Catch e As RuntimeException
l4x.Log("Critical error: " + e.Message, Log4Xojo.LogLevel.Critical, CurrentMethodName)
End Try
Outcome: The log files can be used to investigate the cause of the crash.
The Structure of the Log4Xojo Class
Log4Xojo includes several constants, enums, properties, and methods that provide its advanced functionality. Let’s break them down:
Constants
MaxQueueSize
- Defines the maximum number of log messages that can be stored in the logging queue before processing.
- Default:
10,000
.
Enums
LogDestination
- Specifies where log messages should be sent:
DebugLog
: Logs to the Xojo Debugger usingSystem.DebugLog
.SystemLog
: Logs to the system’s logging framework usingSystem.Log
.FileLog
: Logs to a file.All
: Logs to all destinations.
- Specifies where log messages should be sent:
LogLevel
- Defines the severity levels for log messages:
Debug
: Detailed debugging information.Info
: General informational messages.Warning
: Potential issues that don’t interrupt program flow.Error
: Errors requiring attention.Critical
: Critical issues that may cause application failure.
- Defines the severity levels for log messages:
Properties
mLogName
- The name of the log. This is required during initialization and is used to identify logs and organize log files.
mCurrentLogLevel
- The minimum severity level of messages to log. Messages below this level are ignored.
mLogDestinations
- An array of
LogDestination
values specifying where logs should be sent.
- An array of
mLogFilePath
- The file path for logs. This is automatically generated based on the log name and current date but can also be customized.
mMaxBackupFiles
- The maximum number of backup log files to retain. Default:
10
.
- The maximum number of backup log files to retain. Default:
mMaxLogFileSize
- The maximum size of the log file in bytes before rotation occurs. Default:
1 MB
.
- The maximum size of the log file in bytes before rotation occurs. Default:
mLogQueue
- A queue for temporarily storing log messages before they are processed.
mLogQueueMutex
- A mutex used to ensure thread safety when accessing the log queue. Check mutex documentation here.
mLogThread
- A background thread that processes log messages. Check thread documentation here.
mRunning
- A flag indicating whether the logging thread is running.
Methods
The Log4Xojo
class provides several methods to configure logging behavior and manage log messages. Let’s go through each one in detail:
1. Constructor(Name As String)
Purpose: The constructor initializes a new instance of the Log4Xojo
class with a required log name. This name is used to identify the log instance and organize log files.
Usage:
Var l4x As New Log4Xojo("AppLog")
Details:
- The
Name
parameter is required and cannot be empty. If you pass an empty string, the class will raise anInvalidArgumentException
. - The log name is used to generate log file names (e.g.,
AppLog_2024-11-01.txt
).
2. Log(message As String, level As LogLevel, Optional location As String = “”)
Purpose: Logs a message with a specified log level and an optional location string.
Usage:
l4x.Log("This is an informational message.", Log4Xojo.LogLevel.Info)
l4x.Log("Warning: Low disk space.", Log4Xojo.LogLevel.Warning, CurrentMethodName)
Details:
message
: The log message you want to record.level
: The severity level of the message. This determines whether the message will be logged, based on the current log level (mCurrentLogLevel
).location
(optional): A string to provide context for the log message, such as the method or class where it originated. This is especially useful for tracing the source of issues. UsingCurrentMethodName
is more than enough, unless you need to specify something else.- If the log level is below the configured
mCurrentLogLevel
, the message is ignored.
Example Output:
[2023-10-01 12:34:56] [INFO] This is an informational message.
[2023-10-01 12:35:00] [WARNING] [Code-Location] Warning: Low disk space.
3. SetLogDestinations(ParamArray destinations As LogDestination)
Purpose: Specifies where log messages should be sent (e.g., Debugger, system logs, files, or all destinations).
Usage:
l4x.SetLogDestinations(Log4Xojo.LogDestination.DebugLog, Log4Xojo.LogDestination.FileLog)
Details:
- Accepts one or more
LogDestination
values as parameters. - Supported destinations:
LogDestination.DebugLog
: Logs to the Xojo Debugger usingSystem.DebugLog
.LogDestination.SystemLog
: Logs to the system’s logging framework usingSystem.Log
.LogDestination.FileLog
: Logs to a file.LogDestination.All
: Logs to all destinations simultaneously.
- You can mix and match destinations to suit your needs.
Example:
- To log messages only to a file:
l4x.SetLogDestinations(Log4Xojo.LogDestination.FileLog)
- To log messages to both the Debugger and system logs:
l4x.SetLogDestinations(Log4Xojo.LogDestination.DebugLog, Log4Xojo.LogDestination.SystemLog)
4. SetLogFilePath(baseLocation As FolderItem)
Purpose: Sets the folder where log files should be stored.
Usage:
Var folder As FolderItem = SpecialFolder.Desktop
l4x.SetLogFilePath(folder)
Details:
baseLocation
: AFolderItem
representing the folder where log files will be saved.- The folder must exist and be writable. If the folder is invalid, the method will raise an
InvalidArgumentException
. - The log file name is automatically generated based on the log name and current date (e.g.,
AppLog_2024-11-01.txt
).
Example:
- To store logs in the Documents folder:
l4x.SetLogFilePath(SpecialFolder.Documents)
5. SetLogLevel(level As LogLevel)
Purpose: Sets the minimum severity level for messages to be logged.
Usage:
l4x.SetLogLevel(Log4Xojo.LogLevel.Warning)
Details:
level
: ALogLevel
value that determines which log messages will be recorded.- Messages with a severity lower than the set level are ignored.
Supported Log Levels:
Log4Xojo.LogLevel.Debug
: Detailed debugging information.Log4Xojo.LogLevel.Info
: General informational messages.Log4Xojo.LogLevel.Warning
: Potential issues that don’t interrupt program flow.Log4Xojo.LogLevel.Error
: Errors requiring attention.Log4Xojo.LogLevel.Critical
: Critical issues that may cause application failure.
Example: If you set the log level to LogLevel.Warning
, only warnings, errors, and critical messages will be logged:
l4x.SetLogLevel(Log4Xojo.LogLevel.Warning)
l4x.Log("This is a debug message.", Log4Xojo.LogLevel.Debug) // Ignored
l4x.Log("This is a warning message.", Log4Xojo.LogLevel.Warning) // Logged
6. SetMaxBackupFiles(max As Integer)
Purpose: Configures the maximum number of backup log files to retain.
Usage:
l4x.SetMaxBackupFiles(5)
Details:
max
: The maximum number of backup files to keep. Older backups are automatically deleted when the limit is reached.- When the current log file exceeds the size limit (set via
SetMaxLogFileSize
), it is renamed as a backup (e.g.,AppLog_Date_1.txt
), and a new log file is created.
Example: If max = 5
, the backups will look like this:
AppLog_Date_1.txt
AppLog_Date_2.txt
AppLog_Date_3.txt
AppLog_Date_4.txt
AppLog_Date_5.txt
Once the 6th backup is created, AppLog_Date_1.txt
is deleted to make room for it.
7. SetMaxLogFileSize(sizeInBytes As Integer)
Purpose: Defines the maximum size of the log file before it is rotated.
Usage:
l4x.SetMaxLogFileSize(1 * 1024 * 1024) // 1 MB
Details:
sizeInBytes
: The maximum size of the log file in bytes.- When the log file exceeds this size, it is renamed as a backup, and a new log file is created.
8. StopLogging()
Purpose: Stops the logging thread and processes any remaining messages in the queue.
Usage:
l4x.StopLogging()
Details:
- Use this method when your application is shutting down to ensure that all log messages are written to their destinations.
- The method waits for a short period to process any remaining messages in the queue before terminating the logging thread.
Conclusion
Xojo’s built-in logging methods, System.DebugLog
and System.Log
, are excellent built-in tools for debugging and monitoring application behavior. They are simple, effective, and suitable for most types of projects.
For applications with more advanced requirements, the Log4Xojo class provides a powerful complement to these methods. With features like file logging, log rotation, multi-destination support, and log level filtering, Log4Xojo is the perfect solution for larger or more complex projects.
Download Log4Xojo from GitHub https://github.com/xojo/log4xojo or as a Xojo Binary Project file and start enhancing your logging workflow.
We look forward to hearing how you’re using Log4Xojo in your projects!