Puree
Description
Puree is a log collector which provides the following features:
- Filtering: Enable to interrupt process before sending log. You can add common params to logs, or the sampling of logs.
- Buffering: Store logs to buffers and send them later.
- Batching: Send logs in a single request with
PureeBufferedOutput
. - Retrying: Retry to send logs after backoff time if sending logs fails.
Puree helps you unify your logging infrastructure.
Installation
This is published on jcenter
and you can use Puree as:
// build.gradle buildscript {
repositories {
jcenter()
}
...
}
// app/build.gradle dependencies {
compile 'com.cookpad.puree:puree:4.1.1'
}
Usage
Initialize
Configure Puree with PureeConfiguration
in Application#onCreate()
, which registers pairs of what and where.
public class MyApplication extends Application {
@Override
public void onCreate() {
Puree.initialize(buildConfiguration(this));
}
public static PureeConfiguration buildConfiguration(Context context) {
PureeFilter addEventTimeFilter = new AddEventTimeFilter();
return new PureeConfiguration.Builder(context)
.executor(Executors.newScheduledThreadPool(1)) // optional
.register(ClickLog.class, new OutLogcat())
.register(ClickLog.class, new OutBufferedLogcat().withFilters(addEventTimeFilter))
.build();
}
}
See also: demo/PureeConfigurator.java
Definition of PureeLog objects
A log class is required to implement PureeLog
, which is a marker interface just like as Serializable
, to serialize logs with Gson
.
public class ClickLog implements PureeLog {
@SerializedName("page")
private String page;
@SerializedName("label")
private String label;
public ClickLog(String page, String label) {
this.page = page;
this.label = label;
}
}
You can use Puree.send()
to send these logs to registered output plugins:
Puree.send(new ClickLog("MainActivity", "Hello"));
// => {
"page":"MainActivity","label":"Hello"
}
Definition of PureeOutput plugins
There are two types of output plugins: non-buffered and buffered.
PureeOutput
: Non-buffered output plugins write logs immediately.PureeBufferedOutput
: Buffered output plugins enqueue logs to a local storage and then flush them in background tasks.
If you don't need buffering, you can use PureeOutput
.
public class OutLogcat extends PureeOutput {
private static final String TYPE = "out_logcat";
@Override
public String type() {
return TYPE;
}
@Override
public OutputConfiguration configure(OutputConfiguration conf) {
return conf;
}
@Override
public void emit(JsonObject jsonLog) {
Log.d(TYPE, jsonLog.toString());
}
}
If you need buffering, you can use PureeBufferedOutput
.
public class OutFakeApi extends PureeBufferedOutput {
private static final String TYPE = "out_fake_api";
private static final FakeApiClient CLIENT = new FakeApiClient();
@Override
public String type() {
return TYPE;
}
@Override
public OutputConfiguration configure(OutputConfiguration conf) {
// you can change settings of this plugin
// set interval of sending logs. defaults to 2 * 60 * 1000 (2 minutes).
conf.setFlushIntervalMillis(1000);
// set num of logs per request. defaults to 100.
conf.setLogsPerRequest(10);
// set retry count. if fail to send logs, logs will be sending at next time. defaults to 5.
conf.setMaxRetryCount(3);
return conf;
}
@Override
public void emit(JsonArray jsonArray, final AsyncResult result) {
// you have to call result.success or result.fail()
// to notify whether if puree can clear logs from buffer
CLIENT.sendLog(jsonArray, new FakeApiClient.Callback() {
@Override
public void success() {
result.success();
}
@Override
public void fail() {
result.fail();
}
}
);
}
}
Definition of Filters
If you need to add common params to each logs, you can use PureeFilter
:
public class AddEventTimeFilter implements PureeFilter {
public JsonObject apply(JsonObject jsonLog) {
jsonLog.addProperty("event_time", System.currentTimeMillis());
return jsonLog;
}
}
You can make PureeFilter#apply()
to return null
to skip sending logs:
public class SamplingFilter implements PureeFilter {
private final float samplingRate;
public SamplingFilter(float samplingRate) {
this.samplingRate = samplingRate;
}
@Override
public JsonObject apply(JsonObject jsonLog) {
return (samplingRate < Math.random() ? null : jsonLog);
}
}
Then register filters to output plugins on initializing Puree.
new PureeConfiguration.Builder(context)
.register(ClickLog.class, new OutLogcat())
.register(ClickLog.class, new OutFakeApi().withFilters(addEventTimeFilter, samplingFilter)
.build();
Testing
If you want to mock or ignore Puree.send()
and Puree.flush()
, you can use Puree.setPureeLogger()
to replace the internal logger. See PureeTest.java for details.
Release Engineering
Set bintrayUser
and bintrayKey
in ~/.gradle/gradle.properties
bintrayUser=BINTRAY_USER bintrayKey=BINTRAY_API_KEY
and run the following tasks:
./gradlew clean connectedCheck bintrayUpload --info # dry-run ./gradlew bintrayUpload -PdryRun=false
See Also
- Puree - mobile application log collector - Cookpad Developers' blog (Japanese)
- https://github.com/cookpad/puree-ios - Puree for iOS
Copyright
Copyright (c) 2014 Cookpad Inc. https://github.com/cookpad
See LICENSE.txt for the license.