Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: getsentry/sentry-rust
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.5.1
Choose a base ref
...
head repository: getsentry/sentry-rust
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Mar 12, 2018

  1. feat: Initial commit

    mitsuhiko committed Mar 12, 2018
    Copy the full SHA
    b6f3c1b View commit details

Commits on Mar 13, 2018

  1. Copy the full SHA
    18a77e2 View commit details

Commits on Mar 21, 2018

  1. Copy the full SHA
    9bf5cbc View commit details
  2. Copy the full SHA
    89e6c04 View commit details
  3. Copy the full SHA
    6da8d69 View commit details

Commits on Mar 22, 2018

  1. Copy the full SHA
    8846c23 View commit details
  2. Copy the full SHA
    cec392d View commit details
  3. Copy the full SHA
    60d3df0 View commit details

Commits on Mar 23, 2018

  1. Copy the full SHA
    503c837 View commit details
  2. Copy the full SHA
    3df1500 View commit details
  3. Copy the full SHA
    bdcbb8a View commit details
  4. Copy the full SHA
    63e7ed8 View commit details
  5. Copy the full SHA
    0ae2eb1 View commit details
  6. ref: Some small doc updates

    mitsuhiko committed Mar 23, 2018
    Copy the full SHA
    c09da41 View commit details
  7. Copy the full SHA
    9428574 View commit details
  8. ref: cargo fmt

    mitsuhiko committed Mar 23, 2018
    Copy the full SHA
    b6d0c9c View commit details
  9. Copy the full SHA
    036d6dd View commit details
  10. Copy the full SHA
    fd67bb7 View commit details
  11. Copy the full SHA
    96fa812 View commit details
  12. Copy the full SHA
    49f4570 View commit details
  13. test: Added test on repos

    mitsuhiko committed Mar 23, 2018
    Copy the full SHA
    12411e1 View commit details
  14. Copy the full SHA
    a223bed View commit details
  15. Copy the full SHA
    de1ebb0 View commit details
  16. Copy the full SHA
    a1436e4 View commit details
  17. test: Added request test

    mitsuhiko committed Mar 23, 2018
    Copy the full SHA
    842be0a View commit details
  18. Copy the full SHA
    f89814d View commit details
  19. Copy the full SHA
    0840187 View commit details

Commits on Mar 24, 2018

  1. Copy the full SHA
    d100bf1 View commit details
  2. Copy the full SHA
    7f84b13 View commit details
  3. Copy the full SHA
    211f917 View commit details
  4. Copy the full SHA
    3a9f06e View commit details
  5. Copy the full SHA
    719dde7 View commit details

Commits on Mar 25, 2018

  1. Copy the full SHA
    14ee567 View commit details
  2. Copy the full SHA
    c4d287b View commit details
  3. Copy the full SHA
    fa695b3 View commit details
  4. Copy the full SHA
    f564fe4 View commit details
  5. Copy the full SHA
    bc18d7f View commit details
  6. Copy the full SHA
    53672f0 View commit details
  7. Copy the full SHA
    1581f1b View commit details
  8. Copy the full SHA
    87f2948 View commit details
  9. Copy the full SHA
    d24f8c8 View commit details
  10. Copy the full SHA
    a5bdedc View commit details
  11. Copy the full SHA
    c02861d View commit details
  12. Copy the full SHA
    3463c9b View commit details
  13. Copy the full SHA
    ec34576 View commit details
  14. Copy the full SHA
    1c28a66 View commit details
  15. ref: cargo fmt

    mitsuhiko committed Mar 25, 2018
    Copy the full SHA
    437023c View commit details
  16. Copy the full SHA
    c9d357a View commit details
  17. meta: Updated cargo toml

    mitsuhiko committed Mar 25, 2018
    Copy the full SHA
    f994fb0 View commit details
  18. meta: Fixed doc link

    mitsuhiko committed Mar 25, 2018
    Copy the full SHA
    4224b7a View commit details
