Skip to content

Commit

Permalink
merge: #3968
Browse files Browse the repository at this point in the history
3968: Front-end otel sending to honeycomb via collector r=jobelenus a=jobelenus

Let's see all the things!
<img src="https://media0.giphy.com/media/T6nxuWttJ8RSGifSm1/giphy.gif"/>

Co-authored-by: John Obelenus <[email protected]>
  • Loading branch information
si-bors-ng[bot] and jobelenus authored Jun 11, 2024
2 parents ee3faa7 + 130045f commit 7bb24d4
Show file tree
Hide file tree
Showing 23 changed files with 694 additions and 14 deletions.
3 changes: 2 additions & 1 deletion app/web/.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ VITE_API_PROXY_PATH=/api
VITE_AUTH_API_URL=https://auth-api.systeminit.com
VITE_AUTH_PORTAL_URL=https://auth.systeminit.com
VITE_AUTH0_DOMAIN=systeminit.auth0.com
VITE_BACKEND_HOSTS=["/localhost/g","/si.keeb.dev/g","/app.systeminit.com/g","/tools.systeminit.com/g"]

# Add to env.local for Cypress E2E Testing, pull out of 1 Password for the Production Synthetic Ones
#VITE_AUTH0_USERNAME
Expand Down Expand Up @@ -42,4 +43,4 @@ VITE_POSTHOG_API_HOST=https://e.systeminit.com
# ENV VARIABLES FOR WS CONSOLE LOGGING
# VITE_LOG_WS=true # turn on console logging for all WS events except cursor and online events
# VITE_LOG_WS_CURSOR=true # turn on console logging for cursor related WS events
# VITE_LOG_WS_ONLINE=true # turn on console logging for online related WS events
# VITE_LOG_WS_ONLINE=true # turn on console logging for online related WS events
4 changes: 3 additions & 1 deletion app/web/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ VITE_SI_ENV=production

# POSTHOG ENV VARS
# note - this is a public key that gets published/used in the built code, so it's safe to publish
VITE_POSTHOG_PUBLIC_KEY=phc_KpehlXOqtU44B2MeW6WjqR09NxRJCYEiUReA58QcAYK
VITE_POSTHOG_PUBLIC_KEY=phc_KpehlXOqtU44B2MeW6WjqR09NxRJCYEiUReA58QcAYK

VITE_OTEL_EXPORTER_OTLP_ENDPOINT=""
4 changes: 3 additions & 1 deletion app/web/.env.staging
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
NODE_ENV=production
VITE_SI_ENV=staging
# VITE_SENTRY_DSN= TODO
# VITE_GA_MEASUREMENT_ID= TODO
# VITE_GA_MEASUREMENT_ID= TODO

VITE_OTEL_EXPORTER_OTLP_ENDPOINT=""
6 changes: 6 additions & 0 deletions app/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@
"@codemirror/state": "^6.1.4",
"@codemirror/view": "^6.7.1",
"@headlessui/vue": "^1.7.10",
"@honeycombio/opentelemetry-web": "^0.3.0",
"@lezer/highlight": "^1.1.3",
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/auto-instrumentations-web": "^0.39.0",
"@opentelemetry/instrumentation-document-load": "^0.38.0",
"@opentelemetry/instrumentation-long-task": "^0.38.0",
"@opentelemetry/instrumentation-user-interaction": "^0.38.0",
"@replit/codemirror-vim": "^6.0.11",
"@si/ts-lib": "workspace:*",
"@si/vue-lib": "workspace:*",
Expand Down
71 changes: 71 additions & 0 deletions app/web/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
// Example filename: tracing.js
import { Buffer } from "buffer";
import { HoneycombWebSDK } from "@honeycombio/opentelemetry-web";
import { getWebAutoInstrumentations } from "@opentelemetry/auto-instrumentations-web";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
import { UserInteractionInstrumentation } from "@opentelemetry/instrumentation-user-interaction";
import { LongTaskInstrumentation } from "@opentelemetry/instrumentation-long-task";
import opentelemetry, { Span } from "@opentelemetry/api";

import { createApp } from "vue";
import FloatingVue from "floating-vue";
import VueKonva from "vue-konva";
Expand All @@ -15,6 +23,44 @@ import "./utils/posthog";
import router from "./router";
import store from "./store";

const backendHosts = import.meta.env.VITE_BACKEND_HOSTS
? JSON.parse(import.meta.env.VITE_BACKEND_HOSTS).map(
(r: string) => new RegExp(r),
)
: [];
const sdk = new HoneycombWebSDK({
endpoint: `${
import.meta.env.VITE_OTEL_EXPORTER_OTLP_ENDPOINT
}:4318/v1/traces`,
serviceName: "si-vue",
skipOptionsValidation: true,
instrumentations: [
getWebAutoInstrumentations({
// load custom configuration for xml-http-request instrumentation
"@opentelemetry/instrumentation-xml-http-request": {
propagateTraceHeaderCorsUrls: backendHosts,
},
"@opentelemetry/instrumentation-fetch": {
propagateTraceHeaderCorsUrls: backendHosts,
},
}), // add automatic instrumentation
new DocumentLoadInstrumentation(),
new UserInteractionInstrumentation({
shouldPreventSpanCreation: (eventType, element, span) => {
span.setAttribute("target.tagName", element.tagName);
span.setAttribute("target.html", element.outerHTML);
},
}), // just click events for now
new LongTaskInstrumentation({
observerCallback: (span, _longtaskEvent) => {
span.setAttribute("location.pathname", window.location.pathname);
},
}),
],
});

