NSLog
Traditional logging method available in Objective-C and Swift. Simple usage but limitations in performance and flexibility. Not recommended in modern Swift development, but may be used for maintaining compatibility with legacy code.
Library
NSLog
Overview
NSLog is a traditional logging function included in Apple's Foundation framework, serving as a fundamental log output feature that has been used in iOS and macOS development for many years. It supports string formatting similar to the C printf function, automatically appending timestamps and process IDs to output to Apple System Log or Console.app. While existing since the Objective-C era and still usable in Swift, it processes synchronously causing slower performance, and Apple currently recommends migrating to OSLog.
Details
NSLog has served as a legacy API for logging on Apple platforms, functioning as a basic debugging tool for iOS/macOS developers for over 15 years. Available on all iOS and macOS versions as part of the Foundation framework, it supports message output using NSString-formatted strings. It records messages to the system log through system logging functionality, allowing log verification in Xcode's debug console and macOS Console.app. However, due to performance degradation during high-volume log output caused by synchronous processing design, Apple strongly recommends migrating to the unified logging system (OSLog/Logger) from iOS 14 onwards.
Key Features
- Simple API: Intuitive printf-style format string output
- Automatic Timestamps: Automatic appending of log output time and process ID
- System Integration: Direct output to Apple System Log and Console.app
- Cross-Language Support: Available in both Objective-C and Swift
- Universal Compatibility: Guaranteed operation on all Apple platforms
- Debug Support: Immediate verification in Xcode debug console
Pros and Cons
Pros
- Easy-to-use API available immediately without learning curve
- Compatibility across all iOS/macOS versions
- Intuitive operation through printf-style string formatting
- Real-time verification in Xcode debug console
- Persistence through automatic system log integration
- Foundation standard feature available without external dependencies
Cons
- Clear performance issues due to synchronous processing
- Lack of log level and category classification features
- No privacy control or filtering functionality
- Main thread blocking during high-volume log output
- Limited log management features in production environments
- Not Apple's recommended modern logging API
Reference Pages
- NSLog | Apple Developer Documentation
- Technical Note TN2347: Basic debugging using logging
- Logging | Apple Developer Documentation
Code Examples
Basic Setup
// Usage in Swift
import Foundation
// In Objective-C, #import <Foundation/Foundation.h> is required
// NSLog is automatically available as part of Foundation
Basic Log Output
// Basic message output
NSLog("Application started")
NSLog("Application has been initialized")
// Format strings with variables
let userName = "John Doe"
let userId = 12345
NSLog("User login: %@ (ID: %d)", userName, userId)
// Number and boolean formatting
let temperature = 23.5
let isConnected = true
NSLog("Current temperature: %.1f°C, Connection status: %@", temperature, isConnected ? "Connected" : "Disconnected")
// Complex formatting with multiple variables
let timestamp = Date()
let requestCount = 42
NSLog("Request processing completed - Time: %@, Processed: %d requests", timestamp, requestCount)
// Basic usage in Objective-C
#import <Foundation/Foundation.h>
// Basic message
NSLog(@"Application started");
// Log with variables
NSString *userName = @"John Doe";
NSInteger userId = 12345;
NSLog(@"User login: %@ (ID: %ld)", userName, (long)userId);
// Number formatting
CGFloat progress = 0.75f;
NSLog(@"Progress: %.2f%%", progress * 100);
Advanced Configuration
// Error information and stack trace output
func processData() {
do {
// Some processing
let result = try performComplexOperation()
NSLog("Data processing successful: %@", result)
} catch {
NSLog("Data processing error: %@", error.localizedDescription)
NSLog("Error details: %@", error)
}
}
// Conditional log output (debug builds only)
func debugLog(_ message: String, _ args: CVarArg...) {
#if DEBUG
let formattedMessage = String(format: message, args)
NSLog("DEBUG: %@", formattedMessage)
#endif
}
// Usage example
debugLog("Variable check: %d, %@", 42, "Test string")
// Logs with class and method information
class DataManager {
func loadData() {
NSLog("[%@::%@] Data loading started", NSStringFromClass(type(of: self)), #function)
// Data loading process
NSLog("[%@::%@] Data loading completed", NSStringFromClass(type(of: self)), #function)
}
}
// Detailed logs with file name and line number
func detailedLog(_ message: String, file: String = #file, line: Int = #line, function: String = #function) {
let fileName = (file as NSString).lastPathComponent
NSLog("[%@:%d %@] %@", fileName, line, function, message)
}
// Usage example
detailedLog("Important process has been executed")
Error Handling
// Network error logging
func handleNetworkError(_ error: Error) {
if let urlError = error as? URLError {
switch urlError.code {
case .notConnectedToInternet:
NSLog("Network error: No internet connection")
case .timedOut:
NSLog("Network error: Timeout")
case .cannotFindHost:
NSLog("Network error: Cannot find host")
default:
NSLog("Network error: %@ (Code: %ld)", urlError.localizedDescription, urlError.errorCode)
}
} else {
NSLog("Unexpected error: %@", error.localizedDescription)
}
}
// Exception handling and logging
func safeExecute<T>(_ operation: () throws -> T) -> T? {
do {
let result = try operation()
NSLog("Operation successful")
return result
} catch {
NSLog("Operation failed: %@", error)
return nil
}
}
// Core Data error logging
func logCoreDataError(_ error: NSError) {
NSLog("Core Data error:")
NSLog(" Domain: %@", error.domain)
NSLog(" Code: %ld", error.code)
NSLog(" Description: %@", error.localizedDescription)
if let userInfo = error.userInfo as? [String: Any] {
for (key, value) in userInfo {
NSLog(" %@: %@", key, String(describing: value))
}
}
}
Practical Examples
// Application lifecycle recording
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NSLog("Application launch completed")
return true
}
func applicationWillEnterForeground(_ application: UIApplication) {
NSLog("Application moving to foreground")
}
func applicationDidEnterBackground(_ application: UIApplication) {
NSLog("Application moving to background")
}
}
// API request logging
class APIClient {
func performRequest(url: String) {
NSLog("API request started: %@", url)
guard let requestURL = URL(string: url) else {
NSLog("API request error: Invalid URL - %@", url)
return
}
let task = URLSession.shared.dataTask(with: requestURL) { data, response, error in
if let error = error {
NSLog("API request failed: %@", error.localizedDescription)
return
}
if let httpResponse = response as? HTTPURLResponse {
NSLog("API response: Status code %ld", httpResponse.statusCode)
if let data = data {
NSLog("API response: Data size %ld bytes", data.count)
}
}
}
task.resume()
}
}
// User action recording
class UserActionLogger {
static func logButtonTap(_ buttonTitle: String) {
NSLog("User action: Button tap - %@", buttonTitle)
}
static func logScreenView(_ screenName: String) {
NSLog("Screen transition: Displaying %@", screenName)
}
static func logUserEvent(_ eventName: String, parameters: [String: Any]? = nil) {
var logMessage = "User event: \(eventName)"
if let params = parameters {
let paramStrings = params.map { "\($0.key)=\($0.value)" }
logMessage += " [\(paramStrings.joined(separator: ", "))]"
}
NSLog("%@", logMessage)
}
}
// Usage examples
UserActionLogger.logButtonTap("Login Button")
UserActionLogger.logScreenView("Home Screen")
UserActionLogger.logUserEvent("Product Purchase", parameters: ["Product ID": "ABC123", "Price": 1980])
// Performance measurement
class PerformanceLogger {
private static var timers: [String: CFAbsoluteTime] = [:]
static func startTimer(_ name: String) {
timers[name] = CFAbsoluteTimeGetCurrent()
NSLog("Performance measurement started: %@", name)
}
static func endTimer(_ name: String) {
guard let startTime = timers.removeValue(forKey: name) else {
NSLog("Performance measurement error: Timer '%@' not found", name)
return
}
let duration = CFAbsoluteTimeGetCurrent() - startTime
NSLog("Performance measurement ended: %@ - %.3f seconds", name, duration)
}
}
// Usage example
PerformanceLogger.startTimer("Data Loading")
// Some heavy processing
PerformanceLogger.endTimer("Data Loading")