package org.commcare.android.javarosa;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import org.commcare.android.database.SqlStorage;
import org.javarosa.core.api.ILogger;
import org.javarosa.core.log.IFullLogSerializer;
import org.javarosa.core.log.LogEntry;
import org.javarosa.core.log.StreamLogSerializer;
import org.javarosa.core.services.storage.EntityFilter;
import org.javarosa.core.services.storage.StorageFullException;
/**
*
* Logging engine for CommCare ODK Environments.
*
* @author ctsims
*
*/
public class AndroidLogger implements ILogger {
//Log Types:
/** Fatal problem with one of CommCare's cryptography libraries */
public static final String TYPE_ERROR_CRYPTO = "error-crypto";
/** Some invariant application assumption has been violated */
public static final String TYPE_ERROR_ASSERTION = "error-state";
/** Some invariant application assumption has been violated */
public static final String TYPE_ERROR_WORKFLOW = "error-workflow";
/** There is a problem with the underlying storage layer which is preventing the app from working correctly */
public static final String TYPE_ERROR_STORAGE = "error-storage";
/** One of the config files (suite, profile, xform, locale, etc) contains something
* which is invalid and prevented the app from working properly */
public static final String TYPE_ERROR_CONFIG_STRUCTURE = "error-config";
/** Something bad happened which the app should not have allowed to happen. This
* category of error should be aggressively caught and addressed by the software team **/
public static final String TYPE_ERROR_DESIGN ="error-design";
/** Something bad happened because of network connectivity **/
public static final String TYPE_WARNING_NETWORK ="warning-network";
/** Logs relating to user events (login/logout/restore, etc) **/
public static final String TYPE_USER = "user";
/** Logs relating to the external files and resources which make up an app **/
public static final String TYPE_RESOURCES = "resources";
/** Maintenance events (autopurging, cleanups, etc) **/
public static final String TYPE_MAINTENANCE = "maintenance";
/** Form Entry workflow messages **/
public static final String TYPE_FORM_ENTRY = "form-entry";
/** Problem reported via report activity at home screen **/
public static final String USER_REPORTED_PROBLEM = "user-report";
//TODO: Currently assumes that it gets back iterated records in RecordID order.
//when serializing a limited number of records then clearing
SqlStorage<AndroidLogEntry> storage;
private int lastEntry = -1;
private boolean serializing = false;
private final Object serializationLock = new Object();
public AndroidLogger(SqlStorage<AndroidLogEntry> storage) {
this.storage = storage;
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#log(java.lang.String, java.lang.String, java.util.Date)
*/
@Override
public void log(String type, String message, Date logDate) {
try {
storage.write(new AndroidLogEntry(type, message, logDate));
} catch (StorageFullException e) {
e.printStackTrace();
panic();
}
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#clearLogs()
*/
@Override
public void clearLogs() {
if(serializing) {
storage.removeAll();
} else {
storage.removeAll(new EntityFilter<AndroidLogEntry>() {
/*
* (non-Javadoc)
* @see org.javarosa.core.services.storage.EntityFilter#matches(java.lang.Object)
*/
@Override
public boolean matches(AndroidLogEntry e) {
if(e.getID() <= lastEntry) {
return true;
} else {
return false;
}
}
});
}
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#serializeLogs(org.javarosa.core.log.IFullLogSerializer)
*/
@Override
public <T> T serializeLogs(IFullLogSerializer<T> serializer) {
ArrayList<LogEntry> logs = new ArrayList<LogEntry>();
for(AndroidLogEntry entry : storage) {
logs.add(entry);
if(serializing) {
if(entry.getID() > lastEntry) {
lastEntry = entry.getID();
}
}
}
return serializer.serializeLogs(logs.toArray(new LogEntry[0]));
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#serializeLogs(org.javarosa.core.log.StreamLogSerializer)
*/
@Override
public void serializeLogs(StreamLogSerializer serializer) throws IOException {
for(AndroidLogEntry entry : storage) {
serializer.serializeLog(entry.getID(), entry);
if(serializing) {
if(entry.getID() > lastEntry) {
lastEntry = entry.getID();
}
}
}
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#serializeLogs(org.javarosa.core.log.StreamLogSerializer, int)
*/
@Override
public void serializeLogs(StreamLogSerializer serializer, int limit) throws IOException {
int count = 0;
for(AndroidLogEntry entry : storage) {
serializer.serializeLog(entry.getID(), entry);
if(serializing) {
if(entry.getID() > lastEntry) {
lastEntry = entry.getID();
}
}
count++;
if(count > limit) { break; }
}
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#panic()
*/
@Override
public void panic() {
//Unclear
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#logSize()
*/
@Override
public int logSize() {
return storage.getNumRecords();
}
/*
* (non-Javadoc)
* @see org.javarosa.core.api.ILogger#halt()
*/
@Override
public void halt() {
//Meh.
}
/**
* Call before serializing to limit what records will be purged during any
* calls to clear records.
*
* TODO: This is kind of weird.
*/
public void beginSerializationSession() {
synchronized(serializationLock) {
serializing = true;
lastEntry = -1;
}
}
/**
* Call after done with a serialization/purging session to reset the internal
* state of the logger
*
* TODO: This is kind of weird.
*/
public void endSerializatonSession() {
synchronized(serializationLock) {
serializing = false;
lastEntry = -1;
}
}
}