diff --git a/README.md b/README.md
index 9659640..23b943a 100644
--- a/README.md
+++ b/README.md
@@ -90,6 +90,7 @@ Add the following inside the application manifest (inside ``):
* **LogseneRequiresUnmeteredNetwork**: if logs should be shipped only on unmetered network connection
* **LogseneRequiresDeviceIdle**: if logs should be shipped only when device is idle
* **LogseneRequiresBatteryNotLow**: if logs should be shipped only when battery is not low
+ * **LogseneAutomaticLocationEnabled**: if logs should be automatically enriched with device location information
Example Application
@@ -99,6 +100,22 @@ To see how some basic use cases are actually implemented, checkout the bundled `
**Note** that it's highly recommended that you use one instance of Logsene at any time in your app.
+Initializing Logsene
+--------------------
+
+Starting with version **3.0.0** of the Android library `Logsene` needs to be initialized in a **static** way in order to be used. This allows usage in classes that don't have `Context` available. To do that, you need to call the following in your application code. Keep in mind this is only needed **once**:
+
+```java
+Logsene.init(this);
+```
+
+To get the instance of the `Logsene` object that supports logging you need to call the `getInstance()` method, for example:
+
+```java
+Logsene logsene = Logsene.getInstance();
+logsene.info("Hello World!");
+```
+
Mobile Application Analytics
----------------------------
@@ -109,7 +126,7 @@ try {
JSONObject event = new JSONObject();
event.put("activity", this.getClass().getSimpleName());
event.put("action", "started");
- Logsene logsene = new Logsene(this);
+ Logsene logsene = Logsene.getInstance();
logsene.event(event);
} catch (JSONException e) {
Log.e("myapp", "Unable to construct json", e);
@@ -214,7 +231,8 @@ Because of the automatic retrieval of location from the device the `ACCESS_COARS
If your application uses JUL (java.util.logging) loggers, you can use the provided custom Handler for Logsene. You will need to configure it through code, since we need a reference to the `Context` object. If you configure your loggers to use the `LogseneHandler`, all log messages will be sent to Sematext for centralized logging.
```java
-Logsene logsene = new Logsene(context);
+Logsene.init(this);
+Logsene logsene = Logsene.getInstance();
Logger logger = Logger.getLogger("mylogger");
logger.addHandler(new LogseneHandler(logsene));
```
@@ -224,7 +242,8 @@ logger.addHandler(new LogseneHandler(logsene));
If you use JUL and the `LogseneHandler`, all logged exceptions will be sent to Sematext, no further configuration is needed. However, if you don't use JUL, the library provides a helper method to log exceptions:
```java
-Logsene logsene = new Logsene(context);
+Logsene.init(this);
+Logsene logsene = Logsene.getInstance();
try {
trySomeOperation();
} catch (IOException e) {
@@ -243,7 +262,8 @@ public class TestApplication extends Application {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
// Send uncaught exception to Logsene.
- Logsene logsene = new Logsene(TestApplication.this);
+ Logsene.init(TestApplication.this);
+ Logsene logsene = Logsene.getInstance();
logsene.error(ex);
// Run the default android handler if one is set
@@ -261,3 +281,28 @@ public class TestApplication extends Application {
```
Don't forget to declare the custom application class in your manifest (with `android:name` on `application` element).
+
+Migrating to version 3.x from 2.x
+---------------------------------
+
+Starting from version **3.0.0** Logsene Android SDK contains backwards incompatible changes related to how it is initilized. You no longer need to create the `Logsene` object everytime you would like to use it for logging. You no longer create the `Logsene` object itself like this:
+
+```java
+Logsene logsene = new Logsene(context, true);
+
+```
+
+Instead you call the `init(Context)` method once and retrieve the instance of `Logsene` object later:
+
+```java
+Logsene.init(context);
+Logsene logsene = Logsene.getInstance();
+```
+
+You also need to include the `LogseneAutomaticLocationEnabled` property in your manifest file to enable automatic log enrichment with location data:
+
+```xml
+
+```
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 4851699..94e5bca 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,7 +7,7 @@ android {
minSdkVersion 19
targetSdkVersion 28
versionCode 1
- versionName "2.5.0"
+ versionName "3.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 59ad7ab..293ed10 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -18,12 +18,10 @@
-
-
@@ -33,7 +31,9 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/java/com/sematext/logseneapplication/MainActivity.java b/app/src/main/java/com/sematext/logseneapplication/MainActivity.java
index 9463033..8ed7378 100644
--- a/app/src/main/java/com/sematext/logseneapplication/MainActivity.java
+++ b/app/src/main/java/com/sematext/logseneapplication/MainActivity.java
@@ -22,8 +22,8 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
-
- logsene = new Logsene(this, true);
+ Logsene.init(this);
+ logsene = logsene.getInstance();
Log.e("INFO", "Android version: " + Build.VERSION.RELEASE);
diff --git a/build.gradle b/build.gradle
index b0a10a1..033cb20 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.2'
+ classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8ce6d7d..a096c38 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Jun 03 17:02:22 CEST 2020
+#Wed Dec 16 21:30:23 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/logseneandroid/build.gradle b/logseneandroid/build.gradle
index 9a799a0..4d70c86 100644
--- a/logseneandroid/build.gradle
+++ b/logseneandroid/build.gradle
@@ -9,7 +9,7 @@ android {
minSdkVersion 19
targetSdkVersion 28
versionCode 4
- versionName "2.5.0"
+ versionName "3.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
diff --git a/logseneandroid/src/main/java/com/sematext/logseneandroid/Logsene.java b/logseneandroid/src/main/java/com/sematext/logseneandroid/Logsene.java
index f5ef2d5..5b05c78 100644
--- a/logseneandroid/src/main/java/com/sematext/logseneandroid/Logsene.java
+++ b/logseneandroid/src/main/java/com/sematext/logseneandroid/Logsene.java
@@ -58,9 +58,9 @@ public class Logsene {
*/
private static final int DEFAULT_TIME_INTERVAL = 15 * 60 * 1000;
-
private static JSONObject defaultMeta;
- private final Context context;
+ private static boolean initialized = false;
+ private static Logsene self;
private String versionName;
private Integer versionCode;
private String uuid;
@@ -84,78 +84,82 @@ public class Logsene {
private boolean sendRequiresDeviceIdle;
private boolean sendRequiresBatteryNotLow;
private boolean isActive;
+ private boolean automaticLocationEnabled;
private LogseneLocationListener locationListener;
- public Logsene(Context context) {
- this(context, false);
+ // Private constructor - no instances.
+ private Logsene() {
}
- public Logsene(Context context, boolean automaticLocationEnabled) {
- Utils.requireNonNull(context);
- this.context = context;
- this.uuid = Installation.id(context);
- config();
- this.preflightQueue = new SqliteObjectQueue(Logsene.this.context, maxOfflineMessages);
- this.lastScheduled = SystemClock.elapsedRealtime();
- if (automaticLocationEnabled) {
- this.locationListener = new LogseneLocationListener(context);
- }
- this.isActive = true;
- schedulePeriodicWorker();
- }
+ /**
+ * Initializes Logsene
object.
+ * @param context Context
+ */
+ public static void init(Context context) {
+ if (!Logsene.isInitialized()) {
+ Utils.requireNonNull(context);
+ Logsene logsene = new Logsene();
+ logsene.uuid = Installation.id(context);
+ logsene.config(context);
+ logsene.preflightQueue = new SqliteObjectQueue(context, logsene.maxOfflineMessages);
+ logsene.lastScheduled = SystemClock.elapsedRealtime();
+ if (logsene.automaticLocationEnabled) {
+ logsene.locationListener = new LogseneLocationListener(context);
+ }
+ logsene.isActive = true;
+ logsene.schedulePeriodicWorker();
- public long getQueueSize() {
- return preflightQueue.size();
+ // finally set the static values
+ Logsene.initialized = true;
+ Logsene.self = logsene;
+ }
}
- public LogseneLocationListener getLocationListener() {
- return locationListener;
+ /**
+ * Checks if the Logsene object is initialized.
+ * @return true
if initialized, false
otherwise
+ */
+ public static boolean isInitialized() {
+ return Logsene.initialized;
}
- private void config() {
- Bundle data = null;
- try {
- data = context.getPackageManager().getApplicationInfo(context.getPackageName(),
- PackageManager.GET_META_DATA).metaData;
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- // required fields
- if (!data.containsKey("LogseneAppToken")) {
- throw new RuntimeException("Please provide ");
- } else if (!data.containsKey("LogseneType")) {
- throw new RuntimeException("Please provide ");
+ /**
+ * Returns instance of Logsene
object if it is initialized.
+ * @return Logsene
instance
+ */
+ public static Logsene getInstance() {
+ if (Logsene.isInitialized()) {
+ return Logsene.self;
}
- appToken = data.getString("LogseneAppToken");
- type = data.getString("LogseneType");
+ throw new NullPointerException("Logsene is not initialized");
+ }
- // optional fields
- receiverUrl = data.getString("LogseneReceiverUrl", RECEIVER_URL);
- maxOfflineMessages = data.getInt("LogseneMaxOfflineMessages", DEFAULT_MAX_OFFLINE_MESSAGES);
- minTimeDelay = (long)(data.getInt("LogseneMinTimeDelay", DEFAULT_MIN_TIME_DELAY));
- timeInterval = (long)(data.getInt("LogseneInterval", DEFAULT_TIME_INTERVAL));
- sendRequiresUnmeteredNetwork = data.getBoolean("LogseneSendRequiresUnmeteredNetwork", false);
- sendRequiresDeviceIdle = data.getBoolean("LogseneSendRequiresDeviceIdle", false);
- sendRequiresBatteryNotLow = data.getBoolean("LogseneSendRequiresBatteryNotLow", false);
+ /**
+ * Sets the default meta properties. These will be included with every request.
+ * @param metadata the default meta properties, use null to disable.
+ */
+ public static void setDefaultMeta(JSONObject metadata) {
+ defaultMeta = metadata;
+ }
- Log.d(TAG, String.format("Logsene is configured:\n"
- + " Type: %s\n"
- + " Receiver URL: %s\n"
- + " Max Offline Messages: %d\n"
- + " Min Time Trigger: %d\n"
- + " Max Time Trigger: %d\n"
- + " Send logs only on unmetered network: %s\n"
- + " Send logs only when device is idle: %s\n"
- + " Send logs only when battery is not low: %s",
- type, receiverUrl, maxOfflineMessages, minTimeDelay, timeInterval,
- sendRequiresUnmeteredNetwork, sendRequiresDeviceIdle, sendRequiresBatteryNotLow));
+ /**
+ * Returns LogseneLocationListener
for location related functionality.
+ * @return LogseneLocationListener
+ */
+ public LogseneLocationListener getLocationListener() {
+ return locationListener;
}
+ /**
+ * Pauses sending of the data.
+ */
public void pause() {
this.isActive = false;
}
+ /**
+ * Resumes sending of the data.
+ */
public void resume() {
this.isActive = true;
}
@@ -343,17 +347,6 @@ public void event(JSONObject object) {
addToQueue(object);
}
- /**
- * Sets the default meta properties.
- *
- * These will be included with every request.
- *
- * @param metadata the default meta properties, use null to disable.
- */
- public static void setDefaultMeta(JSONObject metadata) {
- defaultMeta = metadata;
- }
-
/**
* Flush the message queue.
*
@@ -366,6 +359,60 @@ public void flushMessageQueue() {
scheduleUnconstrainedWorker();
}
+ private void config(Context context) {
+ Bundle data = null;
+ try {
+ data = context.getPackageManager().getApplicationInfo(context.getPackageName(),
+ PackageManager.GET_META_DATA).metaData;
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ // required fields
+ if (!data.containsKey("LogseneAppToken")) {
+ throw new RuntimeException("Please provide ");
+ } else if (!data.containsKey("LogseneType")) {
+ throw new RuntimeException("Please provide ");
+ }
+ appToken = data.getString("LogseneAppToken");
+ type = data.getString("LogseneType");
+
+ // retrieve version name and version code
+ PackageInfo pInfo = null;
+ try {
+ pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+ versionName = pInfo.versionName;
+ versionCode = pInfo.versionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, e.getMessage(), e);
+ versionName = "n/a";
+ versionCode = -1;
+ }
+
+ // optional fields
+ receiverUrl = data.getString("LogseneReceiverUrl", RECEIVER_URL);
+ maxOfflineMessages = data.getInt("LogseneMaxOfflineMessages", DEFAULT_MAX_OFFLINE_MESSAGES);
+ minTimeDelay = (long)(data.getInt("LogseneMinTimeDelay", DEFAULT_MIN_TIME_DELAY));
+ timeInterval = (long)(data.getInt("LogseneInterval", DEFAULT_TIME_INTERVAL));
+ sendRequiresUnmeteredNetwork = data.getBoolean("LogseneSendRequiresUnmeteredNetwork", false);
+ sendRequiresDeviceIdle = data.getBoolean("LogseneSendRequiresDeviceIdle", false);
+ sendRequiresBatteryNotLow = data.getBoolean("LogseneSendRequiresBatteryNotLow", false);
+ automaticLocationEnabled = data.getBoolean("LogseneAutomaticLocationEnabled", false);
+
+ Log.d(TAG, String.format("Logsene is configured:\n"
+ + " Type: %s\n"
+ + " Receiver URL: %s\n"
+ + " Max Offline Messages: %d\n"
+ + " Min Time Trigger: %d\n"
+ + " Max Time Trigger: %d\n"
+ + " Automatic location enabled: %b\n"
+ + " Send logs only on unmetered network: %s\n"
+ + " Send logs only when device is idle: %s\n"
+ + " Send logs only when battery is not low: %s",
+ type, receiverUrl, maxOfflineMessages, minTimeDelay, timeInterval, automaticLocationEnabled,
+ sendRequiresUnmeteredNetwork, sendRequiresDeviceIdle, sendRequiresBatteryNotLow));
+ }
+
private Data getWorkerData() {
return new Data.Builder()
.putString(KEY_RECEIVERURL, receiverUrl)
@@ -451,34 +498,10 @@ private void addToQueue(JSONObject obj) {
}
private String getVersionName() {
- if (versionName == null) {
- PackageInfo pInfo = null;
- try {
- pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- versionName = pInfo.versionName;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, e.getMessage(), e);
- // set to n/a so we don't try again
- versionName = "n/a";
- }
- }
-
return versionName;
}
private int getVersionCode() {
- if (versionCode == null) {
- PackageInfo pInfo = null;
- try {
- pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- versionCode = pInfo.versionCode;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, e.getMessage(), e);
- // set to n/a so we don't try again
- versionCode = -1;
- }
- }
-
return versionCode;
}