sdk.start();

// this is for joi - because we are importing the source rather than the default build made for the browser
globalThis.Buffer = Buffer;

Expand All @@ -24,6 +70,31 @@ app.use(createHead());
app.use(router);
app.use(store);

window.onerror = (message, source, lineno, colno, error) => {
const span = opentelemetry.trace.getActiveSpan();

const _report = (span: Span) => {
span.setAttribute("error.stacktrace", error?.stack || "");
span.setAttribute("error.message", message.toString());
span.setAttribute("error.source", source || "");
span.setAttribute("error.lineno", lineno || "");
span.setAttribute("error.colno", colno || "");
};

if (span) {
_report(span);
} else {
const tracer = opentelemetry.trace.getTracer("errorHandler");
tracer.startActiveSpan("error", (span) => {
_report(span);
span.end();
});
}
};

// seemingly this doesnt do anything at all
// app.config.errorHandler = (err, instance, info) => {};

// set the default tooltip delay to show and hide faster
FloatingVue.options.themes.tooltip.delay = { show: 10, hide: 100 };

Expand Down
4 changes: 4 additions & 0 deletions app/web/src/store/asset.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
useFuncStore,
} from "./func/funcs.store";
import { useRouterStore } from "./router.store";
import handleStoreError from "./errors";

export type AssetId = string;

Expand Down Expand Up @@ -471,7 +472,10 @@ export const useAssetStore = () => {
},
},
]);

const actionUnsub = this.$onAction(handleStoreError);
return () => {
actionUnsub();
realtimeStore.unsubscribe(this.$id);
};
},
Expand Down
8 changes: 8 additions & 0 deletions app/web/src/store/auth.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { posthog } from "@/utils/posthog";
import { User } from "@/api/sdf/dal/user";
import { Workspace } from "@/api/sdf/dal/workspace";
import { useWorkspacesStore } from "./workspaces.store";
import handleStoreError from "./errors";

export type UserId = string;

Expand Down Expand Up @@ -215,4 +216,11 @@ export const useAuthStore = defineStore("auth", {
});
},
},
onActivated() {
const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
};
},
});
4 changes: 4 additions & 0 deletions app/web/src/store/change_sets.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import MovedToHead from "@/components/toasts/MovedToHead.vue";
import { useWorkspacesStore } from "./workspaces.store";
import { useRealtimeStore } from "./realtime/realtime.store";
import { useRouterStore } from "./router.store";
import handleStoreError from "./errors";

const toast = useToast();

Expand Down Expand Up @@ -433,7 +434,10 @@ export function useChangeSetsStore() {
},
]);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
stopWatchSelectedChangeSet();
realtimeStore.unsubscribe(this.$id);
};
Expand Down
4 changes: 4 additions & 0 deletions app/web/src/store/component_attributes.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
GROUP_BOTTOM_INTERNAL_PADDING,
GROUP_INTERNAL_PADDING,
} from "@/components/ModelingDiagram/diagram_constants";
import handleStoreError from "./errors";
import { useChangeSetsStore } from "./change_sets.store";
import { useRealtimeStore } from "./realtime/realtime.store";
import { APIComponentGeometry, useComponentsStore } from "./components.store";
Expand Down Expand Up @@ -447,7 +448,10 @@ export const useComponentAttributesStore = (componentId: ComponentId) => {
},
]);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
realtimeStore.unsubscribe(this.$id);
};
},
Expand Down
4 changes: 4 additions & 0 deletions app/web/src/store/components.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
GROUP_DEFAULT_WIDTH,
GROUP_INTERNAL_PADDING,
} from "@/components/ModelingDiagram/diagram_constants";
import handleStoreError from "./errors";
import { useChangeSetsStore } from "./change_sets.store";
import { useRealtimeStore } from "./realtime/realtime.store";
import {
Expand Down Expand Up @@ -1820,11 +1821,14 @@ export const useComponentsStore = (forceChangeSetId?: ChangeSetId) => {
],
);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
// clear selection without triggering url stuff
this.selectedComponentIds = [];
this.selectedEdgeId = null;

actionUnsub();
stopWatchingUrl();
realtimeStore.unsubscribe(`${this.$id}-changeset`);
realtimeStore.unsubscribe(`${this.$id}-workspace`);
Expand Down
43 changes: 43 additions & 0 deletions app/web/src/store/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
StoreOnActionListenerContext,
StateTree,
_GettersTree,
_ActionsTree,
} from "pinia";
import opentelemetry, { Span } from "@opentelemetry/api";

