Skip to main content

Sentry

Our Sentry integration allows you to import projects and issues from your Sentry cloud account into Port, according to your mapping and definition.

A Project is essentially a container for all the data and information related to a specific application or service that you want to monitor.

An Issue is a group of incidents that describe the underlying problem of your symptoms.

Common use casesโ€‹

  • Map your monitored projects and issues into Port.

Prerequisitesโ€‹

To install the integration, you need a Kubernetes cluster that the integration's container chart will be deployed to.

Please make sure that you have kubectl and helm installed on your machine, and that your kubectl CLI is connected to the Kubernetes cluster where you plan to install the integration.

Installationโ€‹

Choose one of the following installation methods:

Using this installation option means that the integration will be able to update Port in real time using webhooks.

This table summarizes the available parameters for the installation. Set them as you wish in the script below, then copy it and run it in your terminal:

ParameterDescriptionRequired
port.clientIdYour port client idโœ…
port.clientSecretYour port client secretโœ…
port.baseUrlYour port base url, relevant only if not using the default port appโŒ
integration.identifierChange the identifier to describe your integrationโœ…
integration.typeThe integration typeโœ…
integration.eventListener.typeThe event listener typeโœ…
integration.secrets.sentryTokenThe Sentry API tokenโœ…
integration.config.sentryHostThe Sentry host. For example https://sentry.ioโœ…
integration.config.sentryOrganizationThe Sentry organization slugโœ…
scheduledResyncIntervalThe number of minutes between each resyncโŒ
initializePortResourcesDefault true, When set to true the integration will create default blueprints and the port App config MappingโŒ

To install the integration using Helm, run the following command:

helm repo add --force-update port-labs https://port-labs.github.io/helm-charts
helm upgrade --install my-sentry-integration port-labs/port-ocean \
--set port.clientId="PORT_CLIENT_ID" \
--set port.clientSecret="PORT_CLIENT_SECRET" \
--set port.baseUrl="https://api.getport.io" \
--set initializePortResources=true \
--set integration.identifier="my-sentry-integration" \
--set integration.type="sentry" \
--set integration.eventListener.type="POLLING" \
--set integration.config.sentryHost="https://sentry.io" \
--set integration.secrets.sentryToken="string" \
--set integration.config.sentryOrganization="string"

Event listenerโ€‹

The integration uses polling to pull the configuration from Port every minute and check it for changes. If there is a change, a resync will occur.

Advanced integration configuration

For advanced configuration such as proxies or self-signed certificates, click here.

Ingesting Sentry objectsโ€‹

The Sentry integration uses a YAML configuration to describe the process of loading data into the developer portal.

Here is an example snippet from the config which demonstrates the process for getting Issue data from Sentry:

resources:
- kind: issue
selector:
query: "true"
port:
entity:
mappings:
identifier: ".id"
title: ".title"
blueprint: '"sentryIssue"'
properties:
link: ".permalink"
status: ".status"
isUnhandled: ".isUnhandled"
relations:
project: ".project.slug"

The integration makes use of the JQ JSON processor to select, modify, concatenate, transform and perform other operations on existing fields and values from Sentry's API events.

Configuration structureโ€‹

The integration configuration determines which resources will be queried from Sentry, and which entities and properties will be created in Port.

Supported resources

The following resources can be used to map data from Sentry, it is possible to reference any field that appears in the API responses linked below for the mapping configuration.

  • The root key of the integration configuration is the resources key:

    resources:
    - kind: project
    selector:
    ...
  • The kind key is a specifier for a Sentry object:

      resources:
    - kind: project
    selector:
    ...
  • The port, entity and the mappings keys are used to map the Sentry object fields to Port entities. To create multiple mappings of the same kind, you can add another item in the resources array;

    resources:
    - kind: project
    selector:
    query: "true"
    port:
    entity:
    mappings:
    identifier: .slug
    title: .name
    blueprint: '"sentryProject"'
    properties:
    dateCreated: .dateCreated
    platform: .platform
    status: .status
    link: .organization.links.organizationUrl + "/projects/" + .name
