xref: /aosp_15_r20/external/executorch/extension/apple/ExecuTorch/Exported/ExecuTorchLog.mm (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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