const handleStoreError = ({
name, // name of the action
store, // store instance, same as `someStore`
args, // array of parameters passed to the action
onError, // hook if the action throws or rejects
}: StoreOnActionListenerContext<
string,
StateTree,
_GettersTree<StateTree>,
_ActionsTree
>) => {
onError((error) => {
const span = opentelemetry.trace.getActiveSpan();

const _report = (span: Span) => {
span.setAttribute("error.stacktrace", (error as Error)?.stack || "");
span.setAttribute("error.message", (error as Error).message);
span.setAttribute("error.store_$id", store.$id);
span.setAttribute("error.store_fn_name", name);
span.setAttribute("error.store_fn_args", JSON.stringify(args) || "");
};

if (span) {
_report(span);
} else {
const tracer = opentelemetry.trace.getTracer("errorHandler");
tracer.startActiveSpan("error", (span) => {
_report(span);
span.end();
});
}
});
};

export default handleStoreError;
7 changes: 7 additions & 0 deletions app/web/src/store/func_runs.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { useWorkspacesStore } from "./workspaces.store";
import { AttributeValueId } from "./status.store";
import { useChangeSetsStore } from "./change_sets.store";
import handleStoreError from "./errors";

export type FuncRunId = string;
export type FuncRunLogId = string;
Expand Down Expand Up @@ -156,6 +157,12 @@ export const useFuncRunsStore = () => {
});
},
},
onActivated() {
const actionUnsub = this.$onAction(handleStoreError);
return () => {
actionUnsub();
};
},
}),
)();
};
4 changes: 4 additions & 0 deletions app/web/src/store/presence.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useChangeSetsStore } from "@/store/change_sets.store";
import { useWorkspacesStore } from "@/store/workspaces.store";
import { UserId, useAuthStore } from "@/store/auth.store";
import { useRealtimeStore } from "@/store/realtime/realtime.store";
import handleStoreError from "./errors";

const MOUSE_REFRESH_RATE = 5;
export const ONLINE_PING_RATE = 5000; // 5 seconds
Expand Down Expand Up @@ -232,7 +233,10 @@ export const usePresenceStore = () => {
window.addEventListener("mousemove", this.updateLastSeen);
window.addEventListener("keydown", this.updateLastSeen);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
realtimeStore.unsubscribe(`${this.$id}-changeset`);
realtimeStore.unsubscribe(`${this.$id}-workspace`);
clearInterval(interval);
Expand Down
4 changes: 4 additions & 0 deletions app/web/src/store/qualifications.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ComponentId } from "@/api/sdf/dal/component";
import { useChangeSetsStore } from "./change_sets.store";
import { useRealtimeStore } from "./realtime/realtime.store";
import { useComponentsStore } from "./components.store";
import handleStoreError from "./errors";

export type QualificationStatus = "success" | "failure" | "running" | "warning";

Expand Down Expand Up @@ -194,7 +195,10 @@ export const useQualificationsStore = () => {
},
]);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
realtimeStore.unsubscribe(this.$id);
};
},
Expand Down
7 changes: 7 additions & 0 deletions app/web/src/store/secrets.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { encryptMessage } from "@/utils/messageEncryption";
import { PropertyEditorPropWidgetKind } from "@/api/sdf/dal/property_editor";
import { ActorAndTimestamp } from "@/api/sdf/dal/component";
import { useRealtimeStore } from "./realtime/realtime.store";
import handleStoreError from "./errors";

/**
* A public key with metadata, used to encrypt secrets
Expand Down Expand Up @@ -500,6 +501,12 @@ export function useSecretsStore() {
},
},
]);
const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
realtimeStore.unsubscribe(this.$id);
};
},
},
),
Expand Down
4 changes: 4 additions & 0 deletions app/web/src/store/status.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import UpdatingModel from "../components/toasts/UpdatingModel.vue";
import ConflictToast from "../components/toasts/Conflict.vue";

import { useComponentsStore } from "./components.store";
import handleStoreError from "./errors";

const GLOBAL_STATUS_TOAST_TIMEOUT = 1000;
const GLOBAL_STATUS_TOAST_DEBOUNCE = 300;
Expand Down Expand Up @@ -398,7 +399,10 @@ export const useStatusStore = (forceChangeSetId?: ChangeSetId) => {
},
);

const actionUnsub = this.$onAction(handleStoreError);

return () => {
actionUnsub();
clearTimeout(cleanupTimeout);
realtimeStore.unsubscribe(this.$id);
};
Expand Down
3 changes: 3 additions & 0 deletions app/web/src/store/workspaces.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ModuleId } from "@/store/module.store";
import router from "@/router";
import { useAuthStore, UserId } from "./auth.store";
import { useRouterStore } from "./router.store";
import handleStoreError from "./errors";
import { AuthApiRequest } from ".";

export type WorkspacePk = string;
Expand Down Expand Up @@ -256,6 +257,8 @@ export const useWorkspacesStore = () => {
{ immediate: true },
);

this.$onAction(handleStoreError);

// NOTE - don't need to clean up here, since there is only one workspace
// store, and it will always be loaded
},
Expand Down
Loading

0 comments on commit 7bb24d4

Please sign in to comment.