{"id":1350,"url":"https://github.com/swisspol/XLFacility","last_synced_at":"2025-08-02T04:30:54.662Z","repository":{"id":21779163,"uuid":"25101513","full_name":"swisspol/XLFacility","owner":"swisspol","description":"Elegant and extensive logging facility for OS X \u0026 iOS (includes database, Telnet and HTTP servers)","archived":true,"fork":false,"pushed_at":"2019-06-29T00:00:30.000Z","size":388,"stargazers_count":317,"open_issues_count":1,"forks_count":34,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-11-22T12:27:40.847Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swisspol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-12T00:56:21.000Z","updated_at":"2024-05-16T02:10:24.000Z","dependencies_parsed_at":"2022-07-21T20:18:16.870Z","dependency_job_id":null,"html_url":"https://github.com/swisspol/XLFacility","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisspol%2FXLFacility","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisspol%2FXLFacility/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisspol%2FXLFacility/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisspol%2FXLFacility/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swisspol","download_url":"https://codeload.github.com/swisspol/XLFacility/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228438781,"owners_count":17920013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:15:44.421Z","updated_at":"2024-12-06T08:30:45.925Z","avatar_url":"https://github.com/swisspol.png","language":"Objective-C","funding_links":[],"categories":["Logging","Objective-C"],"sub_categories":["Other Hardware","Other free courses"],"readme":"Overview\n========\n\n[![Build Status](https://travis-ci.org/swisspol/XLFacility.svg?branch=master)](https://travis-ci.org/swisspol/XLFacility)\n[![Version](http://cocoapod-badges.herokuapp.com/v/XLFacility/badge.png)](http://cocoadocs.org/docsets/XLFacility)\n[![Platform](http://cocoapod-badges.herokuapp.com/p/XLFacility/badge.png)](https://github.com/swisspol/XLFacility)\n[![License](http://img.shields.io/cocoapods/l/XLFacility.svg)](LICENSE)\n\nXLFacility, which stands for *Extensive Logging Facility*, is an elegant and powerful logging facility for OS X \u0026 iOS. It was written from scratch with the following goals in mind:\n* Drop-in replacement of `NSLog()` along with trivial to use macros to log messages anywhere in your app without impacting performance\n* Support a wide variety of logging destinations aka \"loggers\"\n* Customizable logging formats\n* Modern, clean and compact codebase fully taking advantage of the latest Obj-C runtime and Grand Central Dispatch\n* Easy to understand architecture with the ability to write custom loggers in a few lines of code\n* No dependencies on third-party source code\n* Available under a friendly [New BSD License](LICENSE)\n\nBuilt-in loggers:\n* Standard output and standard error\n* [Apple System Logger](https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/LoggingErrorsAndWarnings.html)\n* Local file\n* Local [SQLite](http://www.sqlite.org/) database\n* Telnet server which can be accessed from a terminal on a different computer to monitor log messages as they arrive\n* HTTP server which can be accessed from a web browser on a different computer to browse the past log messages and see live updates\n* Raw TCP connection which can send log messages to a remote server as they happen\n* User interface logging window overlay for OS X \u0026 iOS apps\n\nRequirements:\n* OS X 10.8 or later (x86_64)\n* iOS 8.0 or later (armv7, armv7s or arm64)\n* ARC memory management only\n\nGetting Started\n===============\n\nDownload or check out the [latest release](https://github.com/swisspol/XLFacility/releases) of XLFacility then add the \"XLFacility\", \"GCDTelnetServer/GCDTelnetServer\" and \"GCDTelnetServer/GCDNetworking/GCDNetworking\" subfolders to your Xcode project.\n\nAlternatively, you can install XLFacility using [CocoaPods](http://cocoapods.org/) by simply adding this line to your Xcode project's Podfile:\n```\npod \"XLFacility\", \"~\u003e 1.0\"\n```\n\nThis provides all loggers including the server ones but if you want just the \"core\" of XLFacility and the basic loggers, use instead:\n```\npod \"XLFacility/Core\", \"~\u003e 1.0\"\n```\n\nDrop-in NSLog Replacement\n=========================\n\nIn the precompiled header file for your Xcode project, insert the following:\n```objectivec\n#ifdef __OBJC__\n#import \"XLFacilityMacros.h\"\n#define NSLog(...) XLOG_INFO(__VA_ARGS__)\n#endif\n```\n\nFrom this point on, any calls to `NSLog()` in your app source code to log a message will be replaced by ones to XLFacility. Note that this will **not** affect calls to `NSLog()` done by Apple frameworks or third-party libraries in your app (see \"Capturing Stderr and Stdout\" further in this document for a potential solution).\n\nTest-Driving Your (Modified) App\n================================\n\nSo far nothing has really changed on the surface except that when running your app from Xcode, messages logged with `NSLog()` now appear in the console like this:\n```\n00:00:00.248 [INFO     ]\u003e Hello World!\n```\nWhile previously they would look like that:\n```\n2014-10-12 02:41:29.842 TestApp[37006:2455985] Hello World!\n```\n\nThat's the first big difference between XLFacility and `NSLog()`: you can customize the output to fit your taste. Try adding `#import \"XLStandardLogger.h\"` to the top of the `main.m` file of your app and then inserting this line inside `main()` before `UIApplication` or `NSApplication` gets called:\n```objectivec\n[[XLStandardLogger sharedErrorLogger] setFormat:XLLoggerFormatString_NSLog];\n```\nRun your app again and notice how messages in the console now look exactly like when using `NSLog()`.\n\nLet's use a custom compact format instead:\n```objectivec\n[[XLStandardLogger sharedErrorLogger] setFormat:@\"[%l | %q] %m\"];\n```\nRun your app again and messages in the Xcode console should now look like this:\n```\n[INFO | com.apple.main-thread] Hello World!\n```\n\n*See [XLLogger.h](XLFacility/Core/XLLogger.h) for the full list of format specifiers supported by XLFacility.*\n\nLogging Messages With XLFacility\n================================\n\nLike pretty much all logging systems, XLFacility defines various logging levels, which are by order of importance: `DEBUG`, `VERBOSE`, `INFO`, `WARNING`, `ERROR`, `EXCEPTION` and `ABORT`. The idea is that when logging a message, you also provide the corresponding importance level: for instance `VERBOSE` to trace and help debug what is happening in the code, versus `WARNING` and above to report actual issues. The logging system can then be configured to \"drop\" messages that are below a certain level, allowing the user to control the \"signal-to-noise\" ratio.\n\nBy default, when building your app in \"Release\" configuration, XLFacility ignores messages at the `DEBUG` and `VERBOSE` levels. When building in \"Debug\" configuration (requires the `DEBUG` preprocessor constant evaluating to non-zero), it keeps everything.\n\n**IMPORTANT:** So far you've seen how to \"override\" `NSLog()` calls in your source code to redirect messages to XLFacility at the `INFO` level but this is not the best approach. Instead don't use `NSLog()` at all but call directly XLFacility functions to log messages.\n\nYou can log messages in XLFacility by calling the logging methods on the shared `XLFacility` instance or by using the macros from [XLFacilityMacros.h](XLFacility/Core/XLFacilityMacros.h). The latter is highly recommended as macros produce the exact same logging results but are quite easier to the eye, faster to type, and most importantly they avoid evaluating their arguments unless necessary.\n\nThe following macros are available to log messages at various levels:\n* `XLOG_DEBUG(...)`: Becomes a no-op if building \"Release\" (i.e. if the `DEBUG` preprocessor constant evaluates to zero)\n* `XLOG_VERBOSE(...)`\n* `XLOG_INFO(...)`\n* `XLOG_WARNING(...)`\n* `XLOG_ERROR(...)`\n* `XLOG_EXCEPTION(__EXCEPTION__)`: Takes an `NSException` and not a format string (the message is generated automatically from the exception)\n* `XLOG_ABORT(...)`: Calls `abort()` to immediately terminate the app after logging the message\n\nWhen calling the macros, except for `XLOG_EXCEPTION()`, use the standard format specifiers from Obj-C like in `NSLog()`, `+[NSString stringWithFormat:...]`, etc... For instance:\n```objectivec\nXLOG_WARNING(@\"Unable to load URL \\\"%@\\\": %@\", myURL, myError);\n```\n\nOther useful macros available to use in your source code:\n* `XLOG_CHECK(__CONDITION__)`: Checks a condition and if false calls `XLOG_ABORT()` with an automatically generated message\n* `XLOG_UNREACHABLE()`: Calls `XLOG_ABORT()` with an automatically generated message if the app reaches this point\n* `XLOG_DEBUG_CHECK(__CONDITION__)`: Same as `XLOG_CHECK()` but becomes a no-op if building in \"Release\" configuration (i.e. if the `DEBUG` preprocessor constant evaluates to zero)\n* `XLOG_DEBUG_UNREACHABLE()`: Same as `XLOG_UNREACHABLE()` but becomes a no-op if building in \"Release\" configuration (i.e. if the `DEBUG` preprocessor constant evaluates to zero)\n\nHere are some example use cases:\n```objectivec\n- (void)processString:(NSString*)string {\n  XLOG_CHECK(string);  // Passing a nil string is a serious programming error and we can't continue\n  // Do something\n}\n\n- (void)checkString:(NSString*)string {\n  if ([string hasPrefix:@\"foo\"]) {\n    // Do something\n  } else if ([string hasPrefix:@\"bar\"]) {\n    // Do something\n  } else {\n    XLOG_DEBUG_UNREACHABLE();  // This should never happen\n  }\n}\n```\n\n*Messages logged with XLFacility can be associated with an optional tag which is an arbitrary string. This is a powerful feature that lets you for instance capture as part of the log message the source file name and line number. See [XLFacilityMacros.h](XLFacility/Core/XLFacilityMacros.h) for more information.*\n\nFun With Remote Logging\n=======================\n\nGoing back to the `main.m` file of your app, add `#import \"XLTelnetServerLogger.h\"` to the top, and insert this line before `UIApplication` or `NSApplication` gets called:\n```objectivec\n[XLSharedFacility addLogger:[[XLTelnetServerLogger alloc] init]];\n```\nWhat we are doing here is adding a secondary \"logger\" to XLFacility so that log messages are sent to two destinations simultaneously.\n\nRun your app locally on your computer (use the iOS Simulator for an iOS app) then enter his command in Terminal app:\n```sh\ntelnet localhost 2323\n```\nYou should see this output on screen:\n```sh\n$ telnet localhost 2323\nTrying ::1...\ntelnet: connect to address ::1: Connection refused\nTrying 127.0.0.1...\nConnected to localhost.\nEscape character is '^]'.\nYou are connected to TestApp[37006] (in color!)\n\n```\n\nAny call to `NSLog()` in your app's source code is now being sent live to your Terminal window. And when you connect to your app, as a convenience to make sure you haven't missed anything,  `XLTelnetServerLogger` will immediately replay all messages logged since the app was launched (this behavior can be changed).\n\nWhat's really interesting and useful is connecting to your app while it's running on another Mac or on a real iPhone / iPad. As long as your home / office / WiFi network doesn't block communication on port `2323` (the default port used by `XLTelnetServerLogger`), you should be able to remotely connect by simply entering `telnet YOUR_DEVICE_IP_ADDRESS 2323` in Terminal on your computer.\n\nOf course, like you've already done above with `XLStandardLogger`, you can customize the format used by `XLTelnetServerLogger`, for instance like this:\n```objectivec\nXLLogger* logger = [[XLTelnetServerLogger alloc] init];\nlogger.format = @\"[%l | %q] %m\";\n[XLSharedFacility addLogger:logger];\n```\n\nYou can even add multiples instances of `XLTelnetServerLogger` to XLFacility, each listening on a unique port and configured differently.\n\n**IMPORTANT:** It's not recommended that you ship your app on the App Store with `XLTelnetServerLogger` active by default as this could be a security and / or privacy issue for your users. Since you can add and remove loggers at any point during the lifecyle of your app, you can instead expose a user interface setting that will dynamically add or remove `XLTelnetServerLogger` from XLFacility.\n\nLog Monitoring From Your Web Browser\n====================================\n\nDo the same modification as you've done above to add suport for `XLTelnetServerLogger` but use `XLHTTPServerLogger` instead. When your app is running go to `http://127.0.0.1:8080/` or `http://YOUR_DEVICE_IP_ADDRESS:8080/` in your web browser. You should be able to see all the XLFacility log messages from your app since it started. The web page will even automatically refresh when new log messages are available.\n\n**IMPORTANT:** For the same reasons than for `XLTelnetServerLogger`, it's also not recommended that you ship your app on the App Store with `XLHTTPServerLogger` active by default.\n\nOnscreen Logging Overlay\n========================\n\nOn OS X \u0026 iOS apps you can easily have an overlay logging window that appears whenever log messages are sent to XLFacility. Simply take advantage of `XLUIKitOverlayLogger` or `XLAppKitOverlayLogger` like this:\n\n**iOS version**\n```objectivec\n#import \"XLUIKitOverlayLogger.h\"\n\n@implementation MyAppDelegate\n\n- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {\n  [XLSharedFacility addLogger:[XLUIKitOverlayLogger sharedLogger]];\n  \n  // Rest of your app initialization code goes here\n}\n\n@end\n```\n\n**OS X version**\n```objectivec\n#import \"XLAppKitOverlayLogger.h\"\n\n@implementation MyAppDelegate\n\n- (void)applicationDidFinishLaunching:(NSNotification*)notification {\n  [XLSharedFacility addLogger:[XLAppKitOverlayLogger sharedLogger]];\n  \n  // Rest of your app initialization code goes here\n}\n\n@end\n```\n\nArchiving Log Messages\n======================\n\nThere are a couple ways to save persistently across app relaunches the log messages sent to XLFacility:\n\nThe simplest solution is to use `XLFileLogger` to save log messages to a plain text file like this:\n```objectivec\nXLFileLogger* fileLogger = [[XLFileLogger alloc] initWithFilePath:@\"my-file.log\" append:YES];\nfileLogger.minLogLevel = kXLLogLevel_Error;\nfileLogger.format = @\"%d\\t%m\";\n[XLSharedFacility addLogger:fileLogger];\n```\n\nThe more powerful solution is to use `XLDatabaseLogger` which uses a [SQLite](http://www.sqlite.org/) database under the hood:\n```objectivec\nXLDatabaseLogger* databaseLogger = [[XLDatabaseLogger alloc] initWithDatabasePath:@\"my-database.db\" appVersion:0];\n[XLSharedFacility addLogger:databaseLogger];\n```\n\nNote that `XLDatabaseLogger` serializes the log messages to the database as-is and does not format them i.e. its `format` property has no effect.\n\nYou can easily \"replay\" later the saved log messages, for instance to display them in a log window in your application interface or to send them to a server:\n```objectivec\n[databaseLogger enumerateRecordsAfterAbsoluteTime:0.0\n                                         backward:NO\n                                       maxRecords:0\n                                       usingBlock:^(int appVersion, XLLogRecord* record, BOOL* stop) {\n  // Do something with each log record\n  printf(\"%s\\n\", [record.message UTF8String]);\n}];\n```\n\nFiltering XLFacility Log Messages\n=================================\n\nUse the `minLogLevel` property on the `XLFacility` shared instance to have XLFacility ignore all log messages below a certain level.\n\nYou can also control the minimum and maximum log level on each logger using their `minLogLevel` and `maxLogLevel` properties. You can even set a fully custom log record filter on a logger like this:\n```objectivec\nmyLogger.logRecordFilter = ^BOOL(XLLogger* logger, XLLogRecord* record) {\n  return [record.tag hasPrefix:@\"com.my-app.\"];\n};\n```\n\nLogging Exceptions\n==================\n\nCall `[XLSharedFacility setLogsUncaughtExceptions:YES]` early enough in your app (typically from `main()` before `UIApplication` or `NSApplication` gets called) to have XLFacility install an uncaught exception handler to automatically call `XLOG_EXCEPTION()` passing the exception before the app terminates.\n\nIf you want instead to log *all* exceptions, as they are created and wether or not they are caught, use `[XLSharedFacility setLogsInitializedExceptions:YES]` instead. Note that this will also log exceptions that are not thrown either.\n\nIn both cases, XLFacility will capture the current callstack as part of the log message.\n\nCapturing Stderr and Stdout\n===========================\n\nIf you use XLFacility functions exclusively in your app to log messages, then everything you log from your source code will go to XLFacility. If you use third-party source code, you might be able to patch or override its calls to `NSLog()`, `printf()` or equivalent as demonstrated at the beginning of this document. However this will not work for Apple or third-party libraries or frameworks.\n\nXLFacility has a powerful feature that allows to capture the standard output and standard error from your app. Just call `[XLSharedFacility setCapturesStandardError:YES]` (respectively `[XLSharedFacility setCapturesStandardOutput:YES]`) and from this point on anything written to the standard output (respectively standard error) will be split on newlines boundaries and automatically become separate log messages in XLFacility with the `INFO` (respectively `ERROR`) level.\n\nWriting Custom Loggers\n======================\n\nYou can write a custom logger in a few lines of code by using `XLCallbackLogger` like this:\n```objectivec\n[XLSharedFacility addLogger:[XLCallbackLogger loggerWithCallback:^(XLCallbackLogger* logger, XLLogRecord* record) {\n  // Do something with the log record\n  printf(\"%s\\n\", [record.message UTF8String]);\n}]];\n```\n\nTo implement more complex loggers, you will need to subclass `XLLogger` and implement at least the `-logRecord:` method:\n```objectivec\n@interface MyLogger : XLLogger\n@end\n\n@implementation MyLogger\n\n- (void)logRecord:(XLLogRecord*)record {\n  // Do something with the log record\n  NSString* formattedMessage = [self formatRecord:record];\n  printf(\"%s\", [formattedMessage UTF8String]);\n}\n\n@end\n```\nIf you need to perform specific setup and cleanup operations when an instance of your logger is added or removed from XLFacility, also implement the `-open` and `-close` methods.\n\n**IMPORTANT:** Due to the way XLFacility works, logger instances do not need to be reentrant, but they need to be able to run on arbitrary threads.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisspol%2FXLFacility","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswisspol%2FXLFacility","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisspol%2FXLFacility/lists"}