Skip to content

Commit

Permalink
Regexp filtering (#35)
Browse files Browse the repository at this point in the history
* Add support for regexp filtering

* Reusing filter pattern instead of calling compile on each filter call, adding tests.

* Update readme.md

* Fix checkstyle

* Adding log when regexp is invalid

* Move log tag to constant
  • Loading branch information
dpastor authored and pedrovgs committed Jul 26, 2017
1 parent d7957a9 commit 734cba2
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Lynx [![Build Status](https://travis-ci.org/pedrovgs/Lynx.svg?branch=master)](ht

Are you bored of connect your device to your computer to know what's happening inside your app? If you hate it, this is going to be your favorite library. Shake your phone, press a button or add a ``LynxView`` to your layouts and you'll see what Andoird logcat is printing :)

Lynx is an Android library created to show a custom view with all the information logcat is printing, different traces of different levels will be rendererd to show from log messages to your application exceptions. You can filter this traces, share your logcat to other apps, configure the max number of traces to show or the sampling rate used by the library. The min Api Level supported is 10.
Lynx is an Android library created to show a custom view with all the information logcat is printing, different traces of different levels will be rendererd to show from log messages to your application exceptions. You can filter this traces (using regular expressions if you want), share your logcat to other apps, configure the max number of traces to show or the sampling rate used by the library. The min Api Level supported is 10.

Screenshots
-----------
Expand Down
4 changes: 4 additions & 0 deletions lynx/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

testOptions {
unitTests.returnDefaultValues = true
}
}

dependencies {
Expand Down
46 changes: 38 additions & 8 deletions lynx/src/main/java/com/github/pedrovgs/lynx/model/Lynx.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@

import com.github.pedrovgs.lynx.LynxConfig;
import com.github.pedrovgs.lynx.exception.IllegalTraceException;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import android.util.Log;

/**
* Main business logic class for this project. Lynx responsibility is related to listen Logcat
Expand All @@ -28,12 +33,15 @@
*
* Given a LynxConfig object the sample rating used to notify Lynx clients about new traces can be
* modified on demand. LynxConfig object will be used to filter traces if any filter has been
* previously configured.
* previously configured. Filtering will remove traces that contains given string or that match a
* regular expression specified as filter.
*
* @author Pedro Vicente Gomez Sanchez.
*/
public class Lynx {

private static final String LOGTAG = "Lynx";

private Logcat logcat;
private final MainThread mainThread;
private final TimeProvider timeProvider;
Expand All @@ -43,12 +51,16 @@ public class Lynx {
private LynxConfig lynxConfig = new LynxConfig();
private long lastNotificationTime;

private String lowerCaseFilter = "";
private Pattern regexpFilter;

public Lynx(Logcat logcat, MainThread mainThread, TimeProvider timeProvider) {
this.listeners = new LinkedList<Listener>();
this.tracesToNotify = new LinkedList<Trace>();
this.listeners = new LinkedList<>();
this.tracesToNotify = new LinkedList<>();
this.logcat = logcat;
this.mainThread = mainThread;
this.timeProvider = timeProvider;
setFilters();
}

/**
Expand All @@ -58,6 +70,7 @@ public Lynx(Logcat logcat, MainThread mainThread, TimeProvider timeProvider) {
*/
public synchronized void setConfig(LynxConfig lynxConfig) {
this.lynxConfig = lynxConfig;
setFilters();
}

/**
Expand Down Expand Up @@ -130,6 +143,16 @@ public synchronized void unregisterListener(Listener lynxPresenter) {
listeners.remove(lynxPresenter);
}

private void setFilters() {
lowerCaseFilter = lynxConfig.getFilter().toLowerCase();
try {
regexpFilter = Pattern.compile(lowerCaseFilter);
} catch (PatternSyntaxException exception) {
regexpFilter = null;
Log.d(LOGTAG, "Invalid regexp filter!");
}
}

private synchronized void addTraceToTheBuffer(String logcatTrace) throws IllegalTraceException {
if (shouldAddTrace(logcatTrace)) {
Trace trace = Trace.fromString(logcatTrace);
Expand All @@ -143,10 +166,17 @@ private boolean shouldAddTrace(String logcatTrace) {
}

private synchronized boolean traceMatchesFilter(String logcatTrace) {
TraceLevel levelFilter = lynxConfig.getFilterTraceLevel();
String filter = lynxConfig.getFilter().toLowerCase();
String logcatTraceLowercase = logcatTrace.toLowerCase();
return logcatTraceLowercase.contains(filter) && containsTraceLevel(logcatTrace, levelFilter);
return traceStringMatchesFilter(logcatTrace)
&& containsTraceLevel(logcatTrace, lynxConfig.getFilterTraceLevel());
}

private boolean traceStringMatchesFilter(String logcatTrace) {
String lowerCaseLogcatTrace = logcatTrace.toLowerCase();
boolean matchesFilter = lowerCaseLogcatTrace.contains(lowerCaseFilter);
if (!matchesFilter && regexpFilter != null) {
matchesFilter = regexpFilter.matcher(lowerCaseLogcatTrace).find();
}
return matchesFilter;
}

private boolean containsTraceLevel(String logcatTrace, TraceLevel levelFilter) {
Expand All @@ -161,7 +191,7 @@ private boolean hasTraceLevelEqualOrHigher(String logcatTrace, TraceLevel levelF

private synchronized void notifyNewTraces() {
if (shouldNotifyListeners()) {
final List<Trace> traces = new LinkedList<Trace>(tracesToNotify);
final List<Trace> traces = new LinkedList<>(tracesToNotify);
tracesToNotify.clear();
notifyListeners(traces);
}
Expand Down
36 changes: 34 additions & 2 deletions lynx/src/test/java/com/github/pedrovgs/lynx/model/LynxTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@

import com.github.pedrovgs.lynx.LynxConfig;
import com.github.pedrovgs.lynx.exception.IllegalTraceException;
import java.util.LinkedList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.LinkedList;
import java.util.List;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.never;
Expand All @@ -42,14 +44,21 @@ public class LynxTest {
private static final String ANY_ERROR_TRACE = "02-07 17:45:33.014 E/Any error trace";
private static final String ANY_WTF_TRACE = "02-07 17:45:33.014 F/Any WTF trace";
private static final String ANY_FILTER = "FiLteR";
private static final String ANY_OTHER_FILTER = "Other";
private static final String ANY_INVALID_REGEXP_FILTER = "[a-z";
private static final String ANY_TRACE_MATCHING_FILTER_DEBUG =
"02-07 17:45:33.014 D/any fIltEr trace";
private static final String ANY_TRACE_MATCHING_FILTER_VERBOSE =
"02-07 17:45:33.014 V/any fIltEr trace";
private static final String ANY_TRACE_NON_MATCHING_FILTER =
"02-07 17:45:33.014 V/Any error trace";
private static final String ANY_TRACE_NON_MATCHING_FILTER_DEBUG =
"02-07 17:45:33.014 D/Any error trace";
private static final String ANY_TRACE_MATCHING_FILTER_WTF =
"02-07 17:45:33.014 F/Any error trace";
private static final String ANY_TRACE_MATCHING_INVALID_REGEXP_FILTER =
"02-07 17:45:33.014 D/Any [a-z trace";

private Lynx lynx;

@Mock private Lynx.Listener listener;
Expand Down Expand Up @@ -207,6 +216,29 @@ public class LynxTest {
verify(listener).onNewTraces(expectedTraces);
}

@Test public void shouldNotifyJustTracesMatchingRegexpFilter() throws IllegalTraceException {
givenCurrentTimes(NOW, NOW + 5, NOW + 15, NOW + 20);
givenLynxWithFilter(ANY_FILTER + "|" + ANY_OTHER_FILTER, TraceLevel.DEBUG);

Logcat.Listener logcatListener = startLogcat();
logcatListener.onTraceRead(ANY_TRACE_MATCHING_FILTER_DEBUG);
logcatListener.onTraceRead(ANY_TRACE_NON_MATCHING_FILTER_DEBUG);

List<Trace> expectedTraces = generateTraces(ANY_TRACE_MATCHING_FILTER_DEBUG);
verify(listener).onNewTraces(expectedTraces);
}

@Test public void shouldNotifyTraceFilteredWithInvalidRegexp() throws IllegalTraceException {
givenCurrentTimes(NOW, NOW + 5, NOW + 15, NOW + 20);
givenLynxWithFilter(ANY_INVALID_REGEXP_FILTER, TraceLevel.DEBUG);

Logcat.Listener logcatListener = startLogcat();
logcatListener.onTraceRead(ANY_TRACE_MATCHING_INVALID_REGEXP_FILTER);

List<Trace> expectedTraces = generateTraces(ANY_TRACE_MATCHING_INVALID_REGEXP_FILTER);
verify(listener).onNewTraces(expectedTraces);
}

@Test public void shouldStopAndInterruptLogcatOnRestart() {
lynx.restart();

Expand Down

0 comments on commit 734cba2

Please sign in to comment.