Skip to content

Commit 08a9615

Browse files
Mohit TejaniMohit Tejani
authored andcommitted
Added support for instance level log configuration,new logging approach
1 parent 8055c49 commit 08a9615

23 files changed

Lines changed: 846 additions & 50 deletions

File tree

pubnub/lib/logging.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,12 @@
44
library pubnub.logging;
55

66
export 'src/logging/logging.dart' show LogRecord, StreamLogger;
7-
export 'src/core/logging/logging.dart' show Level, provideLogger, injectLogger;
7+
export 'src/core/logging/logging.dart'
8+
show
9+
Level,
10+
provideLogger,
11+
injectLogger,
12+
ILogger,
13+
LogEvent,
14+
LogEventDetailsType,
15+
globalLoggerRegistry;

pubnub/lib/pubnub.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,7 @@ export 'src/subscribe/subscription.dart' show Subscription;
129129
export 'src/subscribe/extensions/keyset.dart' show SubscribeKeysetExtension;
130130
export 'src/subscribe/envelope.dart'
131131
show Envelope, PresenceEvent, PresenceAction;
132+
133+
// Logging
134+
export 'src/logging/logging.dart' show LogRecord, StreamLogger;
135+
export 'src/core/logging/logging.dart' show Level, ILogger, LogEvent;

