package com.bugsnag.android;
import android.support.annotation.NonNull;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Date;
import java.util.Queue;
import java.util.Map;
import java.util.Collections;
import java.util.concurrent.ConcurrentLinkedQueue;
class Breadcrumbs implements JsonStream.Streamable {
private static class Breadcrumb implements JsonStream.Streamable {
private static final int MAX_MESSAGE_LENGTH = 140;
private static final String DEFAULT_NAME = "manual";
private static final String MESSAGE_METAKEY = "message";
private final String TIMESTAMP_KEY = "timestamp";
private final String NAME_KEY = "name";
private final String METADATA_KEY = "metaData";
private final String TYPE_KEY = "type";
final String timestamp;
final String name;
final BreadcrumbType type;
final Map<String, String> metadata;
Breadcrumb(@NonNull String message) {
this.timestamp = DateUtils.toISO8601(new Date());
this.type = BreadcrumbType.MANUAL;
this.metadata = Collections.singletonMap(MESSAGE_METAKEY, message.substring(0, Math.min(message.length(), MAX_MESSAGE_LENGTH)));
this.name = DEFAULT_NAME;
}
Breadcrumb(@NonNull String name, BreadcrumbType type, Map<String, String> metadata) {
this.timestamp = DateUtils.toISO8601(new Date());
this.type = type;
this.metadata = metadata;
this.name = name;
}
public void toStream(@NonNull JsonStream writer) throws IOException {
writer.beginObject();
writer.name(TIMESTAMP_KEY).value(this.timestamp);
writer.name(NAME_KEY).value(this.name);
writer.name(TYPE_KEY).value(this.type.toString());
writer.name(METADATA_KEY);
writer.beginObject();
for (Map.Entry<String, String> entry : this.metadata.entrySet()) {
writer.name(entry.getKey()).value(entry.getValue());
}
writer.endObject();
writer.endObject();
}
public int payloadSize() throws IOException {
StringWriter writer = new StringWriter();
JsonStream jsonStream = new JsonStream(writer);
toStream(jsonStream);
return writer.toString().length();
}
}
private static final int DEFAULT_MAX_SIZE = 20;
private static final int MAX_PAYLOAD_SIZE = 4096;
final Queue<Breadcrumb> store = new ConcurrentLinkedQueue<>();
private int maxSize = DEFAULT_MAX_SIZE;
public void toStream(@NonNull JsonStream writer) throws IOException {
writer.beginArray();
for (Breadcrumb breadcrumb : store) {
breadcrumb.toStream(writer);
}
writer.endArray();
}
void add(@NonNull String message) {
addToStore(new Breadcrumb(message));
}
void add(@NonNull String name, BreadcrumbType type, Map<String, String> metadata) {
addToStore(new Breadcrumb(name, type, metadata));
}
void clear() {
store.clear();
}
void setSize(int size) {
if (size > store.size()) {
this.maxSize = size;
} else {
// Remove oldest breadcrumbs until reaching the required size
while (store.size() > size) {
store.poll();
}
}
}
private void addToStore(Breadcrumb breadcrumb) {
try {
if (breadcrumb.payloadSize() > MAX_PAYLOAD_SIZE) {
Logger.warn("Dropping breadcrumb because payload exceeds 4KB limit");
return;
}
if (store.size() >= maxSize) {
// Remove oldest breadcrumb
store.poll();
}
store.add(breadcrumb);
} catch (IOException ex) {
Logger.warn("Dropping breadcrumb because it could not be serialized", ex);
}
}
}