Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
536c4a1
Import offline caching POC (#1461)
JoshuaMoelans Nov 27, 2025
5742eda
breakpad: delete .dmp
jpnurmi Jan 15, 2026
6c75ccf
Flatten cache/
jpnurmi Jan 15, 2026
8f3ffd5
Add tests
jpnurmi Jan 16, 2026
3410b6c
Respect has_breakpad
jpnurmi Jan 23, 2026
799d219
SENTRY_TRANSPORT=none
jpnurmi Jan 23, 2026
3c40494
Fix set_file_mtime on Windows
jpnurmi Jan 23, 2026
a37ac86
Present cache_max_age in seconds
jpnurmi Jan 23, 2026
20815bd
Present max_cache_size in bytes
jpnurmi Jan 23, 2026
ef96547
Tweak docs & signatures
jpnurmi Jan 23, 2026
e96d9d4
Fix warning
jpnurmi Jan 23, 2026
93aefb2
Fix test_unit::cache_max_age
jpnurmi Jan 24, 2026
f4936d5
Fix sign conversion warning on Windows
jpnurmi Jan 24, 2026
7eddee4
Add changelog entry for offline caching feature
jpnurmi Jan 24, 2026
8d40ec1
Fix sign conversion warning in CleanDatabase call
jpnurmi Jan 24, 2026
b28663f
Clarify test_integration_cache
jpnurmi Jan 26, 2026
ab62f90
Change cache_max_age type from uint64_t to time_t
jpnurmi Jan 27, 2026
7bc7856
Merge remote-tracking branch 'upstream/master' into jpnurmi/feat/offl…
jpnurmi Feb 2, 2026
9e09bd0
Update CHANGELOG.md
jpnurmi Feb 2, 2026
747edb1
Log warning when envelope caching fails
jpnurmi Feb 3, 2026
b1a4671
Revise crashpad_backend_prune_database
jpnurmi Feb 3, 2026
b101fce
Fix cache size calculation to exclude pruned files
jpnurmi Feb 3, 2026
2e1c63b
Add NULL checks after path allocations in cache handling
jpnurmi Feb 3, 2026
e0cb46e
Add NULL check after path clone in sentry__cleanup_cache
jpnurmi Feb 3, 2026
317968c
Fix cache size pruning to remove all older entries once limit hit
jpnurmi Feb 3, 2026
e30846f
Add INVALID_HANDLE_VALUE check and use TEST_ASSERT for set_file_mtime
jpnurmi Feb 4, 2026
1d565d6
Remove redundant conditional around sentry__path_free
jpnurmi Feb 4, 2026
76a5f81
Replace crashpad prune conditions with custom implementations
jpnurmi Feb 4, 2026
f0e5075
Fix size_t conversion warning on 32-bit Windows
jpnurmi Feb 4, 2026
25da5e1
Add cache_max_items option for Android & Cocoa compat
jpnurmi Feb 4, 2026
fc0f6cc
Merge branch 'master' into jpnurmi/feat/offline-caching
jpnurmi Feb 5, 2026
57cb740
Merge remote-tracking branch 'upstream/master' into jpnurmi/feat/offl…
jpnurmi Feb 5, 2026
fd3c257
Update CHANGELOG.md
jpnurmi Feb 5, 2026
8a17813
Default cache max size/age to 0 (disabled)
jpnurmi Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

**Features**:

- Add new offline caching options to persist envelopes locally, currently supported with the `inproc` and `breakpad` backends: `sentry_options_set_cache_keep`, `sentry_options_set_cache_max_items`, `sentry_options_set_cache_max_size`, and `sentry_options_set_cache_max_age`. ([#1490](https://github.com/getsentry/sentry-native/pull/1490))

## 0.12.6

**Features**:
Expand Down
6 changes: 6 additions & 0 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ main(int argc, char **argv)
if (has_arg(argc, argv, "log-attributes")) {
sentry_options_set_logs_with_attributes(options, true);
}
if (has_arg(argc, argv, "cache-keep")) {
sentry_options_set_cache_keep(options, true);
sentry_options_set_cache_max_size(options, 4 * 1024 * 1024); // 4 MB
sentry_options_set_cache_max_age(options, 5 * 24 * 60 * 60); // 5 days
sentry_options_set_cache_max_items(options, 5);
}

if (has_arg(argc, argv, "enable-metrics")) {
sentry_options_set_enable_metrics(options, true);
Expand Down
46 changes: 46 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ extern "C" {
#include <inttypes.h>
#include <stdarg.h>
#include <stddef.h>
#include <time.h>

/* context type dependencies */
#ifdef _WIN32
Expand Down Expand Up @@ -1415,6 +1416,51 @@ SENTRY_API void sentry_options_set_symbolize_stacktraces(
SENTRY_API int sentry_options_get_symbolize_stacktraces(
const sentry_options_t *opts);

/**
* Enables or disables storing envelopes in a persistent cache.
*
* When enabled, envelopes are written to a `cache/` subdirectory within the
* database directory and retained regardless of send success or failure.
* The cache is cleared on startup based on the cache_max_items, cache_max_size,
* and cache_max_age options.
*/
SENTRY_API void sentry_options_set_cache_keep(
sentry_options_t *opts, int enabled);

/**
* Sets the maximum number of items in the cache directory.
* On startup, cached entries are removed from oldest to newest until the
* directory contains at most the specified number of items.
*
* Defaults to 30.
*/
SENTRY_API void sentry_options_set_cache_max_items(
sentry_options_t *opts, size_t items);

/**
* Sets the maximum size (in bytes) for the cache directory.
* On startup, cached entries are removed from oldest to newest until the
* directory size is within the max size limit.
*
* Defaults to 0 (no max size).
*/
SENTRY_API void sentry_options_set_cache_max_size(
sentry_options_t *opts, size_t bytes);

/**
* Sets the maximum age (in seconds) for cache entries in the cache directory.
* On startup, cached entries exceeding the max age limit are removed.
*
* Defaults to 0 (no max age).
*/
SENTRY_API void sentry_options_set_cache_max_age(
sentry_options_t *opts, time_t seconds);

/**
* Gets the caching mode for crash reports.
*/
SENTRY_API int sentry_options_get_cache_keep(const sentry_options_t *opts);

/**
* Adds a new attachment to be sent along.
*
Expand Down
81 changes: 76 additions & 5 deletions src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,68 @@ crashpad_backend_last_crash(sentry_backend_t *backend)
return crash_time;
}

// seconds-based alternative to crashpad::AgePruneCondition (days)
class MaxAgePruneCondition final : public crashpad::PruneCondition {
public:
explicit MaxAgePruneCondition(time_t max_age)
: max_age_(max_age)
, oldest_report_time_(time(nullptr) - max_age)
{
}

bool
ShouldPruneReport(
const crashpad::CrashReportDatabase::Report &report) override
{
return max_age_ > 0 && report.creation_time < oldest_report_time_;
}

private:
const time_t max_age_;
const time_t oldest_report_time_;
};

// bytes-based alternative to crashpad::DatabaseSizePruneCondition (kb)
class MaxSizePruneCondition final : public crashpad::PruneCondition {
public:
explicit MaxSizePruneCondition(size_t max_size)
: max_size_(max_size)
, measured_size_(0)
{
}

bool
ShouldPruneReport(
const crashpad::CrashReportDatabase::Report &report) override
{
measured_size_ += static_cast<size_t>(report.total_size);
return max_size_ > 0 && measured_size_ > max_size_;
}

private:
const size_t max_size_;
size_t measured_size_;
};

class MaxItemsPruneCondition final : public crashpad::PruneCondition {
public:
explicit MaxItemsPruneCondition(size_t max_items)
: max_items_(max_items)
, item_count_(0)
{
}

bool
ShouldPruneReport(const crashpad::CrashReportDatabase::Report &) override
{
return max_items_ > 0 && ++item_count_ > max_items_;
}

private:
const size_t max_items_;
size_t item_count_;
};

static void
crashpad_backend_prune_database(sentry_backend_t *backend)
{
Expand All @@ -753,11 +815,20 @@ crashpad_backend_prune_database(sentry_backend_t *backend)
// complete database to a maximum of 8M. That might still be a lot for
// an embedded use-case, but minidumps on desktop can sometimes be quite
// large.
data->db->CleanDatabase(60 * 60 * 24 * 2);
crashpad::BinaryPruneCondition condition(crashpad::BinaryPruneCondition::OR,
new crashpad::DatabaseSizePruneCondition(1024 * 8),
new crashpad::AgePruneCondition(2));
crashpad::PruneCrashReportDatabase(data->db, &condition);
SENTRY_WITH_OPTIONS (options) {
if (options->cache_max_age > 0) {
data->db->CleanDatabase(options->cache_max_age);
}

crashpad::BinaryPruneCondition condition(
crashpad::BinaryPruneCondition::OR,
new MaxItemsPruneCondition(options->cache_max_items),
new crashpad::BinaryPruneCondition(
crashpad::BinaryPruneCondition::OR,
new MaxSizePruneCondition(options->cache_max_size),
new MaxAgePruneCondition(options->cache_max_age)));
crashpad::PruneCrashReportDatabase(data->db, &condition);
}
}

#if defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_LINUX)
Expand Down
8 changes: 8 additions & 0 deletions src/path/sentry_path_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,14 @@ sentry__path_remove(const sentry_path_t *path)
return 1;
}

int
sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst)
{
int status;
EINTR_RETRY(rename(src->path, dst->path), &status);
return status == 0 ? 0 : 1;
}

int
sentry__path_create_dir_all(const sentry_path_t *path)
{
Expand Down
12 changes: 12 additions & 0 deletions src/path/sentry_path_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,18 @@ sentry__path_remove(const sentry_path_t *path)
return removal_success ? 0 : !is_last_error_path_not_found();
}

int
sentry__path_rename(const sentry_path_t *src, const sentry_path_t *dst)
{
wchar_t *src_w = src->path_w;
wchar_t *dst_w = dst->path_w;
if (!src_w || !dst_w) {
return 1;
}
// MOVEFILE_REPLACE_EXISTING allows overwriting the destination if it exists
return MoveFileExW(src_w, dst_w, MOVEFILE_REPLACE_EXISTING) ? 0 : 1;
}

int
sentry__path_create_dir_all(const sentry_path_t *path)
{
Expand Down
4 changes: 4 additions & 0 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ sentry_init(sentry_options_t *options)
backend->prune_database_func(backend);
}

if (options->cache_keep) {
sentry__cleanup_cache(options);
}

if (options->auto_session_tracking) {
sentry_start_session();
}
Expand Down
Loading
Loading