pubnub/lib/src/core/core.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Core {
2323

2424
static String version = '5.2.1';
2525

26+
static String instanceId =
27+
'${(DateTime.now().millisecondsSinceEpoch % 100000)}'.padLeft(5);
28+
2629
Core(
2730
{Keyset? defaultKeyset,
2831
required this.networking,

pubnub/lib/src/core/logging/logging.dart

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
import 'dart:async';
2+
import 'dart:mirrors';
3+
4+
import 'package:pubnub/pubnub.dart';
5+
import '../../networking/response/response.dart';
6+
import '../net/request_type.dart';
7+
import '../core.dart';
28

39
final _pubnubLoggerModuleKey = #pubnub.logging;
410

@@ -46,6 +52,9 @@ class LazyLogger implements ILogger {
4652
@override
4753
void shout(message) => logger.shout(message);
4854

55+
@override
56+
void fine(message) => logger.fine(message);
57+
4958
@override
5059
void silly(message) => logger.silly(message);
5160

@@ -62,10 +71,41 @@ class LazyLogger implements ILogger {
6271
///
6372
/// If there is no provider, returned logger will be a [DummyLogger].
6473
LazyLogger injectLogger(String id) {
65-
return LazyLogger(
66-
id, () => Zone.current[_pubnubLoggerModuleKey] ?? DummyLogger());
74+
return LazyLogger(id, () {
75+
// First check the current Zone for a logger (existing behavior)
76+
var zoneLogger = Zone.current[_pubnubLoggerModuleKey];
77+
if (zoneLogger != null) {
78+
return zoneLogger;
79+
}
80+
81+
// If no Zone logger, check for global PubNub logger (NEW)
82+
var globalLogger = _getGlobalPubNubLogger();
83+
if (globalLogger != null) {
84+
return globalLogger;
85+
}
86+
87+
// Fallback to dummy logger
88+
return DummyLogger();
89+
});
90+
}
91+
92+
/// Get the global PubNub logger if available
93+
ILogger? _getGlobalPubNubLogger() {
94+
try {
95+
for (var entry in globalLoggerRegistry.entries) {
96+
if (entry.key.startsWith('pubnub-')) {
97+
return entry.value;
98+
}
99+
}
100+
return null;
101+
} catch (e) {
102+
return null;
103+
}
67104
}
68105

106+
/// Global registry for loggers that can be accessed across the SDK
107+
final Map<String, ILogger> globalLoggerRegistry = {};
108+
69109
abstract class ILogger {
70110
ILogger get(String id);
71111
void log(int level, dynamic message);
@@ -75,6 +115,7 @@ abstract class ILogger {
75115
void severe(dynamic message) => log(Level.severe, message);
76116
void warning(dynamic message) => log(Level.warning, message);
77117
void info(dynamic message) => log(Level.info, message);
118+
void fine(dynamic message) => log(Level.fine, message);
78119
void verbose(dynamic message) => log(Level.verbose, message);
79120
void silly(dynamic message) => log(Level.silly, message);
80121
}
@@ -102,6 +143,9 @@ abstract class Level {
102143
/// Intended for informational messages.
103144
static final int info = 160;
104145

146+
/// Intended for the fine mode.
147+
static final int fine = 500;
148+
105149
/// Intended for the verbose mode.
106150
static final int verbose = 320;
107151

@@ -119,6 +163,7 @@ abstract class Level {
119163
80: 'warning',
120164
160: 'info',
121165
320: 'verbose',
166+
500: 'fine',
122167
640: 'silly',
123168
10000: 'all'
124169
};
@@ -130,3 +175,189 @@ abstract class Level {
130175
.value;
131176
}
132177
}
178+
179+
Map<String, dynamic> extractObjectProperties(Object obj,
180+
{List<String> skipProperties = const ['keyset']}) {
181+
final result = <String, dynamic>{};
182+
183+
try {
184+
final mirror = reflect(obj);
185+
final declarations = mirror.type.declarations;
186+
187+
for (var declaration in declarations.values) {
188+
if (declaration is VariableMirror && !declaration.isStatic) {
189+
final name = MirrorSystem.getName(declaration.simpleName);
190+
191+
if (skipProperties.contains(name)) {
192+
continue;
193+
}
194+
195+
try {
196+
final value = mirror.getField(declaration.simpleName).reflectee;
197+
result[name] = value;
198+
} catch (e) {
199+
result[name] = '<inaccessible>';
200+
}
201+
}
202+
}
203+
} catch (e) {
204+
// Fallback: try to convert to string representation
205+
result['toString'] = obj.toString();
206+
}
207+
208+
return result;
209+
}
210+
211+
/// Enum for different types of log event details
212+
enum LogEventDetailsType {
213+
pubNubInstanceInfo,
214+
apiParametersInfo,
215+
networkRequestInfo,
216+
networkResponseInfo,
217+
}
218+
219+
/// Enhanced LogEvent that can handle both Map and Object details
220+
class LogEvent {
221+
final String? message;
222+
final Object? details;
223+
final LogEventDetailsType? detailsType;
224+
225+
LogEvent({this.message, this.details, this.detailsType});
226+
227+
@override
228+
String toString() {
229+
var messageString = message ?? '';
230+
try {
231+
if (detailsType == LogEventDetailsType.pubNubInstanceInfo) {
232+
var pubnub = details as PubNub;
233+
var keysets = pubnub.keysets;
234+
235+
messageString += '\n\tPubNub Instance Information:';
236+
messageString += '\n\tInstance ID: ${Core.instanceId}';
237+
messageString += '\n\tVersion: ${Core.version}';
238+
239+
// Log module information
240+
messageString += '\n\tModules:';
241+
messageString +=
242+
'\n\t Networking: ${pubnub.networking.runtimeType.toString().split('.').last}';
243+
messageString +=
244+
'\n\t Parser: ${pubnub.parser.runtimeType.toString().split('.').last}';
245+
messageString +=
246+
'\n\t Crypto: ${pubnub.crypto.runtimeType.toString().split('.').last}';
247+
248+
// Log keyset information
249+
messageString += '\n\tKeysets:';
250+
var allKeysets = keysets.keysets;
251+
if (allKeysets.isEmpty) {
252+
messageString += '\n\t No keysets configured';
253+
} else {
254+
// Get all keyset names by trying to access them
255+
var keysetNames = <String>[];
256+
try {
257+
// Try to get the default keyset name by checking which one is the default
258+
var defaultKeyset = keysets.defaultKeyset;
259+
for (var keyset in allKeysets) {
260+
// Find the name by comparing with default
261+
if (keyset == defaultKeyset) {
262+
keysetNames.add('default');
263+
} else {
264+
// For non-default keysets, we'll use a generic name
265+
keysetNames.add('keyset_${keysetNames.length + 1}');
266+
}
267+
}
268+
} catch (e) {
269+
// If no default keyset, just number them
270+
for (var i = 0; i < allKeysets.length; i++) {
271+
keysetNames.add('keyset_${i + 1}');
272+
}
273+
}
274+
275+
for (var i = 0; i < allKeysets.length; i++) {
276+
var keyset = allKeysets[i];
277+
var name = keysetNames[i];
278+
messageString += '\n\t $name:';
279+
messageString += '\n\t Subscribe Key: ${keyset.subscribeKey}';
280+
messageString +=
281+
'\n\t Publish Key: ${keyset.publishKey ?? 'not provided'}';
282+
messageString +=
283+
'\n\t Secret Key: ${keyset.secretKey != null ? 'provided' : 'not provided'}';
284+
messageString += '\n\t User ID: ${keyset.userId}';
285+
messageString +=
286+
'\n\t Auth Key: ${keyset.authKey != null ? 'provided' : 'not provided'}';
287+
messageString +=
288+
'\n\t Cipher Key: ${keyset.cipherKey != null ? 'provided' : 'not provided'}';
289+
if (keyset.settings.isNotEmpty) {
290+
messageString += '\n\t Settings: ${keyset.settings}';
291+
}
292+
if (i == 0 && keysetNames[i] == 'default') {
293+
messageString += '\n\t (Default)';
294+
}
295+
}
296+
}
297+
} else if (detailsType == LogEventDetailsType.apiParametersInfo) {
298+
// Handle both Map and Object types
299+
Map<String, dynamic> detailsMap;
300+
if (details is Map) {
301+
detailsMap = details as Map<String, dynamic>;
302+
} else if (details != null) {
303+
// Extract properties from any object
304+
detailsMap = extractObjectProperties(details!);
305+
} else {
306+
detailsMap = {};
307+
}
308+
messageString +=
309+
'\n\t${detailsMap.entries.map((entry) => '${entry.key}: ${entry.value}').join('\n\t')}';
310+
} else if (detailsType == LogEventDetailsType.networkRequestInfo) {
311+
var requestMap = details as Map<String, dynamic>;
312+
313+
for (var entry in requestMap.entries) {
314+
switch (entry.key) {
315+
case 'type':
316+
var requestType = entry.value as RequestType;
317+
messageString += '\n\tMethod: ${requestType.method}';
318+
break;
319+
case 'uri':
320+
messageString += '\n\tURL: ${entry.value}';
321+
break;
322+
case 'headers':
323+
var headers = entry.value as Map<String, dynamic>;
324+
if (headers.isNotEmpty) {
325+
messageString += '\n\tHeaders:';
326+
for (var headerEntry in headers.entries) {
327+
messageString +=
328+
'\n\t ${headerEntry.key}: ${headerEntry.value}';
329+
}
330+
}
331+
break;
332+
case 'body':
333+
if (entry.value != null) {
334+
if (entry.value is List<int>) {
335+
var length = entry.value.length;
336+
messageString +=
337+
'\n\tBody:binary content with length $length';
338+
} else {
339+
messageString += '\n\tBody: ${entry.value}';
340+
}
341+
}
342+
break;
343+
}
344+
}
345+
} else if (detailsType == LogEventDetailsType.networkResponseInfo) {
346+
var responseMap = details as Map<String, dynamic>;
347+
messageString += '\n\tURL: ${responseMap['request'].uri}';
348+
messageString +=
349+
'\n\tStatus Code: ${responseMap['response'].statusCode}';
350+
var response = responseMap['response'] as Response;
351+
if (response.headers.containsKey('server')) {
352+
messageString +=
353+
'\n\tBody: binary content length ${response.byteList.length}';
354+
} else {
355+
messageString += '\n\tBody: ${response.text}';
356+
}
357+
}
358+
} catch (e) {
359+
messageString += '\n logging error: $e';
360+
}
361+
return messageString;
362+
}
363+
}

pubnub/lib/src/core/supervisor/fiber.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class Fiber<T> {
5353
return _completer.completeError(exception, stackTrace);
5454
}
5555

56-
__logger.silly('Possible reason found: $diagnostic');
56+
__logger.fine('Network issue possible reason found: $diagnostic');
5757

5858
var resolutions = _core.supervisor.runStrategies(this, diagnostic);
5959

0 commit comments

Comments
 (0)