From b70cc9e4e0d184fc927fcb5c0cc435bf35888f19 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Mon, 27 Oct 2025 22:13:57 +0600 Subject: [PATCH 1/6] chore: add new classes for logging - Add FlutterLogbackAppender.java for logging events in the Flutter app - Integrate OptimizelyFlutterSdkPlugin with FlutterLogbackAppender - Update Constants.swift, SwiftOptimizelyFlutterSdkPlugin.swift, and related classes --- .../FlutterLogbackAppender.java | 42 +++++++++++++++++++ .../OptimizelyFlutterSdkPlugin.java | 29 +++++++++++++ ios/Classes/HelperClasses/Constants.swift | 1 - .../SwiftOptimizelyFlutterSdkPlugin.swift | 13 ++---- lib/optimizely_flutter_sdk.dart | 12 +----- lib/src/optimizely_client_wrapper.dart | 1 - lib/src/utils/constants.dart | 1 - 7 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java new file mode 100644 index 0000000..d20083a --- /dev/null +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java @@ -0,0 +1,42 @@ +package com.optimizely.optimizely_flutter_sdk; + +import android.os.Handler; +import android.os.Looper; + +import java.util.HashMap; +import java.util.Map; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import io.flutter.plugin.common.MethodChannel; + +public class FlutterLogbackAppender extends AppenderBase { + + public static final String CHANNEL_NAME = "optimizely_flutter_sdk_logger"; + public static MethodChannel channel; + private static final Handler mainThreadHandler = new Handler(Looper.getMainLooper()); + + public static void setChannel(MethodChannel channel) { + FlutterLogbackAppender.channel = channel; + } + + @Override + protected void append(ILoggingEvent event) { + if (channel == null) { + return; + } + + String message = event.getFormattedMessage(); + int level = event.getLevel().toInt(); + + Map logData = new HashMap<>(); + logData.put("level", level); + logData.put("message", message); + + mainThreadHandler.post(() -> { + if (channel != null) { + channel.invokeMethod("log", logData); + } + }); + } +} diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java index 89f787c..be0575b 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java @@ -32,10 +32,19 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; + + /** OptimizelyFlutterSdkPlugin */ public class OptimizelyFlutterSdkPlugin extends OptimizelyFlutterClient implements FlutterPlugin, ActivityAware, MethodCallHandler { public static MethodChannel channel; + private Appender flutterLogbackAppender; @Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { @@ -157,11 +166,31 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "optimizely_flutter_sdk"); channel.setMethodCallHandler(this); context = binding.getApplicationContext(); + + MethodChannel loggerChannel = new MethodChannel(binding.getBinaryMessenger(), FlutterLogbackAppender.CHANNEL_NAME); + FlutterLogbackAppender.setChannel(loggerChannel); + + // Add appender to logback + flutterLogbackAppender = new FlutterLogbackAppender(); + LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); + flutterLogbackAppender.setContext(lc); + flutterLogbackAppender.start(); + Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.addAppender(flutterLogbackAppender); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); + // Stop and detach the appender + if (flutterLogbackAppender != null) { + Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.detachAppender(flutterLogbackAppender); + flutterLogbackAppender.stop(); + flutterLogbackAppender = null; + } + // Clean up the channel + FlutterLogbackAppender.setChannel(null); } @Override diff --git a/ios/Classes/HelperClasses/Constants.swift b/ios/Classes/HelperClasses/Constants.swift index 1b0cbac..a29370a 100644 --- a/ios/Classes/HelperClasses/Constants.swift +++ b/ios/Classes/HelperClasses/Constants.swift @@ -91,7 +91,6 @@ struct RequestParameterKey { static let reasons = "reasons" static let decideOptions = "optimizelyDecideOption" static let defaultLogLevel = "defaultLogLevel" - static let useCustomLogger = "useCustomLogger" static let eventBatchSize = "eventBatchSize" static let eventTimeInterval = "eventTimeInterval" static let eventMaxQueueSize = "eventMaxQueueSize" diff --git a/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift b/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift index 75ba8b9..be81576 100644 --- a/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift +++ b/ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift @@ -172,15 +172,10 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { notificationIdsTracker.removeValue(forKey: sdkKey) optimizelyClientsTracker.removeValue(forKey: sdkKey) - // Check if custom logger is requested - var logger: OPTLogger? - if let useCustomLogger = parameters[RequestParameterKey.useCustomLogger] as? Bool, useCustomLogger { - // OptimizelyFlutterLogger bridges iOS logs to Flutter via Method Channel - // When useCustomLogger = true: - // iOS SDK log → OptimizelyFlutterLogger → Flutter Method Channel → Flutter console - logger = OptimizelyFlutterLogger() - } - + // OptimizelyFlutterLogger bridges iOS logs to Flutter via Method Channel + // iOS SDK log → OptimizelyFlutterLogger → Flutter Method Channel → Flutter console + var logger: OPTLogger = OptimizelyFlutterLogger() + // Creating new instance let optimizelyInstance = OptimizelyClient( sdkKey:sdkKey, diff --git a/lib/optimizely_flutter_sdk.dart b/lib/optimizely_flutter_sdk.dart index a1ff583..cc7d11d 100644 --- a/lib/optimizely_flutter_sdk.dart +++ b/lib/optimizely_flutter_sdk.dart @@ -73,11 +73,6 @@ class OptimizelyFlutterSdk { final OptimizelyLogLevel _defaultLogLevel; final SDKSettings _sdkSettings; static OptimizelyLogger? _customLogger; - /// Set a custom logger for the SDK - static void setLogger(OptimizelyLogger logger) { - _customLogger = logger; - LoggerBridge.initialize(logger); - } /// Get the current logger static OptimizelyLogger? get logger { return _customLogger; @@ -97,11 +92,8 @@ class OptimizelyFlutterSdk { _defaultLogLevel = defaultLogLevel, _sdkSettings = sdkSettings { // Set the logger if provided - if (logger != null) { - setLogger(logger); - } else { - logWarning("Logger not provided."); - } + _customLogger = logger ?? DefaultOptimizelyLogger(); + LoggerBridge.initialize(_customLogger); } /// Starts Optimizely SDK (Synchronous) with provided sdkKey. diff --git a/lib/src/optimizely_client_wrapper.dart b/lib/src/optimizely_client_wrapper.dart index 8caa22c..a7c092d 100644 --- a/lib/src/optimizely_client_wrapper.dart +++ b/lib/src/optimizely_client_wrapper.dart @@ -80,7 +80,6 @@ class OptimizelyClientWrapper { Constants.eventBatchSize: eventOptions.batchSize, Constants.eventTimeInterval: eventOptions.timeInterval, Constants.eventMaxQueueSize: eventOptions.maxQueueSize, - Constants.useCustomLogger: logger != null, }; // Odp Request params diff --git a/lib/src/utils/constants.dart b/lib/src/utils/constants.dart index 8a1586e..2bb5421 100644 --- a/lib/src/utils/constants.dart +++ b/lib/src/utils/constants.dart @@ -86,7 +86,6 @@ class Constants { static const String optimizelyDecideOption = "optimizelyDecideOption"; static const String optimizelySegmentOption = "optimizelySegmentOption"; static const String optimizelySdkSettings = "optimizelySdkSettings"; - static const String useCustomLogger = 'useCustomLogger'; static const String defaultLogLevel = "defaultLogLevel"; static const String payload = "payload"; static const String value = "value"; From 6620481f6b87660348ea1e712be15aba37d60da4 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 28 Oct 2025 16:28:38 +0600 Subject: [PATCH 2/6] refactor: update log level conversion logic - Adjust method to correctly convert log level strings to integers - Refactor switch statement for better readability and maintainability --- .../FlutterLogbackAppender.java | 35 +++++++++++++++++-- .../OptimizelyFlutterSdkPlugin.java | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java index d20083a..bb6c2e2 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java @@ -1,4 +1,5 @@ package com.optimizely.optimizely_flutter_sdk; +import com.optimizely.optimizely_flutter_sdk.helper_classes.Constants; import android.os.Handler; import android.os.Looper; @@ -27,10 +28,12 @@ protected void append(ILoggingEvent event) { } String message = event.getFormattedMessage(); - int level = event.getLevel().toInt(); - + String level = event.getLevel().toString(); + // print level here + System.out.println("loglevel: " + level); + int logLevel = convertLogLevel(level); Map logData = new HashMap<>(); - logData.put("level", level); + logData.put("level", logLevel); logData.put("message", message); mainThreadHandler.post(() -> { @@ -39,4 +42,30 @@ protected void append(ILoggingEvent event) { } }); } + + int convertLogLevel(String logLevel) { + int level = 3; // Default to INFO + + if (logLevel == null || logLevel.isEmpty()) { + return level; + } + + switch (logLevel.toLowerCase()) { + case Constants.LogLevel.ERROR: + level = 1; + break; + case Constants.LogLevel.WARNING: + level = 2; + break; + case Constants.LogLevel.INFO: + level = 3; + break; + case Constants.LogLevel.DEBUG: + level = 4; + break; + default: { + } + } + return level; + } } diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java index be0575b..5ca2d8e 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.java @@ -176,6 +176,7 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { flutterLogbackAppender.setContext(lc); flutterLogbackAppender.start(); Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + rootLogger.setLevel(ch.qos.logback.classic.Level.ALL); rootLogger.addAppender(flutterLogbackAppender); } From d6287327fae20d8aa315f71ff8bfcee36ad5bc99 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 28 Oct 2025 18:10:31 +0600 Subject: [PATCH 3/6] refactor: optimize log level conversion - Remove unnecessary default log level value initialization - Refactor switch statement to directly return log levels for each case - Simplify comparison for warning log levels to include both "WARN" and "WARNING" --- .../FlutterLogbackAppender.java | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java index bb6c2e2..ac07b7a 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java @@ -29,8 +29,6 @@ protected void append(ILoggingEvent event) { String message = event.getFormattedMessage(); String level = event.getLevel().toString(); - // print level here - System.out.println("loglevel: " + level); int logLevel = convertLogLevel(level); Map logData = new HashMap<>(); logData.put("level", logLevel); @@ -44,28 +42,22 @@ protected void append(ILoggingEvent event) { } int convertLogLevel(String logLevel) { - int level = 3; // Default to INFO - if (logLevel == null || logLevel.isEmpty()) { - return level; + return 3; } - switch (logLevel.toLowerCase()) { - case Constants.LogLevel.ERROR: - level = 1; - break; - case Constants.LogLevel.WARNING: - level = 2; - break; - case Constants.LogLevel.INFO: - level = 3; - break; - case Constants.LogLevel.DEBUG: - level = 4; - break; - default: { - } + switch (logLevel.toUpperCase()) { + case "ERROR": + return 1; + case "WARN": + case "WARNING": + return 2; + case "INFO": + return 3; + case "DEBUG": + return 4; + default: + return 3; } - return level; } } From e1faba9ce2c3cc26aa6624ad368153d1ebfe69e7 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 28 Oct 2025 18:35:45 +0600 Subject: [PATCH 4/6] style: update logback configuration and log levels - Comment out configuration block in logback.xml - Modify log level 'WARNING' to 'WARN' in FlutterLogbackAppender.java --- android/src/main/assets/logback.xml | 4 ++-- .../optimizely_flutter_sdk/FlutterLogbackAppender.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/android/src/main/assets/logback.xml b/android/src/main/assets/logback.xml index 8e6e0d6..947713d 100644 --- a/android/src/main/assets/logback.xml +++ b/android/src/main/assets/logback.xml @@ -1,4 +1,4 @@ - - + --> diff --git a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java index ac07b7a..2252cdd 100644 --- a/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java +++ b/android/src/main/java/com/optimizely/optimizely_flutter_sdk/FlutterLogbackAppender.java @@ -50,7 +50,6 @@ int convertLogLevel(String logLevel) { case "ERROR": return 1; case "WARN": - case "WARNING": return 2; case "INFO": return 3; From b6046130bfd1e673d96a4fe92decda47278bc537 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 28 Oct 2025 19:07:10 +0600 Subject: [PATCH 5/6] fix: ensure debugging logs are only printed in debug mode - Add check for kDebugMode to control printing of logs --- lib/src/logger/flutter_logger.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/logger/flutter_logger.dart b/lib/src/logger/flutter_logger.dart index ad3ec67..f561ed9 100644 --- a/lib/src/logger/flutter_logger.dart +++ b/lib/src/logger/flutter_logger.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:optimizely_flutter_sdk/src/data_objects/log_level.dart'; abstract class OptimizelyLogger { @@ -8,7 +9,9 @@ abstract class OptimizelyLogger { class DefaultOptimizelyLogger implements OptimizelyLogger { @override void log(OptimizelyLogLevel level, String message) { - print('[OPTIMIZELY] [${level.name.toUpperCase()}]: $message'); + if (kDebugMode) { + print('[OPTIMIZELY] [${level.name.toUpperCase()}]: $message'); + } } } From 9326b6ba21496ff1b7224445a23dc9997955cf88 Mon Sep 17 00:00:00 2001 From: muzahidul-opti Date: Tue, 28 Oct 2025 23:39:42 +0600 Subject: [PATCH 6/6] chore: update logback.xml - Comment out old logback configuration - Add note explaining the use of FlutterLogbackAppender for logging --- android/src/main/assets/logback.xml | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/android/src/main/assets/logback.xml b/android/src/main/assets/logback.xml index 947713d..7f531a8 100644 --- a/android/src/main/assets/logback.xml +++ b/android/src/main/assets/logback.xml @@ -1,18 +1 @@ - +