Skip to content

Commit

Permalink
support of sentry sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
n0str authored and n0str committed Dec 4, 2024
1 parent ced34da commit 147eb33
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 1 deletion.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"test:db": "jest lib/db/",
"test:cache": "jest lib/cache",
"test:default": "jest workers/default",
"test:sentry": "jest workers/sentry",
"test:javascript": "jest workers/javascript",
"test:release": "jest workers/release",
"test:slack": "jest workers/slack",
Expand All @@ -28,6 +29,7 @@
"test:notifier": "jest workers/notifier",
"test:clear": "jest --clearCache",
"run-default": "yarn worker hawk-worker-default",
"run-sentry": "yarn worker hawk-worker-sentry",
"run-js": "yarn worker hawk-worker-javascript",
"run-slack": "yarn worker hawk-worker-slack",
"run-grouper": "yarn worker hawk-worker-grouper",
Expand Down Expand Up @@ -79,4 +81,4 @@
"webpack": "^4.43.0",
"yargs": "^15.3.1"
}
}
}
9 changes: 9 additions & 0 deletions workers/sentry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Worker / Sentry

Sentry worker for handling events in correct format.

## How to run

1. Make sure you are in Workers root directory
3. `yarn install`
4. `yarn run-sentry`
10 changes: 10 additions & 0 deletions workers/sentry/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "hawk-worker-sentry",
"version": "0.0.1",
"description": "Handles events in Sentry format",
"main": "src/index.ts",
"author": "CodeX",
"license": "UNLICENSED",
"private": true,
"workerType": "errors/sentry"
}
134 changes: 134 additions & 0 deletions workers/sentry/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { EventWorker } from '../../../lib/event-worker';
import * as pkg from '../package.json';
import { SentryEventWorkerTask } from '../types/sentry-event-worker-task';
import { SentryEnvelope, SentryItem } from '../types/sentry-envelope';
import { DefaultEventWorkerTask } from '../../default/types/default-event-worker-task';
import * as WorkerNames from '../../../lib/workerNames';

const b64decode = (str: string): string => Buffer.from(str, 'base64').toString('binary');

/**
* Worker for handling Sentry events
*/
export default class SentryEventWorker extends EventWorker {
/**
* Worker type (will pull tasks from Registry queue with the same name)
*/
public type: string = pkg.workerType;

/**
* Message handle function
*
* @param event - event to handle
*/
public async handle(event: SentryEventWorkerTask): Promise<void> {
/**
* Define event type
*/
this.type = 'errors/sentry';

const rawEvent = b64decode(event.payload.envelope);
const envelope = this.parseSentryEnvelope(rawEvent);
this.logger.debug(JSON.stringify(envelope));

// Todo: For now, we only handle the first item in the envelope
const hawkEvent = this.transformToHawkFormat(envelope.Header, envelope.Items[0], event.projectId);
this.logger.debug(JSON.stringify(hawkEvent));

this.validate(hawkEvent);

await this.addTask(WorkerNames.DEFAULT, hawkEvent as DefaultEventWorkerTask);
}

/**
* Parse Sentry envelope into structured format
*
* @param data - raw Sentry envelope data
* @returns Parsed SentryEnvelope object
*/
private parseSentryEnvelope(data: string): SentryEnvelope {
const lines = data.split('\n').filter(line => line.trim() !== '');
const envelope: SentryEnvelope = { Header: {}, Items: [] };

// Parse envelope header
const headerLine = lines.shift();
if (!headerLine) {
throw new Error('Failed to read envelope header');
}
envelope.Header = JSON.parse(headerLine);

// Parse each item
while (lines.length > 0) {
// Item header
const itemHeaderLine = lines.shift();
if (!itemHeaderLine) {
throw new Error('Failed to read item header');
}
const itemHeader = JSON.parse(itemHeaderLine);

// Item payload
const itemPayloadLine = lines.shift();
if (!itemPayloadLine) {
throw new Error('Failed to read item payload');
}
const itemPayload = JSON.parse(itemPayloadLine);

envelope.Items.push({ Header: itemHeader, Payload: itemPayload });
}

return envelope;
}

/**
* Transform a Sentry event into a Hawk-compatible structure
*
* @param envelopeHeader - Sentry envelope header
* @param eventItem - Sentry event item
* @returns Hawk-compatible event structure
*/
private transformToHawkFormat(
envelopeHeader: Record<string, any>,
eventItem: SentryItem,
projectId: string
): DefaultEventWorkerTask {
const { sent_at, trace } = envelopeHeader;
const { Payload } = eventItem;

// delete public_key from trace
delete trace.public_key;

// convert sent_at from ISO 8601 to Unix timestamp
const sent_at_unix = new Date(sent_at).getTime();

const backtrace = Payload.exception?.values?.[0]?.stacktrace?.frames?.map((frame: any) => ({
file: frame.filename,
line: frame.lineno,
function: frame.function,
sourceCode: frame.pre_context?.concat(frame.context_line, frame.post_context || [])
.map((line: string, index: number) => ({
line: frame.lineno + index - frame.pre_context.length,
content: line
})) || [],
})) || [];

return {
projectId: projectId, // Public key is used as hawk project ID
catcherType: 'errors/sentry',
payload: {
title: `${Payload.exception?.values?.[0]?.type || 'Unknown'}: ${Payload.exception?.values?.[0]?.value || ''}`,
type: Payload.level || 'error',
timestamp: sent_at_unix,
backtrace,
release: trace?.release || null,
context: {
sentAt: sent_at_unix,
trace: trace || {},
environment: trace?.environment || 'unknown',
},
catcherVersion: pkg.version,
user: null, // Can be populated with user data if available
},
};
}

}
12 changes: 12 additions & 0 deletions workers/sentry/types/sentry-envelope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Structures for Sentry envelope
*/
export interface SentryEnvelope {
Header: Record<string, any>;
Items: SentryItem[];
}

export interface SentryItem {
Header: Record<string, any>;
Payload: Record<string, any>;
}
12 changes: 12 additions & 0 deletions workers/sentry/types/sentry-event-worker-task.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { WorkerTask } from '../../../lib/types/worker-task';

/**
* Format of task for Sentry Event Worker
*/
export interface SentryEventWorkerTask extends WorkerTask {
/**
* Sentry-specific payload
*/
payload: any;
projectId: string;
}

0 comments on commit 147eb33

Please sign in to comment.