blob: dcfa832698dfa7ffca25f158fb255e28097f6b86 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements a platform-specific Weave log interface.
*
*/
#include <stdarg.h>
#include <stdio.h>
#import <Foundation/Foundation.h>
#import "NLWeaveLogWriter.h"
#import "NLWeaveLogging.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#import <os/log.h>
#endif
#include <Weave/Support/logging/WeaveLogging.h>
NS_ASSUME_NONNULL_BEGIN
#if WEAVE_ERROR_LOGGING || WEAVE_PROGRESS_LOGGING || WEAVE_DETAIL_LOGGING
#pragma mark - nl::Weave::Logging::Log
namespace nl {
namespace Weave {
namespace Logging {
/*
* void Log()
*
* Description:
* This routine writes to the Foundation log stream, the
* specified Weave-related variable argument message for the
* specified Weave module and category.
*
* Input(s):
* aModule - An enumeration indicating the Weave module or
* subsystem the message is associated with.
* aCategory - An enumeration indicating the Weave category
* the message is associated with.
* aMsg - A NULL-terminated C string containing the message,
* with C Standard I/O-style format specifiers, to log.
* ... - A variable argument list, corresponding to format
* specifiers in the message.
*
* Output(s):
* N/A
*
* Returns:
* N/A
*
*/
void Log(uint8_t aModule, uint8_t aCategory, const char * aMsg, ...)
{
va_list v;
va_start(v, aMsg);
if (IsCategoryEnabled(aCategory)) {
char formattedMsg[512];
size_t prefixLen;
char moduleName[nlWeaveLoggingModuleNameLen + 1];
GetModuleName(moduleName, aModule);
prefixLen = snprintf(formattedMsg, sizeof(formattedMsg), "WEAVE:%s: %s", moduleName,
(aCategory == kLogCategory_Error) ? "ERROR: " : "");
if (prefixLen >= sizeof(formattedMsg))
prefixLen = sizeof(formattedMsg) - 1;
vsnprintf(formattedMsg + prefixLen, sizeof(formattedMsg) - prefixLen, aMsg, v);
// Pass the formatted log to @c NLWeaveLogging for handling.
[NLWeaveLogging handleWeaveLogFromModule:(NLLogModule) aModule
moduleName:[NSString stringWithUTF8String:moduleName]
level:(NLLogLevel) aCategory
formattedMessage:[NSString stringWithUTF8String:formattedMsg]];
}
va_end(v);
}
} // namespace Logging
} // namespace Weave
} // namespace nl
#endif // WEAVE_ERROR_LOGGING || WEAVE_PROGRESS_LOGGING || WEAVE_DETAIL_LOGGING
#if TARGET_OS_IPHONE && DEBUG
/**
* @def IS_OPERATING_SYSTEM_AT_LEAST_VERSION(version)
*
* @brief
* Compares the current version of operating system to the given version.
*
* @param version An @c NSOperatingSystemVersion describing an iOS operating system version.
*
* @return A boolean indicating whether the operating system version is at least the given version.
*/
#define IS_OPERATING_SYSTEM_AT_LEAST_VERSION(version) \
([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)] && \
[[NSProcessInfo new] isOperatingSystemAtLeastVersion:version])
/** Minimum iOS version with OSLog support (iOS 10.0.0). */
static const NSOperatingSystemVersion kMinimumOSLogOperatingSystemVersion
= { .majorVersion = 10, .minorVersion = 0, .patchVersion = 0 };
/** Root of the OSLog subsystem for Weave logs. */
static NSString * const kWeaveOSLogSubsystem = @"com.nest.weave";
/** Name of the subsystem for Weave logs from the shared C++ code. */
static NSString * const kWeaveCppComponent = @"cpp";
/** Name of the subsystem for Weave logs from the platform-specific Objective-C code. */
static NSString * const kWeaveCocoaComponent = @"cocoa";
/** Root of the OSLog category for Weave logs. */
static NSString * const kWeaveOSLogCategory = @"WEAVE";
#pragma mark - @c NLOSLogStore
/** Stores a dictionary mapping each @c NLLogModule to an OSLog logging component. */
@interface NLOSLogStore : NSObject
/** Returns the @c os_log_t for the given @c NLLogModule, creating a new one if needed. */
+ (os_log_t)logForModule:(NLLogModule)logModule name:(NSString *)moduleName;
@end
@implementation NLOSLogStore
/** Map of @c NLLogModule values to their OS Log object. */
static NSMutableDictionary<NSNumber *, os_log_t> * gModuleMap;
#pragma mark Initialization
+ (void)initialize
{
if (self != [NLOSLogStore class]) {
return;
}
@synchronized(self) {
gModuleMap = [[NSMutableDictionary alloc] init];
}
}
#pragma mark Public
+ (os_log_t)logForModule:(NLLogModule)logModule name:(NSString *)logModuleName
{
@synchronized(self) {
if (!gModuleMap[@(logModule)]) {
gModuleMap[@(logModule)] = [self createLogForModule:logModule name:logModuleName];
}
return gModuleMap[@(logModule)];
}
}
#pragma mark Private
+ (os_log_t)createLogForModule:(NLLogModule)logModule name:(NSString *)name
{
NSString * component = (logModule == NLLogModuleCocoa ? kWeaveCocoaComponent : kWeaveCppComponent);
NSString * subsystem = [NSString stringWithFormat:@"%@.%@", kWeaveOSLogSubsystem, component];
NSString * category = [NSString stringWithFormat:@"%@.%@", kWeaveOSLogCategory, name];
return os_log_create(subsystem.UTF8String, category.UTF8String);
}
@end
#endif // TARGET_OS_IPHONE && DEBUG
#pragma mark - @c NLWeaveLogging
@implementation NLWeaveLogging
/** The shared external log writer that is subscribed to receive Weave logs. */
static __nullable id<NLWeaveLogWriter> gSharedWriter = nil;
#pragma mark - Public
+ (void)setSharedLogWriter:(nullable id<NLWeaveLogWriter>)logWriter
{
@synchronized(self) {
gSharedWriter = logWriter;
}
}
+ (void)handleWeaveLogFromModule:(NLLogModule)logModule
moduleName:(NSString *)logModuleName
level:(NLLogLevel)logLevel
formattedMessage:(NSString *)formattedLogMessage
{
if (!formattedLogMessage) {
return;
}
// 1. Write the log to the system console (in debug builds).
#if DEBUG
[self writeConsoleLog:formattedLogMessage logModule:logModule logModuleName:logModuleName logLevel:logLevel];
#endif // DEBUG
// 2. Notify the shared log writer.
id<NLWeaveLogWriter> writer = gSharedWriter;
[writer writeLogFromModule:logModule level:logLevel message:formattedLogMessage];
}
#pragma mark - Private
#if DEBUG
/** Writes a log message to the system console. */
+ (void)writeConsoleLog:(NSString *)formattedLogMessage
logModule:(NLLogModule)logModule
logModuleName:(NSString *)logModuleName
logLevel:(NLLogLevel)logLevel
{
#if TARGET_OS_IPHONE
// For iOS 10+, OSLog replaces Apple System Logger (and NSLog).
if (IS_OPERATING_SYSTEM_AT_LEAST_VERSION(kMinimumOSLogOperatingSystemVersion)) {
os_log_t log = [NLOSLogStore logForModule:logModule name:logModuleName];
os_log_type_t type = (logLevel == NLLogLevelError ? OS_LOG_TYPE_ERROR : OS_LOG_TYPE_DEFAULT);
os_log_with_type(log, type, "%{public}s", formattedLogMessage.UTF8String);
return;
}
#endif // TARGET_OS_IPHONE
// Fallback to NSLog for both iOS <9 and non-iPhone targets.
NSLog(@"%@", formattedLogMessage);
}
#endif // DEBUG
@end
NS_ASSUME_NONNULL_END