Blueprint key

Note the value of the blueprint key - if you want to use a hardcoded string, you need to encapsulate it in 2 sets of quotes, for example use a pair of single-quotes (') and then another pair of double-quotes (")

Ingest data into Portโ€‹

To ingest Sentry objects using the integration configuration, you can follow the steps below:

  1. Go to the DevPortal Builder page.
  2. Select a blueprint you want to ingest using Sentry.
  3. Choose the Ingest Data option from the menu.
  4. Select Sentry under the APM & alerting category.
  5. Add the contents of your integration configuration to the editor.
  6. Click Resync.

Examplesโ€‹

Examples of blueprints and the relevant integration configurations:

Projectโ€‹

Project blueprint
{
"identifier": "sentryProject",
"title": "project",
"icon": "Sentry",
"schema": {
"properties": {
"dateCreated": {
"title": "dateCreated",
"type": "string",
"format": "date-time"
},
"platform": {
"type": "string",
"title": "platform"
},
"status": {
"title": "status",
"type": "string",
"enum": [
"active",
"disabled",
"pending_deletion",
"deletion_in_progress"
]
},
"link": {
"title": "link",
"type": "string",
"format": "url"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}
Integration configuration
resources:
- kind: project
selector:
query: "true"
port:
entity:
mappings:
identifier: .slug
title: .name
blueprint: '"sentryProject"'
properties:
dateCreated: .dateCreated
platform: .platform
status: .status
link: .organization.links.organizationUrl + "/projects/" + .name

Issueโ€‹

Issue blueprint
{
"identifier": "sentryIssue",
"title": "issue",
"icon": "Sentry",
"schema": {
"properties": {
"link": {
"title": "link",
"type": "string",
"format": "url"
},
"status": {
"title": "status",
"type": "string"
},
"isUnhandled": {
"icon": "DefaultProperty",
"title": "isUnhandled",
"type": "boolean"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"project": {
"title": "project",
"target": "sentryProject",
"required": false,
"many": false
}
}
}
Integration configuration
- kind: issue
selector:
query: "true"
port:
entity:
mappings:
identifier: ".id"
title: ".title"
blueprint: '"sentryIssue"'
properties:
link: ".permalink"
status: ".status"
isUnhandled: ".isUnhandled"
relations:
project: ".project.slug"

Let's Test Itโ€‹

This section includes a sample response data from Sentry. In addition, it includes the entity created from the resync event based on the Ocean configuration provided in the previous section.

Payloadโ€‹

Here is an example of the payload structure from Sentry:

Project response data
{
"id": "4504931759095808",
"slug": "python-fastapi",
"name": "python-fastapi",
"platform": "python-fastapi",
"dateCreated": "2023-03-31T06:18:37.290732Z",
"isBookmarked": false,
"isMember": false,
"features": [
"alert-filters",
"minidump",
"race-free-group-creation",
"similarity-indexing",
"similarity-view",
"span-metrics-extraction",
"span-metrics-extraction-resource",
"releases"
],
"firstEvent": "2023-03-31T06:25:54.666640Z",
"firstTransactionEvent": false,
"access": [],
"hasAccess": true,
"hasMinifiedStackTrace": false,
"hasMonitors": false,
"hasProfiles": false,
"hasReplays": false,
"hasFeedbacks": false,
"hasSessions": false,
"isInternal": false,
"isPublic": false,
"avatar": {
"avatarType": "letter_avatar",
"avatarUuid": null
},
"color": "#913fbf",
"status": "active",
"organization": {
"id": "4504931754901504",
"slug": "test-org",
"status": {
"id": "active",
"name": "active"
},
"name": "Test Org",
"dateCreated": "2023-03-31T06:17:33.619189Z",
"isEarlyAdopter": false,
"require2FA": false,
"requireEmailVerification": false,
"avatar": {
"avatarType": "letter_avatar",
"avatarUuid": null,
"avatarUrl": null
},
"features": [
"performance-tracing-without-performance",
"performance-consecutive-http-detector",
"performance-large-http-payload-detector",
"escalating-issues",
"minute-resolution-sessions",
"performance-issues-render-blocking-assets-detector",
"event-attachments"
],
"links": {
"organizationUrl": "https://test-org.sentry.io",
"regionUrl": "https://us.sentry.io"
},
"hasAuthProvider": false
}
}
Issue response data
{
"id": "4605173695",
"shareId": "None",
"shortId": "PYTHON-FASTAPI-2",
"title": "ZeroDivisionError: division by zero",
"culprit": "index",
"permalink": "https://test-org.sentry.io/issues/4605173695/",
"logger": "None",
"level": "error",
"status": "unresolved",
"statusDetails": {},
"substatus": "new",
"isPublic": false,
"platform": "python",
"project": {
"id": "4504931759095808",
"name": "python-fastapi",
"slug": "python-fastapi",
"platform": "python-fastapi"
},
"type": "error",
"metadata": {
"value": "division by zero",
"type": "ZeroDivisionError",
"filename": "app.py",
"function": "index",
"display_title_with_tree_label": false,
"in_app_frame_mix": "mixed"
},
"numComments": 0,
"assignedTo": "None",
"isBookmarked": false,
"isSubscribed": false,
"subscriptionDetails": "None",
"hasSeen": false,
"annotations": [],
"issueType": "error",
"issueCategory": "error",
"isUnhandled": true,
"count": "1",
"userCount": 0,
"firstSeen": "2023-11-06T08:31:27.058163Z",
"lastSeen": "2023-11-06T08:31:27.058163Z",
"stats": {
"24h": [
[1699174800, 0],
[1699178400, 0],
[1699182000, 0],
[1699250400, 0],
[1699254000, 0],
[1699257600, 1]
]
}
}

Mapping Resultโ€‹

The combination of the sample payload and the Ocean configuration generates the following Port entity:

Project entity in Port
{
"identifier": "python-fastapi",
"title": "python-fastapi",
"icon": null,
"blueprint": "sentryProject",
"team": [],
"properties": {
"dateCreated": "2023-03-31T06:18:37.290732Z",
"platform": "python-fastapi",
"status": "active",
"link": "https://test-org.sentry.io/projects/python-fastapi"
},
"relations": {},
"createdAt": "2023-11-06T08:49:17.700Z",
"createdBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW",
"updatedAt": "2023-11-06T08:59:11.446Z",
"updatedBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW"
}
Issue entity in Port
{
"identifier": "4605173695",
"title": "ZeroDivisionError: division by zero",
"icon": null,
"blueprint": "sentryIssue",
"team": [],
"properties": {
"link": "https://test-org.sentry.io/issues/4605173695/",
"status": "unresolved",
"isUnhandled": true
},
"relations": {
"project": "python-fastapi"
},
"createdAt": "2023-11-06T08:49:20.406Z",
"createdBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW",
"updatedAt": "2023-11-06T08:49:20.406Z",
"updatedBy": "hBx3VFZjqgLPEoQLp7POx5XaoB0cgsxW"
}

Alternative installation via webhookโ€‹

While the Ocean integration described above is the recommended installation method, you may prefer to use a webhook to ingest data from Sentry. If so, use the following instructions:

Webhook installation (click to expand)

In this example you are going to create a webhook integration between Sentry and Port, which will ingest issues entities.

Port configuration

Create the following blueprint definition:

Sentry issue blueprint
{
"identifier": "sentryIssue",
"description": "This blueprint represents an issue trigger event from Sentry",
"title": "Sentry Issue Event",
"icon": "Sentry",
"schema": {
"properties": {
"level": {
"type": "string",
"title": "Level",
"enum": ["error", "info", "fatal", "warning", "debug", "sample"]
},
"platform": {
"type": "string",
"title": "Platform",
"description": "the platform name in Sentry"
},
"status": {
"type": "string",
"title": "Issue Status"
},
"projectID": {
"type": "string",
"title": "Project ID",
"description": "the ID of the project in Sentry"
},
"action": {
"type": "string",
"title": "Action",
"enum": ["created", "resolved", "assigned", "ignored"]
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}

Create the following webhook configuration using Port UI:

Sentry issue webhook configuration
  1. Basic details tab - fill the following details:

    1. Title : Sentry issue mapper;
    2. Identifier : sentry_issue_mapper;
    3. Description : A webhook configuration to map Sentry Issues to Port;
    4. Icon : Sentry;
  2. Integration configuration tab - fill the following JQ mapping:

    [
    {
    "blueprint": "sentryIssue",
    "entity": {
    "identifier": ".body.data.issue.id",
    "title": ".body.data.issue.title",
    "properties": {
    "action": ".body.action",
    "level": ".body.data.issue.level",
    "platform": ".body.data.issue.platform",
    "status": ".body.data.issue.status",
    "projectID": ".body.data.issue.project.id"
    }
    }
    }
    ]
  3. Scroll down to Advanced settings and input the following details:

    1. Signature Header Name : sentry-hook-signature;
    2. Signature Algorithm : Select sha256 from dropdown option;
    3. Click Save at the bottom of the page.
tip

We have left out the secret field from the security object in the webhook configuration because the secret value is generated by Sentry when creating the webhook. So when following this example, please first create the webhook configuration in Port. Use the webhook URL from the response and create the webhook in Sentry. After getting the secret from Sentry, you can go back to Port and update the webhook configuration with the secret.

Create a webhook in Sentry

  1. Log in to Sentry with your organization's credentials;
  2. Click the gear icon (Setting) at the left sidebar of the page;
  3. Choose Developer Settings;
  4. At the upper corner of this page, click on Create New Integration;
  5. Sentry provides two types of integrations: Internal and Public. For the purpose of this guide, choose Internal Integration and click on the Next button;
  6. Input the following details:
    1. Name - use a meaningful name such as Port Webhook;
    2. Webhook URL - enter the value of the url key you received after creating the webhook configuration;
    3. Overview - enter a description for the webhook;
    4. Permissions - Grant your webhook Read permissions for the Issue & Event category;
    5. Webhooks - Under this section, enable the issues checkbox to allow Sentry to report issue events to Port;
  7. Click Save Changes at the bottom of the page.
tip

Now that the webhook is created, you can take the secret value generated by Sentry and use it to update the security object in your Port webhook configuration

Relate comments to Issues

The following example adds a sentryComment blueprint, in addition to the sentryIssue blueprint shown in the previous example. In addition, it also adds a sentryIssue relation. The webhook will create or update the relation between the 2 existing entities, allowing you to map which issue a comment is made on:

Sentry comments blueprint (including the sentryIssue relation)
{
"identifier": "sentryComment",
"description": "This blueprint represents a Sentry comment in our software catalog",
"title": "Sentry Comment",
"icon": "Sentry",
"schema": {
"properties": {
"action": {
"type": "string",
"title": "action",
"enum": ["created", "updated", "deleted"]
},
"comment": {
"type": "string",
"title": "Comment"
},
"project": {
"type": "string",
"title": "Project Slug"
},
"issue_id": {
"type": "string",
"title": "Issue ID"
},
"timestamp": {
"type": "string",
"title": "Comment Timestamp"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {
"sentryIssue": {
"title": "Issue",
"target": "sentryIssue",
"required": false,
"many": false
}
}
}

Create the following webhook configuration using Port UI:

Sentry comments webhook configuration
  1. Basic details tab - fill the following details:

    1. Title : Sentry comment mapper;
    2. Identifier : sentry_comment_mapper;
    3. Description : A webhook configuration to map Sentry Comments to Port;
    4. Icon : Sentry;
  2. Integration configuration tab - fill the following JQ mapping:

    [
    {
    "blueprint": "sentryComment",
    "entity": {
    "identifier": ".body.data.comment_id",
    "title": "Comment Event",
    "properties": {
    "action": ".body.action",
    "comment": ".body.data.comment",
    "project": ".body.data.project_slug",
    "issue_id": ".body.data.issue_id",
    "timestamp": ".body.data.timestamp"
    },
    "relations": {
    "sentryIssue": ".body.data.issue_id | tostring"
    }
    }
    }
    ]
  3. Scroll down to Advanced settings and input the following details:

    1. Signature Header Name : sentry-hook-signature;
    2. Signature Algorithm : Select sha256 from dropdown option;
    3. Click Save at the bottom of the page.
tip

In order to view the different payloads and events available in Sentry webhooks, click here

Done! any issue and comment in Sentry will trigger a webhook event. Port will parse the events according to the mapping and update the catalog entities accordingly.

Let's Test It

This section includes a sample webhook event sent from Sentry when an issue or comment is created. In addition, it includes the entity created from the event based on the webhook configuration provided in the previous section.

Payload

Here is an example of the payload structure sent to the webhook URL when a Sentry issue or comment is created:

Sentry issue webhook event payload
{
"action": "created",
"installation": {
"uuid": "54a3e698-f389-4d86-b9f8-50093a228449"
},
"data": {
"issue": {
"id": "4253613038",
"shareId": "None",
"shortId": "PYTHON-B",
"title": "NameError: name 'total' is not defined",
"culprit": "__main__ in <module>",
"permalink": "None",
"logger": "None",
"level": "error",
"status": "unresolved",
"statusDetails": {},
"substatus": "new",
"isPublic": false,
"platform": "python",
"project": {
"id": "4504989602480128",
"name": "python",
"slug": "python",
"platform": "python"
},
"type": "error",
"metadata": {
"value": "name 'total' is not defined",
"type": "NameError",
"filename": "sentry.py",
"function": "<module>",
"display_title_with_tree_label": false
},
"numComments": 0,
"assignedTo": "None",
"isBookmarked": false,
"isSubscribed": false,
"subscriptionDetails": "None",
"hasSeen": false,
"annotations": [],
"issueType": "error",
"issueCategory": "error",
"isUnhandled": true,
"count": "1",
"userCount": 0,
"firstSeen": "2023-06-15T17:10:09.914274Z",
"lastSeen": "2023-06-15T17:10:09.914274Z"
}
},
"actor": {
"type": "application",
"id": "sentry",
"name": "Sentry"
}
}
Sentry comment webhook event payload
{
"action": "created",
"installation": {
"uuid": "d5a2de51-0138-496a-8e79-c17747c3a40d"
},
"data": {
"comment_id": "1729635072",
"issue_id": "4253613038",
"project_slug": "python",
"timestamp": "2023-06-15T17:15:53.383120Z",
"comment": "Hello admin please take a look at this"
},
"actor": {
"type": "user",
"id": 2683666,
"name": "user@domain.com"
}
}

Mapping Result

The combination of the sample payload and the webhook configuration generates the following Port sentryIssue entity:

{
"identifier": "4253613038",
"title": "NameError: name 'total' is not defined",
"blueprint": "sentryIssue",
"properties": {
"action": "created",
"level": "error",
"platform": "python",
"status": "unresolved",
"projectID": "4504989602480128"
},
"relations": {}
}

In addition, the following Port sentryComment entity will be generated:

{
"identifier": "1729635072",
"title": "Comment Event",
"blueprint": "sentryComment",
"properties": {
"action": "created",
"comment": "Hello admin please take a look at this",
"project": "python",
"issue_id": "4253613038",
"timestamp": "2023-06-15T17:15:53.383120Z"
},
"relations": {
"sentryIssue": "4253613038"
}
}