1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. 7 */ 8 9#import "ExecuTorchLog.h" 10 11#import <os/log.h> 12 13#import <executorch/runtime/platform/log.h> 14#import <executorch/runtime/platform/platform.h> 15 16@interface ExecuTorchLog () 17 18- (void)logWithLevel:(ExecuTorchLogLevel)level 19 timestamp:(NSTimeInterval)timestamp 20 filename:(NSString *)filename 21 line:(NSUInteger)line 22 message:(NSString *)message; 23 24@end 25 26@implementation ExecuTorchLog { 27#ifdef ET_LOG_ENABLED 28 NSHashTable<id<ExecuTorchLogSink>> *_sinks; 29 dispatch_queue_t _queue; 30 NSMutableArray<NSDictionary *> *_buffer; 31#endif 32} 33 34+ (instancetype)sharedLog { 35 static ExecuTorchLog *sharedLog; 36 static dispatch_once_t onceToken; 37 dispatch_once(&onceToken, ^{ 38 sharedLog = [self new]; 39#if ET_LOG_ENABLED 40 sharedLog->_sinks = [NSHashTable weakObjectsHashTable]; 41 sharedLog->_queue = dispatch_queue_create("org.pytorch.executorch.log", 42 DISPATCH_QUEUE_SERIAL); 43 sharedLog->_buffer = [NSMutableArray new]; 44#endif 45 }); 46 return sharedLog; 47} 48 49- (void)addSink:(id<ExecuTorchLogSink>)sink { 50#if ET_LOG_ENABLED 51 dispatch_async(_queue, ^{ 52 [self->_sinks addObject:sink]; 53 for (NSDictionary *log in self->_buffer) { 54 [sink logWithLevel:(ExecuTorchLogLevel)[log[@"level"] integerValue] 55 timestamp:[log[@"timestamp"] doubleValue] 56 filename:log[@"filename"] ?: @"" 57 line:[log[@"line"] unsignedIntegerValue] 58 message:log[@"message"] ?: @""]; 59 } 60 }); 61#else 62 (void)sink; 63#endif 64} 65 66- (void)removeSink:(id<ExecuTorchLogSink>)sink { 67#if ET_LOG_ENABLED 68 dispatch_async(_queue, ^{ 69 [self->_sinks removeObject:sink]; 70 }); 71#else 72 (void)sink; 73#endif 74} 75 76#pragma mark - Private 77 78- (void)logWithLevel:(ExecuTorchLogLevel)level 79 timestamp:(NSTimeInterval)timestamp 80 filename:(NSString *)filename 81 line:(NSUInteger)line 82 message:(NSString *)message { 83#if ET_LOG_ENABLED 84 NSHashTable<id<ExecuTorchLogSink>> __block *sinks; 85 dispatch_sync(_queue, ^{ 86 sinks = [self->_sinks copy]; 87 if (self->_buffer.count >= 100) { 88 [self->_buffer removeObjectAtIndex:0]; 89 } 90 [self->_buffer addObject:@{ 91 @"level" : @(level), 92 @"timestamp" : @(timestamp), 93 @"filename" : filename, 94 @"line" : @(line), 95 @"message" : message 96 }]; 97 }); 98 for (id<ExecuTorchLogSink> sink in sinks) { 99 [sink logWithLevel:level 100 timestamp:timestamp 101 filename:filename 102 line:line 103 message:message]; 104 } 105#else 106 (void)level; 107 (void)timestamp; 108 (void)filename; 109 (void)line; 110 (void)message; 111#endif 112} 113 114@end 115 116void et_pal_emit_log_message(et_timestamp_t timestamp, 117 et_pal_log_level_t level, 118 const char *__nonnull filename, 119 ET_UNUSED const char *function, 120 size_t line, 121 const char *__nonnull message, 122 ET_UNUSED size_t length) { 123#if ET_LOG_ENABLED 124 NSTimeInterval timeInterval = timestamp / 1000000000.0; 125 NSUInteger totalSeconds = (NSUInteger)timeInterval; 126 NSUInteger hours = (totalSeconds / 3600) % 24; 127 NSUInteger minutes = (totalSeconds / 60) % 60; 128 NSUInteger seconds = totalSeconds % 60; 129 NSUInteger microseconds = (timestamp - totalSeconds) * 1000000; 130 NSString *formattedMessage = [NSString 131 stringWithFormat:@"%c %02lu:%02lu:%02lu.%06lu executorch:%s:%zu] %s", 132 (char)level, 133 hours, 134 minutes, 135 seconds, 136 microseconds, 137 filename, 138 line, 139 message]; 140 os_log_type_t logType = OS_LOG_TYPE_DEFAULT; 141 switch (level) { 142 case kDebug: 143 logType = OS_LOG_TYPE_DEBUG; 144 break; 145 case kInfo: 146 logType = OS_LOG_TYPE_INFO; 147 break; 148 case kError: 149 logType = OS_LOG_TYPE_ERROR; 150 break; 151 case kFatal: 152 logType = OS_LOG_TYPE_FAULT; 153 break; 154 default: 155 logType = OS_LOG_TYPE_DEFAULT; 156 break; 157 } 158 os_log_with_type(OS_LOG_DEFAULT, logType, "%{public}@", formattedMessage); 159 160 [ExecuTorchLog.sharedLog 161 logWithLevel:(ExecuTorchLogLevel)level 162 timestamp:timeInterval 163 filename:[NSString stringWithUTF8String:filename] 164 line:(NSUInteger)line 165 message:[NSString stringWithUTF8String:message]]; 166#else 167 (void)timestamp; 168 (void)level; 169 (void)filename; 170 (void)function; 171 (void)line; 172 (void)message; 173 (void)length; 174#endif 175} 176