Showing with 26,964 additions and 4,042 deletions.
  1. +10 −0 .codecov.yml
  2. +10 −0 .craft.yml
  3. +0 −4 .github/release.yml
  4. +122 −0 .github/workflows/ci.yml
  5. +38 −0 .github/workflows/release.yml
  6. +44 −0 .github/workflows/weekly.yml
  7. +0 −2 .gitignore
  8. +0 −3 .gitmodules
  9. +0 −39 .travis.yml
  10. +3 −0 .vscode/settings.json
  11. +693 −4 CHANGELOG.md
  12. +5,280 −0 Cargo.lock
  13. +17 −61 Cargo.toml
  14. +21 −0 LICENSE
  15. +60 −48 Makefile
  16. +90 −19 README.md
  17. +16 −0 README.tpl
  18. +0 −27 appveyor.yml
  19. +0 −65 build.rs
  20. +1 −0 clippy.toml
  21. +0 −153 docs/Makefile
  22. +0 −1 docs/_sentryext
  23. +0 −250 docs/conf.py
  24. +0 −160 docs/index.rst
  25. +0 −190 docs/make.bat
  26. +0 −19 docs/sentry-doc-config.json
  27. +0 −34 examples/error-chain-demo.rs
  28. +0 −36 examples/failure-demo.rs
  29. +0 −24 examples/log-demo.rs
  30. +0 −20 examples/panic-demo.rs
  31. +0 −45 examples/thread-demo.rs
  32. +15 −0 misc/docs/index.html
  33. +3 −0 rustfmt.toml
  34. +16 −0 scripts/bump-version.sh
  35. +13 −0 scripts/generate-readme.sh
  36. +9 −0 scripts/update-readme.sh
  37. +27 −0 sentry-actix/Cargo.toml
  38. +21 −0 sentry-actix/LICENSE
  39. +81 −0 sentry-actix/README.md
  40. +55 −0 sentry-actix/examples/basic.rs
  41. +713 −0 sentry-actix/src/lib.rs
  42. +25 −0 sentry-anyhow/Cargo.toml
  43. +21 −0 sentry-anyhow/LICENSE
  44. +52 −0 sentry-anyhow/README.md
  45. +130 −0 sentry-anyhow/src/lib.rs
  46. +22 −0 sentry-backtrace/Cargo.toml
  47. +21 −0 sentry-backtrace/LICENSE
  48. +19 −0 sentry-backtrace/README.md
  49. +111 −0 sentry-backtrace/src/integration.rs
  50. +27 −0 sentry-backtrace/src/lib.rs
  51. +80 −0 sentry-backtrace/src/parse.rs
  52. +138 −0 sentry-backtrace/src/process.rs
  53. +64 −0 sentry-backtrace/src/trim.rs
  54. +253 −0 sentry-backtrace/src/utils.rs
  55. +31 −0 sentry-contexts/Cargo.toml
  56. +21 −0 sentry-contexts/LICENSE
  57. +31 −0 sentry-contexts/README.md
  58. +66 −0 sentry-contexts/build.rs
  59. +107 −0 sentry-contexts/src/integration.rs
  60. +27 −0 sentry-contexts/src/lib.rs
  61. +218 −0 sentry-contexts/src/utils.rs
  62. +55 −0 sentry-core/Cargo.toml
  63. +21 −0 sentry-core/LICENSE
  64. +112 −0 sentry-core/README.md
  65. +195 −0 sentry-core/benches/scope_benchmark.rs
  66. +300 −0 sentry-core/src/api.rs
  67. +45 −0 sentry-core/src/breadcrumbs.rs
  68. +355 −0 sentry-core/src/client.rs
  69. +313 −0 sentry-core/src/clientoptions.rs
  70. +20 −0 sentry-core/src/constants.rs
  71. +172 −0 sentry-core/src/error.rs
  72. +108 −0 sentry-core/src/futures.rs
  73. +245 −0 sentry-core/src/hub.rs
  74. +196 −0 sentry-core/src/hub_impl.rs
  75. +93 −0 sentry-core/src/integration.rs
  76. +75 −0 sentry-core/src/intodsn.rs
  77. +161 −0 sentry-core/src/lib.rs
  78. +80 −0 sentry-core/src/macros.rs
  79. +1,139 −0 sentry-core/src/performance.rs
  80. +11 −0 sentry-core/src/scope/mod.rs
  81. +123 −0 sentry-core/src/scope/noop.rs
  82. +349 −0 sentry-core/src/scope/real.rs
  83. +666 −0 sentry-core/src/session.rs
  84. +136 −0 sentry-core/src/test.rs
  85. +74 −0 sentry-core/src/transport.rs
  86. +18 −0 sentry-debug-images/Cargo.toml
  87. +21 −0 sentry-debug-images/LICENSE
  88. +32 −0 sentry-debug-images/README.md
  89. +94 −0 sentry-debug-images/src/images.rs
  90. +70 −0 sentry-debug-images/src/integration.rs
  91. +28 −0 sentry-debug-images/src/lib.rs
  92. +21 −0 sentry-log/Cargo.toml
  93. +21 −0 sentry-log/LICENSE
  94. +49 −0 sentry-log/README.md
  95. +42 −0 sentry-log/src/converters.rs
  96. +44 −0 sentry-log/src/lib.rs
  97. +155 −0 sentry-log/src/logger.rs
  98. +20 −0 sentry-panic/Cargo.toml
  99. +21 −0 sentry-panic/LICENSE
  100. +30 −0 sentry-panic/README.md
  101. +142 −0 sentry-panic/src/lib.rs
  102. +23 −0 sentry-slog/Cargo.toml
  103. +21 −0 sentry-slog/LICENSE
  104. +80 −0 sentry-slog/README.md
  105. +173 −0 sentry-slog/src/converters.rs
  106. +135 −0 sentry-slog/src/drain.rs
  107. +82 −0 sentry-slog/src/lib.rs
  108. +40 −0 sentry-tower/Cargo.toml
  109. +21 −0 sentry-tower/LICENSE
  110. +126 −0 sentry-tower/README.md
  111. +236 −0 sentry-tower/src/helloworld.rs
  112. +230 −0 sentry-tower/src/http.rs
  113. +346 −0 sentry-tower/src/lib.rs
  114. +38 −0 sentry-tracing/Cargo.toml
  115. +21 −0 sentry-tracing/LICENSE
  116. +152 −0 sentry-tracing/README.md
  117. +306 −0 sentry-tracing/src/converters.rs
  118. +424 −0 sentry-tracing/src/layer.rs
  119. +149 −0 sentry-tracing/src/lib.rs
  120. +3 −0 sentry-tracing/tests/README.md
  121. +34 −0 sentry-tracing/tests/breadcrumbs.rs
  122. +24 −0 sentry-tracing/tests/shared.rs
  123. +53 −0 sentry-tracing/tests/smoke.rs
  124. +35 −0 sentry-types/Cargo.toml
  125. +21 −0 sentry-types/LICENSE
  126. +48 −0 sentry-types/README.md
  127. +184 −0 sentry-types/src/auth.rs
  128. +149 −0 sentry-types/src/crontab_validator.rs
  129. +349 −0 sentry-types/src/dsn.rs
  130. +60 −0 sentry-types/src/lib.rs
  131. +248 −0 sentry-types/src/macros.rs
  132. +76 −0 sentry-types/src/project_id.rs
  133. +94 −0 sentry-types/src/protocol/attachment.rs
  134. +983 −0 sentry-types/src/protocol/envelope.rs
  135. +19 −0 sentry-types/src/protocol/mod.rs
  136. +198 −0 sentry-types/src/protocol/monitor.rs
  137. +179 −0 sentry-types/src/protocol/session.rs
  138. +2,064 −0 sentry-types/src/protocol/v7.rs
  139. +203 −0 sentry-types/src/utils.rs
  140. +116 −0 sentry-types/tests/test_auth.rs
  141. +1,525 −0 sentry-types/tests/test_protocol_v7.rs
  142. +104 −0 sentry/Cargo.toml
  143. +21 −0 sentry/LICENSE
  144. +128 −0 sentry/README.md
  145. +17 −0 sentry/examples/anyhow-demo.rs
  146. +19 −0 sentry/examples/before-send.rs
  147. +26 −0 sentry/examples/event-processors.rs
  148. +37 −0 sentry/examples/health.rs
  149. +26 −0 sentry/examples/log-demo.rs
  150. +5 −3 { → sentry}/examples/message-demo.rs
  151. +15 −0 sentry/examples/panic-demo.rs
  152. +101 −0 sentry/examples/performance-demo.rs
  153. +18 −0 sentry/examples/send-with-extra.rs
  154. +57 −0 sentry/examples/thread-demo.rs
  155. +61 −0 sentry/examples/tracing-demo.rs
  156. +139 −0 sentry/src/defaults.rs
  157. +110 −0 sentry/src/init.rs
  158. +228 −0 sentry/src/lib.rs
  159. +146 −0 sentry/src/transports/curl.rs
  160. +64 −0 sentry/src/transports/embedded_svc_http.rs
  161. +123 −0 sentry/src/transports/mod.rs
  162. +176 −0 sentry/src/transports/ratelimit.rs
  163. +120 −0 sentry/src/transports/reqwest.rs
  164. +102 −0 sentry/src/transports/surf.rs
  165. +100 −0 sentry/src/transports/thread.rs
  166. +111 −0 sentry/src/transports/tokio_thread.rs
  167. +200 −0 sentry/src/transports/ureq.rs
  168. +5 −0 sentry/tests/test_async.rs
  169. +247 −0 sentry/tests/test_basic.rs
  170. +84 −0 sentry/tests/test_client.rs
  171. +50 −0 sentry/tests/test_log.rs
  172. +120 −0 sentry/tests/test_processors.rs
  173. +80 −0 sentry/tests/test_tower.rs
  174. +182 −0 sentry/tests/test_tracing.rs
  175. +0 −272 src/api.rs
  176. +0 −260 src/backtrace_support.rs
  177. +0 −16 src/client/mod.rs
  178. +0 −35 src/client/noop.rs
  179. +0 −535 src/client/real.rs
  180. +0 −25 src/constants.rs
  181. +0 −83 src/integrations/error_chain.rs
  182. +0 −218 src/integrations/failure.rs
  183. +0 −185 src/integrations/log.rs
  184. +0 −14 src/integrations/mod.rs
  185. +0 −83 src/integrations/panic.rs
  186. +0 −161 src/lib.rs
  187. +0 −46 src/macros.rs
  188. +0 −16 src/scope/mod.rs
  189. +0 −127 src/scope/noop.rs
  190. +0 −378 src/scope/real.rs
  191. +0 −143 src/transport.rs
  192. +0 −238 src/utils.rs
10 changes: 10 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
coverage:
status:
project:
default:
informational: true
patch: false

comment:
layout: "diff"
require_changes: true
10 changes: 10 additions & 0 deletions .craft.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
minVersion: 0.23.1
changelogPolicy: auto
artifactProvider:
name: none
targets:
- name: crates
- name: github
- name: registry
sdks:
cargo:sentry:
4 changes: 0 additions & 4 deletions .github/release.yml

This file was deleted.

122 changes: 122 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
name: CI

on:
push:
branches:
- master
- "release/**"
pull_request:

jobs:
lints:
name: Lints
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3

- run: rustup component add rustfmt clippy

- uses: Swatinem/rust-cache@v2

- name: Run cargo fmt
run: cargo fmt --all -- --check

- name: Run cargo clippy
run: cargo clippy --all-features --workspace --tests --examples -- -D clippy::all

check:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

name: Check feature permutations using Rust stable on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

steps:
- name: Checkout sources
uses: actions/checkout@v3

- uses: Swatinem/rust-cache@v2

- run: make checkall

test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

name: Test using Rust stable on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

steps:
- name: Checkout sources
uses: actions/checkout@v3

- uses: Swatinem/rust-cache@v2

- name: Run cargo test
run: cargo test --workspace --all-features --all-targets

MSRV:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
rust: [1.73.0]

name: Check / Test MSRV on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

steps:
- name: Checkout sources
uses: actions/checkout@v3

- name: Install rust ${{ matrix.rust }} toolchain
run: |
rustup toolchain install ${{ matrix.rust }} --profile minimal --no-self-update
rustup default ${{ matrix.rust }}
- uses: Swatinem/rust-cache@v2

- run: make checkfast

- run: make testfast

codecov:
name: Code Coverage
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- run: rustup component add llvm-tools-preview

- uses: Swatinem/rust-cache@v2

- uses: taiki-e/install-action@cargo-llvm-cov

- run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info

- uses: codecov/codecov-action@e156083f13aff6830c92fc5faa23505779fbf649
with:
files: lcov.info

doc:
name: Build-test documentation
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -Dwarnings

steps:
- name: Checkout sources
uses: actions/checkout@v3

- run: rustup component add rust-docs

- uses: Swatinem/rust-cache@v2

- name: Run cargo doc
run: cargo doc --workspace --all-features --document-private-items --no-deps
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Release

on:
workflow_dispatch:
inputs:
version:
description: Version to release
required: true
force:
description: Force a release even when there are release-blockers (optional)
required: false

jobs:
release:
runs-on: ubuntu-latest
name: "Release a new version"
steps:
- name: Get auth token
id: token
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
with:
app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}

- uses: actions/checkout@v3
with:
token: ${{ steps.token.outputs.token }}
fetch-depth: 0

- run: cargo install cargo-readme

- name: Prepare release
uses: getsentry/action-prepare-release@v1
env:
GITHUB_TOKEN: ${{ steps.token.outputs.token }}
with:
version: ${{ github.event.inputs.version }}
force: ${{ github.event.inputs.force }}
44 changes: 44 additions & 0 deletions .github/workflows/weekly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Weekly CI

on:
schedule:
- cron: "0 0 * * 1" # every monday at 00:00
workflow_dispatch:

env:
RUSTFLAGS: -Dwarnings

jobs:
weekly-ci:
strategy:
fail-fast: false
matrix:
rust: [nightly, beta]

runs-on: ubuntu-latest
if: github.repository_owner == 'getsentry'

steps:
- uses: actions/checkout@v3

