LibComponentLogging is a small open source logging library for Objective-C applications on Mac OS X and the iPhone OS which provides conditional logging based on log levels and log components. Additionally, different logging strategies can be used, e.g. writing log messages to a file or sending them to the system log, while using the same logging interface.
LibComponentLogging is available under the terms of the MIT license.
LibComponentLogging is hosted on GitHub.
Found a bug? Missing a feature? Just drop a line to logging@0xc0.de, or create an issue in one of the repositories on GitHub.
Overview
LibComponentLogging is a small logging library for Objective-C applications which has the following characteristics:
- Log levels: The library provides log levels for distinguishing between error messages, informational messages, and fine-grained trace messages for debugging.
- Log components: The library provides log components for identifying different parts of an application. A log component contains a unique identifier, a short name which is used as a header in a log message, and a full name which can be used in a user interface.
- Active log level per log component: At runtime, the library provides an active log level for each log component in order to enable/disable logging for certain parts of an application.
- Grouping of log components: Log components which have the same name prefix form a group of log components and logging can be enabled/disabled for the whole group with a single command.
- Low runtime-overhead when logging is disabled: Logging is based on a log macro which checks the active log level before constructing the log message and before evaluating log message arguments.
- Code completion support: The library provides symbols for log components and log levels which work with Xcode's code completion.
- Meta data: The library provides public data structures which contain information about log levels and log components, e.g. headers and names.
- Pluggable loggers: The core part of the library does not contain a fixed logger, but provides a simple delegation mechanism for plugging-in a specific logger based on the application's requirements, e.g. a logger which writes to the system log, or a logger which writes to a log file. The logger is configured at build-time.
In order to use the library in your application, just copy the files of the library core and the logger into your source directory, create a configuration file for your application's log components, add the library's implementation files to your project target, and compile them along with all other files of your project.
For step-by-step instructions, take a look at the installation section below.
Library Core
LibComponentLogging is split up into a library core accompanied by different loggers which implement certain logging strategies, e.g. one logger may write log messages to application-specific log files while another logger may send them to the system log, etc. This separation makes LibComponentLogging deployable for different application scenarios while using the same logging interface.
The library core comprises the following files:
-
lcl.h: Main header file. -
lcl.m: Core implementation. -
lcl_config_components.h: Configuration of an application's log components. -
lcl_config_logger.h: Configuration of the logger. -
lcl_config_extensions.h: Configuration of logging extensions. (since 1.1)
The main header lcl.h contains all definitions of the library core and serves as a main include file for all configuration files and other parts of the library. Therefore, an application should only include or import the lcl.h file, e.g. in its prefix header file.
Symbols for log levels and log components, which work with Xcode's code completion, are automatically provided by the main header file. All symbols, e.g. values or functions, which are relevant when using the logging interface in an application, are prefixed with 'lcl_'. Log levels share the common prefix 'lcl_v' and log components the prefix 'lcl_c'. Internal symbols, which are needed when working with meta data, when defining log components, or when writing a logger, are prefixed with '_lcl_'.
GitHub: Library Core | Repository | Recent Changes | Issues | DownloadsLogging Interface
The main interface for writing a message to the application's log is the macro lcl_log which creates a log message from the given arguments and sends it to the configured logger if logging is enabled for the given log component.
The syntax of lcl_log is similiar to NSLog's syntax:
lcl_log(<component>, <level>, @"<format>"[, <arg1>[, <arg2>[, ...]]]);
where
-
<component>is the identifier of a log component, -
<level>is the log level for the current log message, -
<format>is a printf-style format string of type NSString which may include '%@' placeholders, and -
<arg...>are optional arguments required by the format string.
The use of a macro enables checking of the active log level before constructing the log message and before evaluating log message arguments. Therefore, if logging is disabled, the runtime-overhead of logging statements in an application is very low. Additionally, all logging code can be stripped from the application by re-defining the log macro to an empty effect, if required. Since version 1.1, the core's lcl.h file contains a definition of an empty log macro which is used if the preprocessor define _LCL_NO_LOGGING is defined.
At runtime, logging can be enabled/disabled for one or more log components by calling
lcl_configure_by_component(<component>, <level>);
or
lcl_configure_by_name(<name>, <level>);
where <component> is a log component or <name> is the full name of a log component which may include the wildcard suffix * in order to enable logging at the given level <level> for all log components with the same name prefix.
The library core provides the following log levels:
- Critical: Critical situation.
- Error: Error situation.
- Warning: Warning.
- Info: Informational message.
- Debug: Coarse-grained debugging information.
- Trace: Fine-grained debugging information.
All log components of an application are defined in the file lcl_config_components.h by using the _lcl_component macro which has the following syntax:
_lcl_component(<identifier>, <header>, <name>)
where
-
<identifier>is the unique identifier of a log component which is used in calls tolcl_log,lcl_configure_by_component, etc., -
<header>is a C string in UTF-8 which should be used by the logger when writing a log message for the log component, and -
<name>is a C string in UTF-8 which contains the name of the log component and its grouping information based on a path syntax.
Logging Examples
A lcl_config_components.h file which contains the following lines
_lcl_component(UIC1, "ui.c1", "User Interface/Component 1")
_lcl_component(UIC2, "ui.c2", "User Interface/Component 2")
_lcl_component(UIC3, "ui.c3", "User Interface/Component 3")
defines three log components 'Component 1', 'Component 2', and 'Component 3', which form the group 'User Interface'. Their log message headers are 'ui.c1', 'ui.c2', and 'ui.c3', and their identifiers are 'UIC1', 'UIC2', and 'UIC3'. Based on the defined log component identifiers, the library core automatically defines the code symbols lcl_cUIC1, lcl_cUIC2, and lcl_cUIC3, which must be used in calls to lcl_log, lcl_configure_by_component, etc.
Based on these log components, logging of critical messages, errors, warnings, and informational messages for all user interface components can be enabled by calling
lcl_configure_by_name("User Interface/*", lcl_vInfo);
Log messages can be created by the application by calling lcl_log, e.g.
lcl_log(lcl_cUIC2, lcl_vInfo, @"initialized");
…
lcl_log(lcl_cUIC1, lcl_vError, @"initialization failed, reason: %@", reason);
…
lcl_log(lcl_cUIC3, lcl_vTrace, @"button A pressed");
where the last log message for log component UIC3 will not be created and not written to the log, because the log level Trace is not active for the log component UIC3 in this example.
LogFile Logger
LogFile is a file logging class which writes log messages to an application-specific log file. The application's log file is opened automatically when the first log message needs to be written to the log file. If the log file reaches a configured maximum size, it gets rotated and all previous messages will be moved to a backup log file. The backup log file is kept until the next rotation.
The logging class can be used as a logging back-end for LibComponentLogging, but it can also be used as a standalone logger without the Core files of LibComponentLogging, e.g. in combination with some simple DebugLog macros which do not know about log components. (since 1.1)
The LogFile logger uses the format
<date> <time> <pid>:<tid> <level>
<component>:<file>:<line>:<func> <message>
where the file name, the line number and the function name are optional.
Example:
2009-02-01 12:38:32.796 4964:10b D component1:main.m:28:-[Class method] Text
2009-02-01 12:38:32.798 4964:10b D component2:main.m:32:-[Class method] Text
2009-02-01 12:38:32.799 4964:10b D component3:main.m:36:-[Class method] Text
SystemLog Logger
SystemLog is a LibComponentLogging logger implementation which sends log messages to the Apple System Log facility (ASL).
With ASL, log messages are stored as structured messages in a data store. The syslog utility or the Console application can be used to retrieve messages from this data store.
Example:
syslog -F '$(Time) $(Sender)[$(PID):$(Thread)] $(Level0) $(Message) ($(Facility):$(File):$(Line))'
-T utc -k Level0 -k Sender eq Example
retrieves all messages from the data store where the value associated with the 'Sender' key (the identifier of an application) is equal to 'Example' and where a value for the 'Level0' key exists. The key 'Level0' is used by SystemLog to store the log level in addition to a mapped ASL priority level ('Level' key). All retrieved messages will be printed by using the UTC time format and the display format specified via '-F':
2009.02.01 12:38:32 UTC Example[6717:10b] D Message 1 (example.f1:main.m:28)
2009.02.01 12:38:32 UTC Example[6717:10b] C Message 2 (example.f2:main.m:32)
2009.02.01 12:38:32 UTC Example[6717:10b] I Message 3 (example.f3:main.m:36)
The logging class can be used as a logging back-end for LibComponentLogging or as a standalone logger without the Core files of LibComponentLogging, e.g. wrapped by your own DebugLog macros. (since 1.1)
GitHub: SystemLog Logger | Repository | Recent Changes | Issues | DownloadsNSLog Logger
The NSLog logger implements a very simple logger which redirects logging to NSLog, but adds information about the log level, the log component, and the log statement's location (file name and line number).
The logger uses the following format
<NSLog prefix> <level> <component>:<file>:<line> <message>
where NSLog prefix is the header information written by NSLog. This header contains the current date and time, the application's name, the process id, and the thread id.
Example:
2009-02-01 12:38:32.796 Example[4964:10b] D component1:main.m:28 Message
2009-02-01 12:38:32.798 Example[4964:10b] D component2:main.m:32 Message
2009-02-01 12:38:32.799 Example[4964:10b] D component3:main.m:36 Message
Installation
Download the files of the library Core and a logging back-end, e.g. the LogFile logger, from their repositories on GitHub:
Extract the files and copy the extracted files to your application's source directory.
Open Xcode and add all files of the library to your application's project. Xcode will automatically add the library's implementation files to your project's target.
Create a lcl_config_logger.h file and set up the logger, e.g. set the maximum file size and the name of the log file for the LogFile logger.
Create a lcl_config_extensions.h file and optionally add #import statements for logging extensions.
Create your application's lcl_config_components.h file.
Add a #import statement for lcl.h to your application files, e.g. to your application's prefix header file.
Define your log components in lcl_config_components.h.
Add lcl_log(...) log statements to your application.
GitHub Repositories
Library Core
Loggers
Extensions
Examples
Notes and Known Problems
If Xcode's code completion doesn't show your 'lcl_c'-prefixed log components, close Xcode, open a terminal, touch the lcl.* files in your application's source directory, and restart Xcode.
If your project is a Mac OS X framework, rename the Objective-C classes of the logger by adding your framework's unique name prefix, e.g. MyFramework, in order to avoid duplicate symbols in the global namespace. A convenient way to rename the classes is to add preprocessor defines like
#define LCLLogFile MyFrameworkLCLLogFile
as the first statements in the lcl_config_logger.h file.
Bugs and Feature Requests
Please send bugs, feedback, and feature requests to logging@0xc0.de, or create an issue in one of the repositories on GitHub.
Copyright and License
Copyright 2008-2010 Arne Harren <ah at 0xc0.de>.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
