Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FSSDK-9430] feat: add configurable log level support #63

Merged
merged 9 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,16 @@ android {
dependencies {
implementation 'androidx.multidex:multidex:2.0.0'
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
implementation group: 'org.slf4j', name: 'slf4j-android', version: '1.7.25'

jaeopt marked this conversation as resolved.
Show resolved Hide resolved
//"logback-android" required for programmatic control of global sl4j log level.
// - default log configuration in /assets/logback.xml
// - [ref] https://github.com/tony19/logback-android
implementation 'com.github.tony19:logback-android:3.0.0'
implementation 'org.slf4j:slf4j-api:2.0.7'

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10"
implementation "com.optimizely.ab:android-sdk:4.0.0-beta2"
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4'
implementation ('com.google.guava:guava:19.0') {
exclude group:'com.google.guava', module:'listenablefuture'
}
Expand Down
18 changes: 18 additions & 0 deletions android/src/main/assets/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<configuration
xmlns="https://tony19.github.io/logback-android/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://tony19.github.io/logback-android/xml https://cdn.jsdelivr.net/gh/tony19/logback-android/logback.xsd"
>
<appender name="logcat" class="ch.qos.logback.classic.android.LogcatAppender">
<tagEncoder>
<pattern>Optimizely</pattern>
</tagEncoder>
<encoder>
<pattern>%msg</pattern>
</encoder>
</appender>

<root level="DEBUG">
<appender-ref ref="logcat" />
</root>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ protected void initializeOptimizely(@NonNull ArgumentsParser argumentsParser, @N
maxQueueSize = argumentsParser.getEventMaxQueueSize();
}

Utils.setDefaultLogLevel(argumentsParser.getDefaultLogLevel());

DefaultEventHandler eventHandler = DefaultEventHandler.getInstance(context);
eventHandler.setDispatchInterval(-1L);
NotificationCenter notificationCenter = new NotificationCenter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public List<OptimizelyDecideOption> getDecideOptions() {
return Utils.getDecideOptions((List<String>) arguments.get(Constants.RequestParameterKey.DECIDE_OPTIONS));
}

public String getDefaultLogLevel() {
return (String) arguments.get(Constants.RequestParameterKey.DEFAULT_LOG_LEVEL);
}

public String getFlagKey() {
return (String) arguments.get(Constants.RequestParameterKey.FLAG_KEY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public static class RequestParameterKey {
public static final String ATTRIBUTES = "attributes";
public static final String DECIDE_KEYS = "keys";
public static final String DECIDE_OPTIONS = "optimizelyDecideOption";
public static final String DEFAULT_LOG_LEVEL = "defaultLogLevel";
public static final String EVENT_BATCH_SIZE = "eventBatchSize";
public static final String EVENT_TIME_INTERVAL = "eventTimeInterval";
public static final String EVENT_MAX_QUEUE_SIZE = "eventMaxQueueSize";
Expand Down Expand Up @@ -153,4 +154,11 @@ public static class SegmentOption {
public static final String IGNORE_CACHE = "ignoreCache";
public static final String RESET_CACHE = "resetCache";
}

public static class LogLevel {
public static final String ERROR = "error";
public static final String WARNING = "warning";
public static final String INFO = "info";
public static final String DEBUG = "debug";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import androidx.annotation.Nullable;

import static com.optimizely.ab.notification.DecisionNotification.FeatureVariableDecisionNotificationBuilder.SOURCE_INFO;

Expand All @@ -31,6 +32,9 @@
import com.optimizely.ab.notification.UpdateConfigNotification;
import com.optimizely.ab.odp.ODPSegmentOption;
import com.optimizely.ab.optimizelydecision.OptimizelyDecideOption;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

public class Utils {

Expand Down Expand Up @@ -104,4 +108,33 @@ public static Class getNotificationListenerType(String notificationType) {
}
return listenerClass;
}

// SLF4J log level control:
// - logback logger (ch.qos.logback) is the only option available that supports global log level control programmatically (not only via configuration file)
// - "logback-android" logger (com.github.tony19:logback-android) is integrated in build.gradle.
// - log-level control is not integrated into the native android-sdk core since this solution depends on logback logger.

public static void setDefaultLogLevel(@Nullable String logLevel) {
Level defaultLogLevel = Utils.mapLogLevel(logLevel);
Logger rootLogger = (Logger) LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(defaultLogLevel);
}

public static Level mapLogLevel(@Nullable String logLevel) {
Level level = Level.INFO;

if (logLevel == null || logLevel.isEmpty()) {
return level;
}

switch (logLevel) {
case Constants.LogLevel.ERROR: level = Level.ERROR; break;
case Constants.LogLevel.WARNING: level = Level.WARN; break;
case Constants.LogLevel.INFO: level = Level.INFO; break;
case Constants.LogLevel.DEBUG: level = Level.DEBUG; break;
default: {}
}
return level;
}

}
4 changes: 2 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 32
ndkVersion flutter.ndkVersion

compileOptions {
Expand All @@ -40,7 +40,7 @@ android {
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
targetSdkVersion 32
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '10.0'
platform :ios, '11.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
1 change: 1 addition & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class _MyAppState extends State<MyApp> {
datafilePeriodicDownloadInterval: 10 * 60,
eventOptions: const EventOptions(
batchSize: 1, timeInterval: 60, maxQueueSize: 10000),
defaultLogLevel: OptimizelyLogLevel.debug,
defaultDecideOptions: defaultOptions);
var response = await flutterSDK.initializeClient();

Expand Down
62 changes: 61 additions & 1 deletion example/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
90B9B075FD9D5B075E83BE96 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -52,9 +53,10 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
019D04A8C81D7599D2E638FA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* optimizely_flutter_sdk_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "optimizely_flutter_sdk_example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* optimizely_flutter_sdk_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = optimizely_flutter_sdk_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
Expand All @@ -66,7 +68,10 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
7EEA78DE428A25C4C9195EC6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
8D05B77804EBF39FD6D6D079 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand All @@ -75,6 +80,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
90B9B075FD9D5B075E83BE96 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -99,6 +105,7 @@
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
F74646C1D5F338EF32BE10E1 /* Pods */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -148,22 +155,36 @@
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F74646C1D5F338EF32BE10E1 /* Pods */ = {
isa = PBXGroup;
children = (
7EEA78DE428A25C4C9195EC6 /* Pods-Runner.debug.xcconfig */,
8D05B77804EBF39FD6D6D079 /* Pods-Runner.release.xcconfig */,
019D04A8C81D7599D2E638FA /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
834CC0346DE4E8E368DFDFD4 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
F96AA93D7120056FD027BD50 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -270,6 +291,45 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
834CC0346DE4E8E368DFDFD4 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F96AA93D7120056FD027BD50 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down
3 changes: 3 additions & 0 deletions example/macos/Runner.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ios/Classes/HelperClasses/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ struct RequestParameterKey {
static let eventTags = "eventTags"
static let reasons = "reasons"
static let decideOptions = "optimizelyDecideOption"
static let defaultLogLevel = "defaultLogLevel"
static let eventBatchSize = "eventBatchSize"
static let eventTimeInterval = "eventTimeInterval"
static let eventMaxQueueSize = "eventMaxQueueSize"
Expand Down
13 changes: 13 additions & 0 deletions ios/Classes/HelperClasses/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,17 @@ public class Utils: NSObject {
return nil
}
}

static func getDefaultLogLevel(_ logLevel: String) -> OptimizelyLogLevel {
var defaultLogLevel: OptimizelyLogLevel
switch logLevel {
case "error": defaultLogLevel = OptimizelyLogLevel.error
case "warning": defaultLogLevel = OptimizelyLogLevel.warning
case "info": defaultLogLevel = OptimizelyLogLevel.info
case "debug": defaultLogLevel = OptimizelyLogLevel.debug
default: defaultLogLevel = OptimizelyLogLevel.info
}
return defaultLogLevel;
}

}
16 changes: 14 additions & 2 deletions ios/Classes/SwiftOptimizelyFlutterSdkPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin {
decideOptions = options
}
let defaultDecideOptions = Utils.getDecideOptions(options: decideOptions)


var defaultLogLevel = OptimizelyLogLevel.info
if let logLevel = parameters[RequestParameterKey.defaultLogLevel] as? String {
defaultLogLevel = Utils.getDefaultLogLevel(logLevel)
}

// SDK Settings Default Values
var segmentsCacheSize: Int = 100
var segmentsCacheTimeoutInSecs: Int = 600
Expand Down Expand Up @@ -152,7 +157,14 @@ public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin {
optimizelyClientsTracker.removeValue(forKey: sdkKey)

// Creating new instance
let optimizelyInstance = OptimizelyClient(sdkKey:sdkKey, eventDispatcher: eventDispatcher, datafileHandler: datafileHandler, periodicDownloadInterval: datafilePeriodicDownloadInterval, defaultDecideOptions: defaultDecideOptions, settings: optimizelySdkSettings)
let optimizelyInstance = OptimizelyClient(
sdkKey:sdkKey,
eventDispatcher: eventDispatcher,
datafileHandler: datafileHandler,
periodicDownloadInterval: datafilePeriodicDownloadInterval,
defaultLogLevel: defaultLogLevel,
defaultDecideOptions: defaultDecideOptions,
settings: optimizelySdkSettings)

optimizelyInstance.start{ [weak self] res in
switch res {
Expand Down
Loading