Skip to content

Commit 7def2e7

Browse files
committed
feat: send resolved exceptions to posthog
1 parent 414a56f commit 7def2e7

File tree

1 file changed

+30
-27
lines changed

1 file changed

+30
-27
lines changed

src/main/java/net/hollowcube/posthog/PostHogClientImpl.java

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package net.hollowcube.posthog;
22

3-
import com.google.gson.*;
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonArray;
5+
import com.google.gson.JsonElement;
6+
import com.google.gson.JsonObject;
47
import org.jetbrains.annotations.Blocking;
58
import org.jetbrains.annotations.NotNull;
69
import org.jetbrains.annotations.Nullable;
@@ -332,8 +335,8 @@ private void loadRemoteFeatureFlags() {
332335
* Deduplicates recently sent distinctId/featureFlagKey combinations, kind of.
333336
*
334337
* <p>This concept comes from posthog-go in its <a href="https://github.com/PostHog/posthog-go/blob/ec95a60c64b0dd335dafa5c72e8a56c4edc0dbbd/posthog.go#L348">
335-
* handling of sending feature flag called events.</href></a> I'm not very sure what the point is
336-
* especially since the map clear allows two consecutive keys to return true.</p>
338+
* handling of sending feature flag called events.</href></a> I'm not very sure what the point is
339+
* especially since the map clear allows two consecutive keys to return true.</p>
337340
*
338341
* <p>My best guess is that its simply to roughly reduce event volume when evaluating a ton of feature flags.</p>
339342
*
@@ -392,20 +395,25 @@ public void captureException(@NotNull Throwable throwable, @Nullable String dist
392395
}
393396

394397
/**
395-
* PostHog uses the <a href="https://develop.sentry.dev/sdk/data-model/event-payloads/exception/">Sentry exception
396-
* interface</a> for compatibility reasons, so convert to that.
398+
* Generates the exception object for PostHog. We generate a resolved exception, unlike some of their clients.
397399
*
398-
* @return the exception interface as json
400+
* @see <a href="https://github.com/PostHog/posthog/blob/master/rust/cymbal/src/types/mod.rs#L34">sentry type defs</a>
399401
*/
400402
private @NotNull JsonObject buildExceptionInterface(@NotNull Throwable exc, int parentId) {
401403
final JsonObject exception = new JsonObject();
402404
exception.addProperty("type", exc.getClass().getSimpleName());
403-
exception.addProperty("module", exc.getClass().getPackageName());
405+
String moduleName = exc.getClass().getPackageName();
406+
if (exc.getClass().getModule().isNamed())
407+
moduleName = exc.getClass().getModule().getName() + "/" + moduleName;
408+
exception.addProperty("module", moduleName);
404409
exception.addProperty("value", Objects.requireNonNullElse(exc.getMessage(), ""));
405410

406411
JsonObject mechanism = new JsonObject();
407412
mechanism.addProperty("type", "generic");
413+
// We don't currently have a way to indicate this, so just assume yes because the user passed it to us.
408414
mechanism.addProperty("handled", true);
415+
// We include exception_id and parent_id because it is part of the Sentry exception interface to indicate
416+
// exception chaining. PostHog does not currently support this as far as I can tell.
409417
mechanism.addProperty("exception_id", parentId + 1);
410418
if (parentId != -1) {
411419
mechanism.addProperty("type", "chained");
@@ -415,47 +423,41 @@ public void captureException(@NotNull Throwable throwable, @Nullable String dist
415423

416424
JsonObject stackTrace = new JsonObject();
417425
stackTrace.add("frames", getStackFrames(exc.getStackTrace()));
418-
stackTrace.addProperty("type", "raw");
426+
stackTrace.addProperty("type", "resolved");
419427
exception.add("stacktrace", stackTrace);
420428

421429
return exception;
422430
}
423431

424432
private @NotNull JsonArray getStackFrames(StackTraceElement[] elements) {
425433
// Reference: https://github.com/getsentry/sentry-java/blob/9180dc53e73b588db5cb42166e4ee2dc8d3723bc/sentry/src/main/java/io/sentry/SentryStackTraceFactory.java#L30
434+
// Better reference: https://github.com/PostHog/posthog/blob/master/rust/cymbal/src/frames/mod.rs#L81
426435

427436
int startFrame = Math.max(elements.length - STACKTRACE_FRAME_LIMIT, 0);
428437
JsonArray stackFrames = new JsonArray();
429438
for (int i = elements.length - 1; i >= startFrame; i--) {
430439
final StackTraceElement element = elements[i];
431440
if (element == null) continue;
432441

442+
// We generate resolved frames for PostHog.
433443
final JsonObject frame = new JsonObject();
434-
// We lie and tell PostHog that this is a Python exception because they don't actually support
435-
// Java yet :) As far as i can tell this is only actually used for syntax highlighting in source
436-
// code (which we don't send) so this is probably OK for now.
437-
frame.addProperty("platform", "python");
444+
frame.addProperty("resolved", true);
445+
// We just use a 'random' value for the id because its required
446+
frame.addProperty("raw_id", String.valueOf(element.hashCode()));
447+
448+
frame.addProperty("mangled_name", element.getMethodName());
449+
frame.addProperty("resolved_name", element.getMethodName());
450+
// Protocol doesn't accept negative line numbers which can be used to indicate unknown line no.
451+
if (element.getLineNumber() >= 0)
452+
frame.addProperty("line", element.getLineNumber());
438453
var fileName = element.getClassName();
439454
if (element.getModuleName() != null)
440455
fileName = element.getModuleName() + "/" + fileName;
441-
frame.addProperty("filename", fileName);
442-
frame.addProperty("abs_path", element.getFileName());
443-
444-
frame.addProperty("module", element.getClassName());
445-
frame.addProperty("function", element.getMethodName());
446-
447-
// Protocol doesn't accept negative line numbers.
448-
// The runtime seem to use -2 as a way to signal a native method
449-
if (element.getLineNumber() >= 0)
450-
frame.addProperty("lineno", element.getLineNumber());
451-
452-
// The following is required but we don't have this info in Java
453-
frame.add("pre_context", new JsonArray());
454-
frame.add("context_line", JsonNull.INSTANCE);
455-
frame.add("post_context", new JsonArray());
456+
frame.addProperty("source", fileName);
456457

457458
// TODO: expand this further to allow user specified in-app filters.
458459
frame.addProperty("in_app", element.getModuleName() == null || !element.getModuleName().startsWith("java."));
460+
frame.addProperty("lang", "java");
459461

460462
stackFrames.add(frame);
461463
}
@@ -467,4 +469,5 @@ private void setPropertyIfAbsent(@NotNull JsonObject object, @NotNull String key
467469
object.addProperty(key, value);
468470
}
469471
}
472+
470473
}

0 commit comments

Comments
 (0)