forked from getsentry/sentry-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOutboxSender.java
More file actions
203 lines (183 loc) · 7.05 KB
/
OutboxSender.java
File metadata and controls
203 lines (183 loc) · 7.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package io.sentry;
import static io.sentry.SentryLevel.ERROR;
import static io.sentry.cache.EnvelopeCache.PREFIX_CURRENT_SESSION_FILE;
import io.sentry.hints.Flushable;
import io.sentry.hints.Resettable;
import io.sentry.hints.Retryable;
import io.sentry.hints.SubmissionResult;
import io.sentry.util.CollectionUtils;
import io.sentry.util.LogUtils;
import io.sentry.util.Objects;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ApiStatus.Internal
public final class OutboxSender extends DirectoryProcessor implements IEnvelopeSender {
@SuppressWarnings("CharsetObjectCanBeUsed")
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final @NotNull IHub hub;
private final @NotNull IEnvelopeReader envelopeReader;
private final @NotNull ISerializer serializer;
private final @NotNull ILogger logger;
public OutboxSender(
final @NotNull IHub hub,
final @NotNull IEnvelopeReader envelopeReader,
final @NotNull ISerializer serializer,
final @NotNull ILogger logger,
final long flushTimeoutMillis) {
super(logger, flushTimeoutMillis);
this.hub = Objects.requireNonNull(hub, "Hub is required.");
this.envelopeReader = Objects.requireNonNull(envelopeReader, "Envelope reader is required.");
this.serializer = Objects.requireNonNull(serializer, "Serializer is required.");
this.logger = Objects.requireNonNull(logger, "Logger is required.");
}
@Override
protected void processFile(final @NotNull File file, @Nullable Object hint) {
Objects.requireNonNull(file, "File is required.");
if (!isRelevantFileName(file.getName())) {
logger.log(SentryLevel.DEBUG, "File '%s' should be ignored.", file.getAbsolutePath());
return;
}
try (final InputStream stream = new BufferedInputStream(new FileInputStream(file))) {
final SentryEnvelope envelope = envelopeReader.read(stream);
if (envelope == null) {
logger.log(
SentryLevel.ERROR,
"Stream from path %s resulted in a null envelope.",
file.getAbsolutePath());
} else {
processEnvelope(envelope, hint);
logger.log(SentryLevel.DEBUG, "File '%s' is done.", file.getAbsolutePath());
}
} catch (IOException e) {
logger.log(SentryLevel.ERROR, "Error processing envelope.", e);
} finally {
if (hint instanceof Retryable) {
if (!((Retryable) hint).isRetry()) {
try {
if (!file.delete()) {
logger.log(SentryLevel.ERROR, "Failed to delete: %s", file.getAbsolutePath());
}
} catch (RuntimeException e) {
logger.log(SentryLevel.ERROR, e, "Failed to delete: %s", file.getAbsolutePath());
}
}
} else {
LogUtils.logIfNotRetryable(logger, hint);
}
}
}
@Override
protected boolean isRelevantFileName(final @Nullable String fileName) {
// ignore current.envelope
return fileName != null && !fileName.startsWith(PREFIX_CURRENT_SESSION_FILE);
// TODO: Use an extension to filter out relevant files
}
@Override
public void processEnvelopeFile(@NotNull String path, @Nullable Object hint) {
Objects.requireNonNull(path, "Path is required.");
processFile(new File(path), hint);
}
private void processEnvelope(final @NotNull SentryEnvelope envelope, final @Nullable Object hint)
throws IOException {
logger.log(
SentryLevel.DEBUG,
"Processing Envelope with %d item(s)",
CollectionUtils.size(envelope.getItems()));
int items = 0;
for (final SentryEnvelopeItem item : envelope.getItems()) {
items++;
if (item.getHeader() == null) {
logger.log(SentryLevel.ERROR, "Item %d has no header", items);
continue;
}
if (SentryItemType.Event.equals(item.getHeader().getType())) {
try (final Reader eventReader =
new BufferedReader(
new InputStreamReader(new ByteArrayInputStream(item.getData()), UTF_8))) {
SentryEvent event = serializer.deserialize(eventReader, SentryEvent.class);
if (event == null) {
logger.log(
SentryLevel.ERROR,
"Item %d of type %s returned null by the parser.",
items,
item.getHeader().getType());
} else {
if (envelope.getHeader().getEventId() != null
&& !envelope.getHeader().getEventId().equals(event.getEventId())) {
logger.log(
SentryLevel.ERROR,
"Item %d of has a different event id (%s) to the envelope header (%s)",
items,
envelope.getHeader().getEventId(),
event.getEventId());
continue;
}
hub.captureEvent(event, hint);
logger.log(SentryLevel.DEBUG, "Item %d is being captured.", items);
if (!waitFlush(hint)) {
logger.log(
SentryLevel.WARNING,
"Timed out waiting for event submission: %s",
event.getEventId());
break;
}
}
} catch (Exception e) {
logger.log(ERROR, "Item failed to process.", e);
}
} else {
// send unknown item types over the wire
final SentryEnvelope newEnvelope =
new SentryEnvelope(
envelope.getHeader().getEventId(), envelope.getHeader().getSdkVersion(), item);
hub.captureEnvelope(newEnvelope, hint);
logger.log(
SentryLevel.DEBUG,
"%s item %d is being captured.",
item.getHeader().getType().getItemType(),
items);
if (!waitFlush(hint)) {
logger.log(
SentryLevel.WARNING,
"Timed out waiting for item type submission: %s",
item.getHeader().getType().getItemType());
break;
}
}
if (hint instanceof SubmissionResult) {
if (!((SubmissionResult) hint).isSuccess()) {
// Failed to send an item of the envelope: Stop attempting to send the rest (an attachment
// without the event that created it isn't useful)
logger.log(
SentryLevel.WARNING,
"Envelope had a failed capture at item %d. No more items will be sent.",
items);
break;
}
}
// reset the Hint to its initial state as we use it multiple times.
if (hint instanceof Resettable) {
((Resettable) hint).reset();
}
}
}
private boolean waitFlush(final @Nullable Object hint) {
if (hint instanceof Flushable) {
return ((Flushable) hint).waitFlush();
} else {
LogUtils.logIfNotFlushable(logger, hint);
}
return true;
}
}