mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 17:37:57 +00:00
Upload trace file as separate debuglogs item.
This commit is contained in:

committed by
Alex Hart

parent
39f1aea8e3
commit
7bb1262571
@@ -29,4 +29,9 @@ public class CompleteLogLine implements LogLine {
|
|||||||
public @NonNull Style getStyle() {
|
public @NonNull Style getStyle() {
|
||||||
return line.getStyle();
|
return line.getStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Placeholder getPlaceholderType() {
|
||||||
|
return line.getPlaceholderType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,15 +12,13 @@ public interface LogLine {
|
|||||||
long getId();
|
long getId();
|
||||||
@NonNull String getText();
|
@NonNull String getText();
|
||||||
@NonNull Style getStyle();
|
@NonNull Style getStyle();
|
||||||
|
@NonNull Placeholder getPlaceholderType();
|
||||||
static List<LogLine> fromText(@NonNull CharSequence text) {
|
|
||||||
return Stream.of(Pattern.compile("\\n").split(text))
|
|
||||||
.map(s -> new SimpleLogLine(s, Style.NONE))
|
|
||||||
.map(line -> (LogLine) line)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Style {
|
enum Style {
|
||||||
NONE, VERBOSE, DEBUG, INFO, WARNING, ERROR
|
NONE, VERBOSE, DEBUG, INFO, WARNING, ERROR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Placeholder {
|
||||||
|
NONE, TRACE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,19 +20,6 @@ public class LogSectionTrace implements LogSection {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull CharSequence getContent(@NonNull Context context) {
|
public @NonNull CharSequence getContent(@NonNull Context context) {
|
||||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
return LogStyleParser.TRACE_PLACEHOLDER;
|
||||||
GZIPOutputStream compressedStream = new GZIPOutputStream(outputStream))
|
|
||||||
{
|
|
||||||
compressedStream.write(Tracer.getInstance().serialize());
|
|
||||||
compressedStream.flush();
|
|
||||||
compressedStream.close();
|
|
||||||
|
|
||||||
outputStream.flush();
|
|
||||||
outputStream.close();
|
|
||||||
|
|
||||||
return Base64.encodeBytes(outputStream.toByteArray());
|
|
||||||
} catch (IOException e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class LogStyleParser {
|
public class LogStyleParser {
|
||||||
|
|
||||||
|
public static final String TRACE_PLACEHOLDER = "<binary trace data>";
|
||||||
|
|
||||||
private static final Map<String, LogLine.Style> STYLE_MARKERS = new HashMap<String, LogLine.Style>() {{
|
private static final Map<String, LogLine.Style> STYLE_MARKERS = new HashMap<String, LogLine.Style>() {{
|
||||||
put(" V ", LogLine.Style.VERBOSE);
|
put(" V ", LogLine.Style.VERBOSE);
|
||||||
put(" D ", LogLine.Style.DEBUG);
|
put(" D ", LogLine.Style.DEBUG);
|
||||||
@@ -15,7 +17,7 @@ public class LogStyleParser {
|
|||||||
put(" E ", LogLine.Style.ERROR);
|
put(" E ", LogLine.Style.ERROR);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
public static LogLine.Style parseStyle(@NonNull String text) {
|
public static @NonNull LogLine.Style parseStyle(@NonNull String text) {
|
||||||
for (Map.Entry<String, LogLine.Style> entry : STYLE_MARKERS.entrySet()) {
|
for (Map.Entry<String, LogLine.Style> entry : STYLE_MARKERS.entrySet()) {
|
||||||
if (text.contains(entry.getKey())) {
|
if (text.contains(entry.getKey())) {
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
@@ -23,4 +25,12 @@ public class LogStyleParser {
|
|||||||
}
|
}
|
||||||
return LogLine.Style.NONE;
|
return LogLine.Style.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @NonNull LogLine.Placeholder parsePlaceholderType(@NonNull String text) {
|
||||||
|
if (text.equals(TRACE_PLACEHOLDER)) {
|
||||||
|
return LogLine.Placeholder.TRACE;
|
||||||
|
} else {
|
||||||
|
return LogLine.Placeholder.NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,14 +7,16 @@ import androidx.annotation.NonNull;
|
|||||||
*/
|
*/
|
||||||
class SimpleLogLine implements LogLine {
|
class SimpleLogLine implements LogLine {
|
||||||
|
|
||||||
static final SimpleLogLine EMPTY = new SimpleLogLine("", Style.NONE);
|
static final SimpleLogLine EMPTY = new SimpleLogLine("", Style.NONE, Placeholder.NONE);
|
||||||
|
|
||||||
private final String text;
|
private final String text;
|
||||||
private final Style style;
|
private final Style style;
|
||||||
|
private final Placeholder placeholder;
|
||||||
|
|
||||||
SimpleLogLine(@NonNull String text, @NonNull Style style) {
|
SimpleLogLine(@NonNull String text, @NonNull Style style, @NonNull Placeholder placeholder) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.style = style;
|
this.style = style;
|
||||||
|
this.placeholder = placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -22,11 +24,18 @@ class SimpleLogLine implements LogLine {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public @NonNull String getText() {
|
public @NonNull String getText() {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public @NonNull Style getStyle() {
|
public @NonNull Style getStyle() {
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Placeholder getPlaceholderType() {
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import android.content.Context;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
@@ -82,16 +83,48 @@ public class SubmitDebugLogRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void submitLog(@NonNull List<LogLine> lines, Callback<Optional<String>> callback) {
|
public void submitLog(@NonNull List<LogLine> lines, Callback<Optional<String>> callback) {
|
||||||
SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines)));
|
SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines, null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submitLog(@NonNull List<LogLine> lines, @Nullable byte[] trace, Callback<Optional<String>> callback) {
|
||||||
|
SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines, trace)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private @NonNull Optional<String> submitLogInternal(@NonNull List<LogLine> lines) {
|
private @NonNull Optional<String> submitLogInternal(@NonNull List<LogLine> lines, @Nullable byte[] trace) {
|
||||||
StringBuilder bodyBuilder = new StringBuilder();
|
String traceUrl = null;
|
||||||
for (LogLine line : lines) {
|
if (trace != null) {
|
||||||
bodyBuilder.append(line.getText()).append('\n');
|
try {
|
||||||
|
traceUrl = uploadContent("application/octet-stream", trace);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "Error during trace upload.", e);
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringBuilder bodyBuilder = new StringBuilder();
|
||||||
|
for (LogLine line : lines) {
|
||||||
|
switch (line.getPlaceholderType()) {
|
||||||
|
case NONE:
|
||||||
|
bodyBuilder.append(line.getText()).append('\n');
|
||||||
|
break;
|
||||||
|
case TRACE:
|
||||||
|
bodyBuilder.append(traceUrl).append('\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String logUrl = uploadContent("text/plain", bodyBuilder.toString().getBytes());
|
||||||
|
return Optional.of(logUrl);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "Error during log upload.", e);
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private @NonNull String uploadContent(@NonNull String contentType, @NonNull byte[] content) throws IOException {
|
||||||
try {
|
try {
|
||||||
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new StandardUserAgentInterceptor()).dns(SignalServiceNetworkAccess.DNS).build();
|
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new StandardUserAgentInterceptor()).dns(SignalServiceNetworkAccess.DNS).build();
|
||||||
Response response = client.newCall(new Request.Builder().url(API_ENDPOINT).get().build()).execute();
|
Response response = client.newCall(new Request.Builder().url(API_ENDPOINT).get().build()).execute();
|
||||||
@@ -108,14 +141,14 @@ public class SubmitDebugLogRepository {
|
|||||||
MultipartBody.Builder post = new MultipartBody.Builder();
|
MultipartBody.Builder post = new MultipartBody.Builder();
|
||||||
Iterator<String> keys = fields.keys();
|
Iterator<String> keys = fields.keys();
|
||||||
|
|
||||||
post.addFormDataPart("Content-Type", "text/plain");
|
post.addFormDataPart("Content-Type", contentType);
|
||||||
|
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
String key = keys.next();
|
String key = keys.next();
|
||||||
post.addFormDataPart(key, fields.getString(key));
|
post.addFormDataPart(key, fields.getString(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
post.addFormDataPart("file", "file", RequestBody.create(MediaType.parse("text/plain"), bodyBuilder.toString()));
|
post.addFormDataPart("file", "file", RequestBody.create(MediaType.parse(contentType), content));
|
||||||
|
|
||||||
Response postResponse = client.newCall(new Request.Builder().url(url).post(post.build()).build()).execute();
|
Response postResponse = client.newCall(new Request.Builder().url(url).post(post.build()).build()).execute();
|
||||||
|
|
||||||
@@ -123,10 +156,10 @@ public class SubmitDebugLogRepository {
|
|||||||
throw new IOException("Bad response: " + postResponse);
|
throw new IOException("Bad response: " + postResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Optional.of(API_ENDPOINT + "/" + item);
|
return API_ENDPOINT + "/" + item;
|
||||||
} catch (IOException | JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.w(TAG, "Error during upload.", e);
|
Log.w(TAG, "Error during upload.", e);
|
||||||
return Optional.absent();
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +199,12 @@ public class SubmitDebugLogRepository {
|
|||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
List<LogLine> out = new ArrayList<>();
|
List<LogLine> out = new ArrayList<>();
|
||||||
out.add(new SimpleLogLine(formatTitle(section.getTitle(), maxTitleLength), LogLine.Style.NONE));
|
out.add(new SimpleLogLine(formatTitle(section.getTitle(), maxTitleLength), LogLine.Style.NONE, LogLine.Placeholder.NONE));
|
||||||
|
|
||||||
CharSequence content = Scrubber.scrub(section.getContent(context));
|
CharSequence content = Scrubber.scrub(section.getContent(context));
|
||||||
|
|
||||||
List<LogLine> lines = Stream.of(Pattern.compile("\\n").split(content))
|
List<LogLine> lines = Stream.of(Pattern.compile("\\n").split(content))
|
||||||
.map(s -> new SimpleLogLine(s, LogStyleParser.parseStyle(s)))
|
.map(s -> new SimpleLogLine(s, LogStyleParser.parseStyle(s), LogStyleParser.parsePlaceholderType(s)))
|
||||||
.map(line -> (LogLine) line)
|
.map(line -> (LogLine) line)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.tracing.Tracer;
|
||||||
import org.thoughtcrime.securesms.util.DefaultValueLiveData;
|
import org.thoughtcrime.securesms.util.DefaultValueLiveData;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
@@ -23,12 +24,17 @@ public class SubmitDebugLogViewModel extends ViewModel {
|
|||||||
private final MutableLiveData<Mode> mode;
|
private final MutableLiveData<Mode> mode;
|
||||||
|
|
||||||
private List<LogLine> sourceLines;
|
private List<LogLine> sourceLines;
|
||||||
|
private byte[] trace;
|
||||||
|
|
||||||
private SubmitDebugLogViewModel() {
|
private SubmitDebugLogViewModel() {
|
||||||
this.repo = new SubmitDebugLogRepository();
|
this.repo = new SubmitDebugLogRepository();
|
||||||
this.lines = new DefaultValueLiveData<>(Collections.emptyList());
|
this.lines = new DefaultValueLiveData<>(Collections.emptyList());
|
||||||
this.mode = new MutableLiveData<>();
|
this.mode = new MutableLiveData<>();
|
||||||
|
|
||||||
|
if (Tracer.getInstance().isEnabled()) {
|
||||||
|
this.trace = Tracer.getInstance().serialize();
|
||||||
|
}
|
||||||
|
|
||||||
repo.getLogLines(result -> {
|
repo.getLogLines(result -> {
|
||||||
sourceLines = result;
|
sourceLines = result;
|
||||||
mode.postValue(Mode.NORMAL);
|
mode.postValue(Mode.NORMAL);
|
||||||
@@ -40,10 +46,6 @@ public class SubmitDebugLogViewModel extends ViewModel {
|
|||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasLines() {
|
|
||||||
return lines.getValue().size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull LiveData<Mode> getMode() {
|
@NonNull LiveData<Mode> getMode() {
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
@@ -53,7 +55,7 @@ public class SubmitDebugLogViewModel extends ViewModel {
|
|||||||
|
|
||||||
MutableLiveData<Optional<String>> result = new MutableLiveData<>();
|
MutableLiveData<Optional<String>> result = new MutableLiveData<>();
|
||||||
|
|
||||||
repo.submitLog(lines.getValue(), value -> {
|
repo.submitLog(lines.getValue(), trace, value -> {
|
||||||
mode.postValue(Mode.NORMAL);
|
mode.postValue(Mode.NORMAL);
|
||||||
result.postValue(value);
|
result.postValue(value);
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user