- name: Install rust stable toolchain
run: |
rustup toolchain install ${{ matrix.rust }} --profile minimal --component clippy --no-self-update
rustup default ${{ matrix.rust }}
- run: cargo clippy --all-features --workspace --tests --examples -- -D clippy::all

- run: cargo test --workspace --all-features

weekly-audit:
name: Audit
runs-on: ubuntu-latest
if: github.repository_owner == 'getsentry'

steps:
- uses: actions/checkout@v3

# FIXME: find a maintained alternative to audit-check
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
target
Cargo.lock
venv
docs/_build
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
[submodule "docs/_sentryext"]
path = docs/_sentryext
url = https://github.com/getsentry/sentry-doc-support
39 changes: 0 additions & 39 deletions .travis.yml

This file was deleted.

3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": true
}
697 changes: 693 additions & 4 deletions CHANGELOG.md

Large diffs are not rendered by default.

5,280 changes: 5,280 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

78 changes: 17 additions & 61 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,61 +1,17 @@
[package]
name = "sentry"
version = "0.5.1"
authors = ["Sentry <hello@sentry.io>"]
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/getsentry/sentry-rust"
homepage = "https://github.com/getsentry/sentry-rust"
documentation = "https://docs.rs/sentry"
description = """
Sentry (getsentry.com) client for rust ;)
"""
build = "build.rs"

[package.metadata.docs.rs]
all-features = true

[features]
default = ["with_client_implementation", "with_panic", "with_failure", "with_log", "with_device_info", "with_rust_info"]
with_client_implementation = ["reqwest", "im", "url", "with_backtrace"]
with_backtrace = ["backtrace", "regex"]
with_panic = ["with_backtrace"]
with_failure = ["failure", "with_backtrace"]
with_log = ["log", "with_backtrace"]
with_error_chain = ["error-chain", "with_backtrace"]
with_device_info = ["libc", "hostname", "uname", "with_client_implementation"]
with_rust_info = ["rustc_version", "with_client_implementation"]
with_debug_meta = ["findshlibs", "with_client_implementation"]
with_shim_api = []

[dependencies]
backtrace = { version = "0.3.6", optional = true }
url = { version = "1.7.0", optional = true }
failure = { version = "0.1.1", optional = true }
log = { version = "0.4.1", optional = true }
serde = "1.0.43"
serde_json = "1.0.16"
sentry-types = "0.3.3"
reqwest = { version = "0.8.5", optional = true }
uuid = { version = "0.6.3", features = ["v4"] }
lazy_static = "1.0.0"
regex = { version = "0.2.10", optional = true }
error-chain = { version = "0.11.0", optional = true }
im = { version = "10.2.0", optional = true }
libc = { version = "0.2.40", optional = true }
hostname = { version = "0.1.4", optional = true }
findshlibs = { version = "0.4.0", optional = true }

[target."cfg(not(windows))".dependencies]
uname = { version = "0.1.1", optional = true }

[build-dependencies]
rustc_version = { version = "0.2.2", optional = true }

[dev-dependencies]
failure_derive = "0.1.1"
pretty_env_logger = "0.2.2"

[[example]]
name = "error-chain-demo"
required-features = ["with_error_chain"]
[workspace]
resolver = "2"
members = [
"sentry",
"sentry-actix",
"sentry-anyhow",
"sentry-backtrace",
"sentry-contexts",
"sentry-core",
"sentry-debug-images",
"sentry-log",
"sentry-panic",
"sentry-slog",
"sentry-tower",
"sentry-tracing",
"sentry-types",
]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Functional Software, Inc. dba Sentry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
108 changes: 60 additions & 48 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
all: test
all: check
.PHONY: all

clean:
@@ -13,65 +13,77 @@ doc:
@cargo doc
.PHONY: doc

check: style lint test
.PHONY: check

# Linting

style:
@rustup component add rustfmt --toolchain stable 2> /dev/null
cargo +stable fmt -- --check
.PHONY: style

format:
@rustup component add rustfmt --toolchain stable 2> /dev/null
cargo +stable fmt
.PHONY: format

lint:
@rustup component add clippy --toolchain stable 2> /dev/null
cargo +stable clippy --all-features --tests --examples -- -D clippy::all
.PHONY: lint

# Tests

test: checkall testall
.PHONY: test

testfast:
@echo 'TESTSUITE'
cd sentry && cargo test --features=test
.PHONY: testfast

testall:
@echo 'TESTSUITE'
cargo test --all-features
.PHONY: testall

# Checks

checkfast: check-no-default-features check-default-features
.PHONY: checkfast

checkall: check-all-features check-no-default-features check-default-features check-panic check-curl-transport check-actix
.PHONY: checkall

check-all-features:
@echo 'ALL FEATURES'
@RUSTFLAGS=-Dwarnings cargo check --all-features
.PHONY: check-all-features

check-no-default-features:
@echo 'NO DEFAULT FEATURES'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features
.PHONY: check-no-default-features

check-default-features:
@echo 'DEFAULT FEATURES'
@RUSTFLAGS=-Dwarnings cargo check
.PHONY: check-default-features

check-failure:
@echo 'NO CLIENT + FAILURE'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_failure'
.PHONY: check-failure

check-log:
@echo 'NO CLIENT + LOG'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_log'
.PHONY: check-log
check-no-default-features:
@echo 'NO DEFAULT FEATURES'
@cd sentry-core && RUSTFLAGS=-Dwarnings cargo check --no-default-features
.PHONY: check-no-default-features

check-panic:
@echo 'NO CLIENT + PANIC'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_panic'
@cd sentry && RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'panic'
.PHONY: check-panic

check-error-chain:
@echo 'NO CLIENT + ERROR_CHAIN'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_error_chain'
.PHONY: check-error-chain

check-all-impls:
@echo 'NO CLIENT + ALL IMPLS'
@RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'with_failure,with_log,with_panic,with_error_chain'
.PHONY: check-all-impls

check: check-no-default-features check-default-features
.PHONY: check-all-features

checkall: check-all-features check-no-default-features check-default-features check-failure check-log check-panic check-error-chain check-all-impls
.PHONY: checkall

cargotest:
@echo 'TESTSUITE'
@cargo test
.PHONY: cargotest

cargotestall:
@echo 'TESTSUITE'
@cargo test --all-features
.PHONY: cargotest

test: checkall cargotestall
.PHONY: test

format-check:
@cargo fmt -- --write-mode diff
.PHONY: format-check
check-curl-transport:
@echo 'CURL TRANSPORT'
@cd sentry && RUSTFLAGS=-Dwarnings cargo check --features curl
@echo 'CURL TRANSPORT ONLY'
@cd sentry && RUSTFLAGS=-Dwarnings cargo check --no-default-features --features 'curl,panic'
.PHONY: check-curl-transport

check-actix:
@echo 'ACTIX INTEGRATION'
@cd sentry-actix && RUSTFLAGS=-Dwarnings cargo check
.PHONY: check-actix
109 changes: 90 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,103 @@
<p align="center">
<a href="https://sentry.io" target="_blank" align="center">
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
<br />
</p>

Sentry Rust
===========
# Sentry SDK for Rust

[![Build Status](https://travis-ci.org/getsentry/sentry-rust.svg?branch=master)](https://travis-ci.org/getsentry/sentry-rust)
[![Crates.io](https://img.shields.io/crates/v/sentry.svg?style=flat)](https://crates.io/crates/sentry)
[![Build Status](https://github.com/getsentry/sentry-rust/workflows/CI/badge.svg)](https://github.com/getsentry/sentry-rust/actions?workflow=CI)
[![codecov](https://codecov.io/gh/getsentry/sentry-rust/branch/master/graph/badge.svg?token=x4RzFE8N6t)](https://codecov.io/gh/getsentry/sentry-rust)

This is a new work in progress Sentry library. It will eventually be published to
crates.io as `sentry`. At the moment the crate on creates.io is an older and
incompatible version of this crate.
This workspace contains various crates that provide support for logging events and errors / panics to the
[Sentry](https://sentry.io/) error logging service.

- [sentry](./sentry) [![crates.io](https://img.shields.io/crates/v/sentry.svg)](https://crates.io/crates/sentry)
[![docs.rs](https://docs.rs/sentry/badge.svg)](https://docs.rs/sentry)

The main `sentry` crate aimed at application users that want to log events to sentry.

- [sentry-actix](./sentry-actix)
[![crates.io](https://img.shields.io/crates/v/sentry-actix.svg)](https://crates.io/crates/sentry-actix)
[![docs.rs](https://docs.rs/sentry-actix/badge.svg)](https://docs.rs/sentry-actix)

An integration for the `actix-web (3.0+)` framework.

- [sentry-anyhow](./sentry-anyhow)
[![crates.io](https://img.shields.io/crates/v/sentry-anyhow.svg)](https://crates.io/crates/sentry-anyhow)
[![docs.rs](https://docs.rs/sentry-anyhow/badge.svg)](https://docs.rs/sentry-anyhow)

An integration for `anyhow` errors.

- [sentry-backtrace](./sentry-backtrace)
[![crates.io](https://img.shields.io/crates/v/sentry-backtrace.svg)](https://crates.io/crates/sentry-backtrace)
[![docs.rs](https://docs.rs/sentry-backtrace/badge.svg)](https://docs.rs/sentry-backtrace)

A utility crate that creates and processes backtraces.

- [sentry-contexts](./sentry-contexts)
[![crates.io](https://img.shields.io/crates/v/sentry-contexts.svg)](https://crates.io/crates/sentry-contexts)
[![docs.rs](https://docs.rs/sentry-contexts/badge.svg)](https://docs.rs/sentry-contexts)

An integration that provides `os`, `device` and `rust` contexts.

- [sentry-core](./sentry-core)
[![crates.io](https://img.shields.io/crates/v/sentry-core.svg)](https://crates.io/crates/sentry-core)
[![docs.rs](https://docs.rs/sentry-core/badge.svg)](https://docs.rs/sentry-core)

The core of `sentry`, which can be used to instrument code, and to write integrations that generate events or hook
into event processing.

- [sentry-debug-images](./sentry-debug-images)
[![crates.io](https://img.shields.io/crates/v/sentry-debug-images.svg)](https://crates.io/crates/sentry-debug-images)
[![docs.rs](https://docs.rs/sentry-debug-images/badge.svg)](https://docs.rs/sentry-debug-images)

An integration that adds a list of loaded libraries to events.

- [sentry-log](./sentry-log)
[![crates.io](https://img.shields.io/crates/v/sentry-log.svg)](https://crates.io/crates/sentry-log)
[![docs.rs](https://docs.rs/sentry-log/badge.svg)](https://docs.rs/sentry-log)

An integration for the `log` and `env_logger` crate.

- [sentry-panic](./sentry-panic)
[![crates.io](https://img.shields.io/crates/v/sentry-panic.svg)](https://crates.io/crates/sentry-panic)
[![docs.rs](https://docs.rs/sentry-panic/badge.svg)](https://docs.rs/sentry-panic)

An integration for capturing and logging panics.

- [sentry-slog](./sentry-slog)
[![crates.io](https://img.shields.io/crates/v/sentry-slog.svg)](https://crates.io/crates/sentry-slog)
[![docs.rs](https://docs.rs/sentry-slog/badge.svg)](https://docs.rs/sentry-slog)

An integration for the `slog` crate.

- [sentry-tracing](./sentry-tracing)
[![crates.io](https://img.shields.io/crates/v/sentry-tracing.svg)](https://crates.io/crates/sentry-tracing)
[![docs.rs](https://docs.rs/sentry-tracing/badge.svg)](https://docs.rs/sentry-tracing)

An integration for the `tracing` crate.

- [sentry-types](./sentry-types)
[![crates.io](https://img.shields.io/crates/v/sentry-types.svg)](https://crates.io/crates/sentry-types)
[![docs.rs](https://docs.rs/sentry-types/badge.svg)](https://docs.rs/sentry-types)

Contains types for the Sentry v7 protocol as well as other common types.

**Note**: Until the _1.0_ release, the crates in this repository are considered work in progress and do not follow
semver semantics. Between minor releases, we might occasionally introduce breaking changes while we are exploring the
best API and adding new features.

## Requirements

We currently only verify this crate against a recent version fo Sentry hosted on
[sentry.io](https://sentry.io/) but it should work with on-prem Sentry versions
8.20 and later.
We currently only verify this crate against a recent version of Sentry hosted on [sentry.io](https://sentry.io/) but it
should work with on-prem Sentry versions 20.6 and later.

Additionally the lowest Rust version we target is 1.20.
The **Minimum Supported Rust Version** is currently at _1.73.0_.
The Sentry crates tries to support a _6 months_ old Rust version at time of release,
and the MSRV will be increased in accordance with its dependencies.

## Resources

* [crates.io](https://crates.io/crate/sentry)
* [Documentation](https://docs.rs/sentry)
* [Bug Tracker](https://github.com/getsentry/sentry-rust/issues)
* [IRC](irc://chat.freenode.net/sentry) (chat.freenode.net, #sentry)
* Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
16 changes: 16 additions & 0 deletions README.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<p align="center">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
</p>

# Sentry Rust SDK: {{crate}}

{{readme}}

## Resources

License: {{license}}

- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
27 changes: 0 additions & 27 deletions appveyor.yml

This file was deleted.

65 changes: 0 additions & 65 deletions build.rs

This file was deleted.

1 change: 1 addition & 0 deletions clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
msrv = "1.73.0"
153 changes: 0 additions & 153 deletions docs/Makefile

This file was deleted.

1 change: 0 additions & 1 deletion docs/_sentryext
Submodule _sentryext deleted from 1d8e04
250 changes: 0 additions & 250 deletions docs/conf.py

This file was deleted.

160 changes: 0 additions & 160 deletions docs/index.rst

This file was deleted.

190 changes: 0 additions & 190 deletions docs/make.bat

This file was deleted.

19 changes: 0 additions & 19 deletions docs/sentry-doc-config.json

This file was deleted.

34 changes: 0 additions & 34 deletions examples/error-chain-demo.rs

This file was deleted.

36 changes: 0 additions & 36 deletions examples/failure-demo.rs

This file was deleted.

24 changes: 0 additions & 24 deletions examples/log-demo.rs

This file was deleted.

20 changes: 0 additions & 20 deletions examples/panic-demo.rs

This file was deleted.

45 changes: 0 additions & 45 deletions examples/thread-demo.rs

This file was deleted.

15 changes: 15 additions & 0 deletions misc/docs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<!-- "cargo run" doesn't create index.html in target/doc directory, so we should redirect -->
<html>

<head>
<title>Sentry Rust</title>
<meta http-equiv="refresh" content="0; url=./sentry/" />
<link rel="canonical" href="./sentry/" />
</head>

<body onload="window.location = './sentry/'">
Redirecting to <a href="./sentry/">./sentry/</a>...
</body>

</html>
3 changes: 3 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Uncomment and use this with `cargo +nightly fmt`:
# unstable_features = true
# format_code_in_doc_comments = true
16 changes: 16 additions & 0 deletions scripts/bump-version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"/..

OLD_VERSION="${1}"
NEW_VERSION="${2}"

echo "Current version: ${OLD_VERSION}"
echo "Bumping version: ${NEW_VERSION}"

perl -pi -e "s/^version = \".*?\"/version = \"$NEW_VERSION\"/" sentry*/Cargo.toml
perl -pi -e "s/^(sentry.*)?version = \".*?\"/\$1version = \"$NEW_VERSION\"/" sentry*/Cargo.toml

"$SCRIPT_DIR"/update-readme.sh "$NEW_VERSION"
13 changes: 13 additions & 0 deletions scripts/generate-readme.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -euo pipefail

NEW_VERSION="${1}"
CRATE=$(basename "$PWD")
CRATE_UNDERSCORE="${CRATE//-/_}"

cargo readme --template ../README.tpl --output README.md

# rewrite any relative markdown links and point them to docs.rs.
# this will break at some point with intra-rustdoc links :-(
# See https://github.com/livioribeiro/cargo-readme/issues/18
perl -pi -e "s/\](\(|: )(?!http)(\w+)/\]\$1https:\/\/docs.rs\/${CRATE}\/${NEW_VERSION}\/$CRATE_UNDERSCORE\/\$2/g" README.md
9 changes: 9 additions & 0 deletions scripts/update-readme.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
set -euo pipefail

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$SCRIPT_DIR"/..

NEW_VERSION="${1}"

find sentry* -name Cargo.toml -execdir ../scripts/generate-readme.sh "$NEW_VERSION" \;
27 changes: 27 additions & 0 deletions sentry-actix/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "sentry-actix"
version = "0.36.0"
authors = ["Sentry <hello@sentry.io>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/getsentry/sentry-rust"
homepage = "https://sentry.io/welcome/"
description = """
Sentry client extension for actix-web 3.
"""
edition = "2021"
rust-version = "1.73"

[dependencies]
actix-web = { version = "4", default-features = false }
futures-util = { version = "0.3.5", default-features = false }
sentry-core = { version = "0.36.0", path = "../sentry-core", default-features = false, features = [
"client",
] }
actix-http = "3.9.0"

[dev-dependencies]
actix-web = { version = "4" }
futures = "0.3"
sentry = { path = "../sentry", features = ["test"] }
tokio = { version = "1.0", features = ["full"] }
21 changes: 21 additions & 0 deletions sentry-actix/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Functional Software, Inc. dba Sentry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
81 changes: 81 additions & 0 deletions sentry-actix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<p align="center">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
</p>

# Sentry Rust SDK: sentry-actix

This crate adds a middleware for [`actix-web`](https://actix.rs/) that captures errors and
report them to `Sentry`.

To use this middleware just configure Sentry and then add it to your actix web app as a
middleware. Because actix is generally working with non sendable objects and highly concurrent
this middleware creates a new hub per request. As a result many of the sentry integrations
such as breadcrumbs do not work unless you bind the actix hub.

## Example

```rust
use std::io;

use actix_web::{get, App, Error, HttpRequest, HttpServer};

#[get("/")]
async fn failing(_req: HttpRequest) -> Result<String, Error> {
Err(io::Error::new(io::ErrorKind::Other, "An error happens here").into())
}

fn main() -> io::Result<()> {
let _guard = sentry::init(sentry::ClientOptions {
release: sentry::release_name!(),
..Default::default()
});
std::env::set_var("RUST_BACKTRACE", "1");

let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
runtime.block_on(async move {
HttpServer::new(|| {
App::new()
.wrap(sentry_actix::Sentry::new())
.service(failing)
})
.bind("127.0.0.1:3001")?
.run()
.await
})
}
```

## Using Release Health

The actix middleware will automatically start a new session for each request
when `auto_session_tracking` is enabled and the client is configured to
use `SessionMode::Request`.

```rust
let _sentry = sentry::init(sentry::ClientOptions {
release: sentry::release_name!(),
session_mode: sentry::SessionMode::Request,
auto_session_tracking: true,
..Default::default()
});
```

## Reusing the Hub

This integration will automatically create a new per-request Hub from the main Hub, and update the
current Hub instance. For example, the following will capture a message in the current request's Hub:

```rust
sentry::capture_message("Something is not well", sentry::Level::Warning);
```

## Resources

License: MIT

- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
55 changes: 55 additions & 0 deletions sentry-actix/examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use std::env;
use std::io;

use actix_web::{get, App, Error, HttpRequest, HttpServer};
use sentry::Level;

#[get("/")]
async fn healthy(_req: HttpRequest) -> Result<String, Error> {
Ok("All good".into())
}

#[get("/err")]
async fn errors(_req: HttpRequest) -> Result<String, Error> {
Err(io::Error::new(io::ErrorKind::Other, "An error happens here").into())
}

#[get("/msg")]
async fn captures_message(_req: HttpRequest) -> Result<String, Error> {
sentry::capture_message("Something is not well", Level::Warning);
Ok("Hello World".into())
}

// cargo run -p sentry-actix --example basic
fn main() -> io::Result<()> {
let _guard = sentry::init(sentry::ClientOptions {
release: sentry::release_name!(),
auto_session_tracking: true,
traces_sample_rate: 1.0,
session_mode: sentry::SessionMode::Request,
debug: true,
..Default::default()
});
env::set_var("RUST_BACKTRACE", "1");

let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;

runtime.block_on(async move {
let addr = "127.0.0.1:3001";

println!("Starting server on http://{addr}");

HttpServer::new(|| {
App::new()
.wrap(sentry_actix::Sentry::with_transaction())
.service(healthy)
.service(errors)
.service(captures_message)
})
.bind(addr)?
.run()
.await
})
}
713 changes: 713 additions & 0 deletions sentry-actix/src/lib.rs

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions sentry-anyhow/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "sentry-anyhow"
version = "0.36.0"
authors = ["Sentry <hello@sentry.io>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/getsentry/sentry-rust"
homepage = "https://sentry.io/welcome/"
description = """
Sentry integration for anyhow.
"""
edition = "2021"
rust-version = "1.73"

[features]
default = ["backtrace"]
backtrace = ["anyhow/backtrace"]

[dependencies]
sentry-backtrace = { version = "0.36.0", path = "../sentry-backtrace" }
sentry-core = { version = "0.36.0", path = "../sentry-core" }
anyhow = "1.0.39"

[dev-dependencies]
sentry = { path = "../sentry", default-features = false, features = ["test"] }
21 changes: 21 additions & 0 deletions sentry-anyhow/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Functional Software, Inc. dba Sentry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
52 changes: 52 additions & 0 deletions sentry-anyhow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<p align="center">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
</p>

# Sentry Rust SDK: sentry-anyhow

Adds support for capturing Sentry errors from [`anyhow::Error`].

This integration adds a new event *source*, which allows you to create events directly
from an [`anyhow::Error`] struct. As it is only an event source it only needs to be
enabled using the `anyhow` cargo feature, it does not need to be enabled in the call to
[`sentry::init`](https://docs.rs/sentry/*/sentry/fn.init.html).

This integration does not need to be installed, instead it provides an extra function to
capture [`anyhow::Error`], optionally exposing it as a method on the
[`sentry::Hub`](https://docs.rs/sentry/*/sentry/struct.Hub.html) using the
[`AnyhowHubExt`] trait.

Like a plain [`std::error::Error`] being captured, [`anyhow::Error`] is captured with a
chain of all error sources, if present. See
[`sentry::capture_error`](https://docs.rs/sentry/*/sentry/fn.capture_error.html) for
details of this.

## Example

```rust
use sentry_anyhow::capture_anyhow;

fn function_that_might_fail() -> anyhow::Result<()> {
Err(anyhow::anyhow!("some kind of error"))
}

if let Err(err) = function_that_might_fail() {
capture_anyhow(&err);
}
```

## Features

The `backtrace` feature will enable the corresponding feature in anyhow and allow you to
capture backtraces with your events. It is enabled by default.

[`anyhow::Error`]: https://docs.rs/anyhow/*/anyhow/struct.Error.html

## Resources

License: MIT

- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
130 changes: 130 additions & 0 deletions sentry-anyhow/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//! Adds support for capturing Sentry errors from [`anyhow::Error`].
//!
//! This integration adds a new event *source*, which allows you to create events directly
//! from an [`anyhow::Error`] struct. As it is only an event source it only needs to be
//! enabled using the `anyhow` cargo feature, it does not need to be enabled in the call to
//! [`sentry::init`](https://docs.rs/sentry/*/sentry/fn.init.html).
//!
//! This integration does not need to be installed, instead it provides an extra function to
//! capture [`anyhow::Error`], optionally exposing it as a method on the
//! [`sentry::Hub`](https://docs.rs/sentry/*/sentry/struct.Hub.html) using the
//! [`AnyhowHubExt`] trait.
//!
//! Like a plain [`std::error::Error`] being captured, [`anyhow::Error`] is captured with a
//! chain of all error sources, if present. See
//! [`sentry::capture_error`](https://docs.rs/sentry/*/sentry/fn.capture_error.html) for
//! details of this.
//!
//! # Example
//!
//! ```no_run
//! use sentry_anyhow::capture_anyhow;
//!
//! fn function_that_might_fail() -> anyhow::Result<()> {
//! Err(anyhow::anyhow!("some kind of error"))
//! }
//!
//! if let Err(err) = function_that_might_fail() {
//! capture_anyhow(&err);
//! }
//! ```
//!
//! # Features
//!
//! The `backtrace` feature will enable the corresponding feature in anyhow and allow you to
//! capture backtraces with your events. It is enabled by default.
//!
//! [`anyhow::Error`]: https://docs.rs/anyhow/*/anyhow/struct.Error.html
#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
#![warn(missing_docs)]
#![deny(unsafe_code)]

use sentry_core::protocol::Event;
use sentry_core::types::Uuid;
use sentry_core::Hub;

/// Captures an [`anyhow::Error`].
///
/// This will capture an anyhow error as a sentry event if a
/// [`sentry::Client`](../../struct.Client.html) is initialised, otherwise it will be a
/// no-op. The event is dispatched to the thread-local hub, with semantics as described in
/// [`Hub::current`].
///
/// See [module level documentation](index.html) for more information.
///
/// [`anyhow::Error`]: https://docs.rs/anyhow/*/anyhow/struct.Error.html
pub fn capture_anyhow(e: &anyhow::Error) -> Uuid {
Hub::with_active(|hub| hub.capture_anyhow(e))
}

/// Helper function to create an event from a `anyhow::Error`.
pub fn event_from_error(err: &anyhow::Error) -> Event<'static> {
let dyn_err: &dyn std::error::Error = err.as_ref();

// It's not mutated for not(feature = "backtrace")
#[allow(unused_mut)]
let mut event = sentry_core::event_from_error(dyn_err);

#[cfg(feature = "backtrace")]
{
// exception records are sorted in reverse
if let Some(exc) = event.exception.iter_mut().last() {
let backtrace = err.backtrace();
exc.stacktrace = sentry_backtrace::parse_stacktrace(&format!("{backtrace:#}"));
}
}

event
}

/// Hub extension methods for working with [`anyhow`].
pub trait AnyhowHubExt {
/// Captures an [`anyhow::Error`] on a specific hub.
fn capture_anyhow(&self, e: &anyhow::Error) -> Uuid;
}

impl AnyhowHubExt for Hub {
fn capture_anyhow(&self, anyhow_error: &anyhow::Error) -> Uuid {
let event = event_from_error(anyhow_error);
self.capture_event(event)
}
}

#[cfg(all(feature = "backtrace", test))]
mod tests {
use super::*;

#[test]
fn test_event_from_error_with_backtrace() {
std::env::set_var("RUST_BACKTRACE", "1");

let event = event_from_error(&anyhow::anyhow!("Oh jeez"));

let stacktrace = event.exception[0].stacktrace.as_ref().unwrap();
let found_test_fn = stacktrace
.frames
.iter()
.find(|frame| match &frame.function {
Some(f) => f.contains("test_event_from_error_with_backtrace"),
None => false,
});

assert!(found_test_fn.is_some());
}

#[test]
fn test_capture_anyhow_uses_event_from_error_helper() {
std::env::set_var("RUST_BACKTRACE", "1");

let err = &anyhow::anyhow!("Oh jeez");

let event = event_from_error(err);
let events = sentry::test::with_captured_events(|| {
capture_anyhow(err);
});

assert_eq!(event.exception, events[0].exception);
}
}
22 changes: 22 additions & 0 deletions sentry-backtrace/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "sentry-backtrace"
version = "0.36.0"
authors = ["Sentry <hello@sentry.io>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/getsentry/sentry-rust"
homepage = "https://sentry.io/welcome/"
description = """
Sentry integration and utilities for dealing with stacktraces.
"""
edition = "2021"
rust-version = "1.73"

[dependencies]
backtrace = "0.3.44"
once_cell = "1"
regex = { version = "1.5.5", default-features = false, features = [
"std",
"unicode-perl",
] }
sentry-core = { version = "0.36.0", path = "../sentry-core" }
21 changes: 21 additions & 0 deletions sentry-backtrace/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Functional Software, Inc. dba Sentry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
19 changes: 19 additions & 0 deletions sentry-backtrace/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<p align="center">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
</p>

# Sentry Rust SDK: sentry-backtrace

Backtrace Integration and utilities for sentry.

Exposes functions to capture, process and convert/parse stacktraces, as well
as integrations to process event stacktraces.

## Resources

License: MIT

- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
111 changes: 111 additions & 0 deletions sentry-backtrace/src/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::thread;

use sentry_core::protocol::{Event, Thread};
use sentry_core::{ClientOptions, Integration};

use crate::current_stacktrace;
use crate::process::process_event_stacktrace;

/// Integration to process Event stacktraces.
///
/// This integration will trim backtraces, depending on the `trim_backtraces`
/// and `extra_border_frames` options.
/// It will then classify each frame according to the `in_app_include` and
/// `in_app_exclude` options.
#[derive(Debug, Default)]
pub struct ProcessStacktraceIntegration;

impl ProcessStacktraceIntegration {
/// Creates a new Integration to process stacktraces.
pub fn new() -> Self {
Self
}
}

impl Integration for ProcessStacktraceIntegration {
fn name(&self) -> &'static str {
"process-stacktrace"
}

fn process_event(
&self,
mut event: Event<'static>,
options: &ClientOptions,
) -> Option<Event<'static>> {
for exc in &mut event.exception {
if let Some(ref mut stacktrace) = exc.stacktrace {
process_event_stacktrace(stacktrace, options);
}
}
for th in &mut event.threads {
if let Some(ref mut stacktrace) = th.stacktrace {
process_event_stacktrace(stacktrace, options);
}
}
if let Some(ref mut stacktrace) = event.stacktrace {
process_event_stacktrace(stacktrace, options);
}
Some(event)
}
}

/// Integration to attach stacktraces to Events.
///
/// This integration will add an additional thread backtrace to captured
/// messages, respecting the `attach_stacktrace` option.
#[derive(Debug, Default)]
pub struct AttachStacktraceIntegration;

impl AttachStacktraceIntegration {
/// Creates a new Integration to attach stacktraces to Events.
pub fn new() -> Self {
Self
}
}

impl Integration for AttachStacktraceIntegration {
fn name(&self) -> &'static str {
"attach-stacktrace"
}

fn process_event(
&self,
mut event: Event<'static>,
options: &ClientOptions,
) -> Option<Event<'static>> {
if options.attach_stacktrace && !has_stacktrace(&event) {
let thread = current_thread(true);
if thread.stacktrace.is_some() {
event.threads.values.push(thread);
}
}
Some(event)
}
}

fn has_stacktrace(event: &Event) -> bool {
event.stacktrace.is_some()
|| event.exception.iter().any(|exc| exc.stacktrace.is_some())
|| event.threads.iter().any(|thrd| thrd.stacktrace.is_some())
}

/// Captures information about the current thread.
///
/// If `with_stack` is set to `true` the current stacktrace is
/// attached.
pub fn current_thread(with_stack: bool) -> Thread {
// NOTE: `as_u64` is nightly only
// See https://github.com/rust-lang/rust/issues/67939
let thread_id: u64 = unsafe { std::mem::transmute(thread::current().id()) };
Thread {
id: Some(thread_id.to_string().into()),
name: thread::current().name().map(str::to_owned),
current: true,
stacktrace: if with_stack {
current_stacktrace()
} else {
None
},
..Default::default()
}
}
27 changes: 27 additions & 0 deletions sentry-backtrace/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Backtrace Integration and utilities for sentry.
//!
//! Exposes functions to capture, process and convert/parse stacktraces, as well
//! as integrations to process event stacktraces.
#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
#![warn(missing_docs)]

mod integration;
mod parse;
mod process;
mod trim;
mod utils;

pub use crate::integration::{
current_thread, AttachStacktraceIntegration, ProcessStacktraceIntegration,
};
pub use crate::parse::parse_stacktrace;
pub use crate::process::{backtrace_to_stacktrace, process_event_stacktrace};
pub use crate::trim::trim_stacktrace;
pub use sentry_core::protocol::{Frame, Stacktrace};

/// Returns the current backtrace as sentry stacktrace.
pub fn current_stacktrace() -> Option<Stacktrace> {
backtrace_to_stacktrace(&backtrace::Backtrace::new())
}
80 changes: 80 additions & 0 deletions sentry-backtrace/src/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use once_cell::sync::Lazy;
use regex::Regex;

use crate::utils::{demangle_symbol, filename, strip_symbol};
use crate::{Frame, Stacktrace};

static FRAME_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r#"(?xm)
^
\s*(?:\d+:)?\s* # frame number (missing for inline)
(?:
(?P<addr_old>0x[a-f0-9]+) # old style address prefix
\s-\s
)?
(?P<symbol>[^\r\n\(]+) # symbol name
(?:
\s\((?P<addr_new>0x[a-f0-9]+)\) # new style address in parens
)?
(?:
\r?\n
\s+at\s # padded "at" in new line
(?P<path>[^\r\n]+?) # path to source file
(?::(?P<lineno>\d+))? # optional source line
(?::(?P<colno>\d+))? # optional source column
)?
$
"#,
)
.unwrap()
});

/// Parses a backtrace string into a Sentry `Stacktrace`.
pub fn parse_stacktrace(bt: &str) -> Option<Stacktrace> {
let mut last_address = None;

let frames = FRAME_RE
.captures_iter(bt)
.map(|captures| {
let abs_path = captures.name("path").map(|m| m.as_str().to_string());
let filename = abs_path.as_ref().map(|p| filename(p).to_string());
let real_symbol = captures["symbol"].to_string();
let symbol = strip_symbol(&real_symbol);
let function = demangle_symbol(&symbol);

// Obtain the instruction address. A missing address usually indicates an inlined stack
// frame, in which case the previous address needs to be used.
last_address = captures
.name("addr_new")
.or_else(|| captures.name("addr_old"))
.and_then(|m| m.as_str().parse().ok())
.or(last_address);

Frame {
symbol: if symbol != function {
Some(symbol.into())
} else {
None
},
function: Some(function),
instruction_addr: last_address,
abs_path,
filename,
lineno: captures
.name("lineno")
.map(|x| x.as_str().parse::<u64>().unwrap()),
colno: captures
.name("colno")
.map(|x| x.as_str().parse::<u64>().unwrap()),
..Default::default()
}
})
.collect();

Stacktrace::from_frames_reversed(frames)
}
138 changes: 138 additions & 0 deletions sentry-backtrace/src/process.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use std::borrow::Cow;

use backtrace::Backtrace;
use sentry_core::ClientOptions;

use crate::trim::{is_sys_function, trim_stacktrace};
use crate::utils::{
demangle_symbol, filename, function_starts_with, parse_crate_name, strip_symbol,
};
use crate::{Frame, Stacktrace};

/// Processes a `Stacktrace`.
///
/// Trims a `Stacktrace` and marks frames as in-app based on the provided
/// `ClientOptions`.
pub fn process_event_stacktrace(stacktrace: &mut Stacktrace, options: &ClientOptions) {
// automatically trim backtraces
if options.trim_backtraces {
trim_stacktrace(stacktrace, |frame, _| {
if let Some(ref func) = frame.function {
options.extra_border_frames.contains(&func.as_str())
} else {
false
}
})
}

// automatically prime in_app and set package
let mut any_in_app = false;
for frame in &mut stacktrace.frames {
let func_name = match frame.function {
Some(ref func) => func,
None => continue,
};

// set package if missing to crate prefix
if frame.package.is_none() {
frame.package = parse_crate_name(func_name);
}

match frame.in_app {
Some(true) => {
any_in_app = true;
continue;
}
Some(false) => {
continue;
}
None => {}
}

for m in &options.in_app_include {
if function_starts_with(func_name, m) {
frame.in_app = Some(true);
any_in_app = true;
break;
}
}

if frame.in_app.is_some() {
continue;
}

for m in &options.in_app_exclude {
if function_starts_with(func_name, m) {
frame.in_app = Some(false);
break;
}
}

if frame.in_app.is_some() {
continue;
}

if is_sys_function(func_name) {
frame.in_app = Some(false);
}
}

if !any_in_app {
for frame in &mut stacktrace.frames {
if frame.in_app.is_none() {
frame.in_app = Some(true);
}
}
}
}

/// Convert a `backtrace::Backtrace` into a Rust `Stacktrace`
pub fn backtrace_to_stacktrace(bt: &Backtrace) -> Option<Stacktrace> {
let frames = bt
.frames()
.iter()
.flat_map(|frame| {
// For each frame, there may be multiple symbols if a function was inlined, so
// add an entry for each symbol.
let symbols = frame.symbols();
symbols
.iter()
.map(move |sym| {
let abs_path = sym.filename().map(|m| m.to_string_lossy().to_string());
let filename = abs_path.as_ref().map(|p| filename(p).to_string());
let real_symbol = sym
.name()
.map_or(Cow::Borrowed("<unknown>"), |n| Cow::Owned(n.to_string()));
let symbol = strip_symbol(&real_symbol);
let function = demangle_symbol(&symbol);
Frame {
symbol: if symbol != function {
Some(symbol.into())
} else {
None
},
function: Some(function),
instruction_addr: Some(frame.ip().into()),
abs_path,
filename,
lineno: sym.lineno().map(u64::from),
colno: None,
..Default::default()
}

// If there were no symbols at all, make sure to add at least one frame, as we
// may be able to symbolicate it on the server.
})
.chain(if symbols.is_empty() {
Some(Frame {
instruction_addr: Some(frame.ip().into()),
function: Some("<unknown>".into()),
..Default::default()
})
} else {
None
})
})
.collect();
Stacktrace::from_frames_reversed(frames)
}
64 changes: 64 additions & 0 deletions sentry-backtrace/src/trim.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use sentry_core::protocol::{Frame, Stacktrace};

use crate::utils::function_starts_with;

const WELL_KNOWN_SYS_MODULES: &[&str] = &[
"std::",
"core::",
"alloc::",
"backtrace::",
"sentry::",
"sentry_core::",
"sentry_types::",
// these are not modules but things like __rust_maybe_catch_panic
"__rust_",
"___rust_",
// these are well-known library frames
"anyhow::",
"log::",
"tokio::",
"tracing_core::",
];

const WELL_KNOWN_BORDER_FRAMES: &[&str] = &[
"std::panicking::begin_panic",
"core::panicking::panic",
// well-known library frames
"anyhow::",
"<sentry_log::Logger as log::Log>::log",
"tracing_core::",
];

/// A helper function to trim a stacktrace.
pub fn trim_stacktrace<F>(stacktrace: &mut Stacktrace, f: F)
where
F: Fn(&Frame, &Stacktrace) -> bool,
{
let known_cutoff = stacktrace
.frames
.iter()
.rev()
.position(|frame| match frame.function {
Some(ref func) => is_well_known(func) || f(frame, stacktrace),
None => false,
});

if let Some(cutoff) = known_cutoff {
let trunc = stacktrace.frames.len() - cutoff - 1;
stacktrace.frames.truncate(trunc);
}
}

/// Checks if a function is considered to be not in-app
pub fn is_sys_function(func: &str) -> bool {
WELL_KNOWN_SYS_MODULES
.iter()
.any(|m| function_starts_with(func, m))
}

/// Checks if a function is a well-known system function
fn is_well_known(func: &str) -> bool {
WELL_KNOWN_BORDER_FRAMES
.iter()
.any(|m| function_starts_with(func, m))
}
253 changes: 253 additions & 0 deletions sentry-backtrace/src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
use std::borrow::Cow;

use once_cell::sync::Lazy;
use regex::{Captures, Regex};

static HASH_FUNC_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r#"(?x)
^(.*)::h[a-f0-9]{16}$
"#,
)
.unwrap()
});

static CRATE_HASH_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?x)
\b(\[[a-f0-9]{16}\])
",
)
.unwrap()
});

static CRATE_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?x)
^
(?:_?<)? # trait impl syntax
(?:\w+\ as \ )? # anonymous implementor
([a-zA-Z0-9_]+?) # crate name
(?:\.\.|::|\[) # crate delimiter (.. or :: or [)
",
)
.unwrap()
});

static COMMON_RUST_SYMBOL_ESCAPES_RE: Lazy<Regex> = Lazy::new(|| {
Regex::new(
r"(?x)
\$
(SP|BP|RF|LT|GT|LP|RP|C|
u7e|u20|u27|u5b|u5d|u7b|u7d|u3b|u2b|u22)
\$
",
)
.unwrap()
});

/// Tries to parse the rust crate from a function name.
pub fn parse_crate_name(func_name: &str) -> Option<String> {
CRATE_RE
.captures(func_name)
.and_then(|caps| caps.get(1))
.map(|cr| cr.as_str().into())
}

pub fn filename(s: &str) -> &str {
s.rsplit(&['/', '\\'][..]).next().unwrap()
}

pub fn strip_symbol(s: &str) -> Cow<str> {
let stripped_trailing_hash = HASH_FUNC_RE
.captures(s)
.map(|c| c.get(1).unwrap().as_str())
.unwrap_or(s);

CRATE_HASH_RE.replace_all(stripped_trailing_hash, "")
}

pub fn demangle_symbol(s: &str) -> String {
COMMON_RUST_SYMBOL_ESCAPES_RE
.replace_all(s, |caps: &Captures<'_>| match &caps[1] {
"SP" => "@",
"BP" => "*",
"RF" => "&",
"LT" => "<",
"GT" => ">",
"LP" => "(",
"RP" => ")",
"C" => ",",
"u7e" => "~",
"u20" => " ",
"u27" => "'",
"u5b" => "[",
"u5d" => "]",
"u7b" => "{",
"u7d" => "}",
"u3b" => ";",
"u2b" => "+",
"u22" => "\"",
_ => unreachable!(),
})
.to_string()
}

/// Checks whether the function name starts with the given pattern.
///
/// In trait implementations, the original type name is wrapped in "_< ... >" and colons are
/// replaced with dots. This function accounts for differences while checking.
pub fn function_starts_with(mut func_name: &str, mut pattern: &str) -> bool {
if pattern.starts_with('<') {
while pattern.starts_with('<') {
pattern = &pattern[1..];

if func_name.starts_with('<') {
func_name = &func_name[1..];
} else if func_name.starts_with("_<") {
func_name = &func_name[2..];
} else {
return false;
}
}
} else {
func_name = func_name.trim_start_matches('<').trim_start_matches("_<");
}

if !func_name.is_char_boundary(pattern.len()) {
return false;
}

func_name
.chars()
.zip(pattern.chars())
.all(|(f, p)| f == p || f == '.' && p == ':')
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_function_starts_with() {
assert!(function_starts_with(
"futures::task_impl::std::set",
"futures::"
));

assert!(!function_starts_with(
"futures::task_impl::std::set",
"tokio::"
));
}

#[test]
fn test_function_starts_with_impl() {
assert!(function_starts_with(
"_<futures..task_impl..Spawn<T>>::enter::_{{closure}}",
"futures::"
));

assert!(!function_starts_with(
"_<futures..task_impl..Spawn<T>>::enter::_{{closure}}",
"tokio::"
));
}

#[test]
fn test_function_starts_with_newimpl() {
assert!(function_starts_with(
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
"futures::"
));

assert!(!function_starts_with(
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
"tokio::"
));
}

#[test]
fn test_function_starts_with_impl_pattern() {
assert!(function_starts_with(
"_<futures..task_impl..Spawn<T>>::enter::_{{closure}}",
"<futures::"
));

assert!(function_starts_with(
"<futures::task_impl::Spawn<T>>::enter::{{closure}}",
"<futures::"
));

assert!(!function_starts_with(
"futures::task_impl::std::set",
"<futures::"
));
}

#[test]
fn test_parse_crate_name() {
assert_eq!(
parse_crate_name("futures::task_impl::std::set"),
Some("futures".into())
);
}

#[test]
fn test_parse_crate_name_impl() {
assert_eq!(
parse_crate_name("_<futures..task_impl..Spawn<T>>::enter::_{{closure}}"),
Some("futures".into())
);
}

#[test]
fn test_parse_crate_name_anonymous_impl() {
assert_eq!(
parse_crate_name("_<F as alloc..boxed..FnBox<A>>::call_box"),
Some("alloc".into())
);
}

#[test]
fn strip_crate_hash() {
assert_eq!(
&strip_symbol("std::panic::catch_unwind::hd044952603e5f56c"),
"std::panic::catch_unwind"
);
assert_eq!(
&strip_symbol("std[550525b9dd91a68e]::rt::lang_start::<()>"),
"std::rt::lang_start::<()>"
);
assert_eq!(
&strip_symbol("<fn() as core[bb3d6b31f0e973c8]::ops::function::FnOnce<()>>::call_once"),
"<fn() as core::ops::function::FnOnce<()>>::call_once"
);
assert_eq!(&strip_symbol("<std[550525b9dd91a68e]::thread::local::LocalKey<(arc_swap[1d34a79be67db79e]::ArcSwapAny<alloc[bc7f897b574022f6]::sync::Arc<sentry_core[1d5336878cce1456]::hub::Hub>>, core[bb3d6b31f0e973c8]::cell::Cell<bool>)>>::with::<<sentry_core[1d5336878cce1456]::hub::Hub>::with<<sentry_core[1d5336878cce1456]::hub::Hub>::with_active<sentry_core[1d5336878cce1456]::api::with_integration<sentry_panic[c87c9124ff32f50e]::PanicIntegration, sentry_panic[c87c9124ff32f50e]::panic_handler::{closure#0}, ()>::{closure#0}, ()>::{closure#0}, ()>::{closure#0}, ()>"), "<std::thread::local::LocalKey<(arc_swap::ArcSwapAny<alloc::sync::Arc<sentry_core::hub::Hub>>, core::cell::Cell<bool>)>>::with::<<sentry_core::hub::Hub>::with<<sentry_core::hub::Hub>::with_active<sentry_core::api::with_integration<sentry_panic::PanicIntegration, sentry_panic::panic_handler::{closure#0}, ()>::{closure#0}, ()>::{closure#0}, ()>::{closure#0}, ()>");
}

#[test]
fn test_parse_crate_name_none() {
assert_eq!(parse_crate_name("main"), None);
}

#[test]
fn test_parse_crate_name_newstyle() {
assert_eq!(
parse_crate_name("<failure::error::Error as core::convert::From<F>>::from"),
Some("failure".into())
);
}

#[test]
fn test_parse_crate_name_hash() {
assert_eq!(
parse_crate_name("backtrace[856cf81bbf211f65]::backtrace::libunwind::trace"),
Some("backtrace".into())
);
assert_eq!(
parse_crate_name("<backtrace[856cf81bbf211f65]::capture::Backtrace>::new"),
Some("backtrace".into())
);
}
}
31 changes: 31 additions & 0 deletions sentry-contexts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "sentry-contexts"
version = "0.36.0"
authors = ["Sentry <hello@sentry.io>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/getsentry/sentry-rust"
homepage = "https://sentry.io/welcome/"
description = """
Sentry integration for os, device, and rust contexts.
"""
build = "build.rs"
edition = "2021"
rust-version = "1.73"

[dependencies]
sentry-core = { version = "0.36.0", path = "../sentry-core" }
libc = "0.2.66"
hostname = "0.4"

[target."cfg(not(windows))".dependencies]
uname = "0.1.1"

[target."cfg(windows)".dependencies]
os_info = "3.5.0"

[build-dependencies]
rustc_version = "0.4.0"

[dev-dependencies]
sentry = { path = "../sentry", default-features = false, features = ["test"] }
21 changes: 21 additions & 0 deletions sentry-contexts/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Functional Software, Inc. dba Sentry

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
31 changes: 31 additions & 0 deletions sentry-contexts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<p align="center">
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
</a>
</p>

# Sentry Rust SDK: sentry-contexts

Adds Contexts to Sentry Events.

This integration is enabled by default in `sentry` and adds `device`, `os`
and `rust` contexts to Events, and also sets a `server_name` if it is not
already defined.

See the [Contexts Interface] documentation for more info.

## Examples

```rust
let integration = sentry_contexts::ContextIntegration::new().add_os(false);
let _sentry = sentry::init(sentry::ClientOptions::new().add_integration(integration));
```

[Contexts Interface]: https://develop.sentry.dev/sdk/event-payloads/contexts/

## Resources

License: MIT

- [Discord](https://discord.gg/ez5KZN7) server for project discussions.
- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates
66 changes: 66 additions & 0 deletions sentry-contexts/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;

use rustc_version::{version, version_meta, Channel};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let out_dir = env::var("OUT_DIR")?;
let dest_path = Path::new(&out_dir).join("constants.gen.rs");
let mut f = File::create(dest_path)?;

let target = env::var("TARGET")?;
let mut target_bits = target.split('-');
// `NoneError` does not implement `std::error::Error`
// See https://github.com/rust-lang/rust/issues/46871
let arch = target_bits.next().unwrap();
target_bits.next();
let platform = target_bits.next().unwrap();

writeln!(
f,
"/// The rustc version that was used to compile this crate."
)?;
writeln!(
f,
"pub const RUSTC_VERSION: Option<&'static str> = {};",
if let Ok(version) = version() {
format!("Some(\"{version}\")")
} else {
"None".into()
}
)?;
writeln!(
f,
"/// The rustc release channel that was used to compile this crate."
)?;
writeln!(
f,
"pub const RUSTC_CHANNEL: Option<&'static str> = {};",
if let Ok(version_meta) = version_meta() {
let chan = match version_meta.channel {
Channel::Dev => "dev",
Channel::Nightly => "nightly",
Channel::Beta => "beta",
Channel::Stable => "stable",
};
format!("Some(\"{chan}\")")
} else {
"None".into()
}
)?;

writeln!(f, "/// The platform identifier.")?;
writeln!(
f,
"#[allow(unused)] pub const PLATFORM: &str = \"{platform}\";"
)?;
writeln!(f, "/// The CPU architecture identifier.")?;
writeln!(f, "pub const ARCH: &str = \"{arch}\";")?;

println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=Cargo.toml");

Ok(())
}
107 changes: 107 additions & 0 deletions sentry-contexts/src/integration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use std::borrow::Cow;

use sentry_core::protocol::map::Entry;
use sentry_core::protocol::Event;
use sentry_core::{ClientOptions, Integration};

use crate::utils::{device_context, os_context, rust_context, server_name};

/// Adds Contexts to Sentry Events.
///
/// This integration is enabled by default in `sentry` and adds `device`, `os`
/// and `rust` contexts to Events, and also sets a `server_name` if it is not
/// already defined.
///
/// See the [Contexts Interface] documentation for more info.
///
/// # Examples
///
/// ```rust
/// let integration = sentry_contexts::ContextIntegration::new().add_os(false);
/// let _sentry = sentry::init(sentry::ClientOptions::new().add_integration(integration));
/// ```
///
/// [Contexts Interface]: https://develop.sentry.dev/sdk/event-payloads/contexts/
#[derive(Debug)]
pub struct ContextIntegration {
add_os: bool,
add_rust: bool,
add_device: bool,
}

impl Default for ContextIntegration {
fn default() -> Self {
Self {
add_os: true,
add_rust: true,
add_device: true,
}
}
}

impl ContextIntegration {
/// Create a new Context Integration.
pub fn new() -> Self {
Self::default()
}

/// Add `os` context, enabled by default.
#[must_use]
pub fn add_os(mut self, add_os: bool) -> Self {
self.add_os = add_os;
self
}
/// Add `rust` context, enabled by default.
#[must_use]
pub fn add_rust(mut self, add_rust: bool) -> Self {
self.add_rust = add_rust;
self
}

/// Add `device` context, enabled by default.
#[must_use]
pub fn add_device(mut self, add_device: bool) -> Self {
self.add_device = add_device;
self
}
}

impl Integration for ContextIntegration {
fn name(&self) -> &'static str {
"contexts"
}

fn setup(&self, options: &mut ClientOptions) {
if options.server_name.is_none() {
options.server_name = server_name().map(Cow::Owned);
}
}

fn process_event(
&self,
mut event: Event<'static>,
_cfg: &ClientOptions,
) -> Option<Event<'static>> {
if self.add_os {
if let Entry::Vacant(entry) = event.contexts.entry("os".to_string()) {
if let Some(os) = os_context() {
entry.insert(os);
}
}
}
if self.add_rust {
event
.contexts
.entry("rust".to_string())
.or_insert_with(rust_context);
}
if self.add_device {
event
.contexts
.entry("device".to_string())
.or_insert_with(device_context);
}

Some(event)
}
}
27 changes: 27 additions & 0 deletions sentry-contexts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! Adds Contexts to Sentry Events.
//!
//! This integration is enabled by default in `sentry` and adds `device`, `os`
//! and `rust` contexts to Events, and also sets a `server_name` if it is not
//! already defined.
//!
//! See the [Contexts Interface] documentation for more info.
//!
//! # Examples
//!
//! ```rust
//! let integration = sentry_contexts::ContextIntegration::new().add_os(false);
//! let _sentry = sentry::init(sentry::ClientOptions::new().add_integration(integration));
//! ```
//!
//! [Contexts Interface]: https://develop.sentry.dev/sdk/event-payloads/contexts/
#![doc(html_favicon_url = "https://sentry-brand.storage.googleapis.com/favicon.ico")]
#![doc(html_logo_url = "https://sentry-brand.storage.googleapis.com/sentry-glyph-black.png")]
#![warn(missing_docs)]

mod integration;
/// Contains functions to retrieve various contexts that can be useful
/// to attach to events
pub mod utils;

pub use integration::ContextIntegration;
Loading