Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Update `sample_rate` of _Dynamic Sampling Context (DSC)_ when making sampling decisions ([#4374](https://github.com/getsentry/sentry-dotnet/pull/4374))
- Crontabs now support day names (MON-FRI) and allow step values and ranges to be combined ([#4407](https://github.com/getsentry/sentry-dotnet/pull/4407))

## 5.13.0

Expand Down
12 changes: 7 additions & 5 deletions src/Sentry/SentryMonitorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,30 +57,32 @@ public partial class SentryMonitorOptions : ISentryJsonSerializable
// - Allows */n for step values where n must be any positive integer (except zero)
// - Allows single values within their valid ranges
// - Allows ranges (e.g., 8-10)
// - Allows step values with ranges (e.g., 8-18/4)
// - Allows lists of values and ranges (e.g., 6,8,9 or 8-10,12-14)
// - Allows weekday names (MON, TUE, WED, THU, FRI, SAT, SUN)
//
// Valid ranges for each field:
// - Minutes: 0-59
// - Hours: 0-23
// - Days: 1-31
// - Months: 1-12
// - Weekdays: 0-7 (0 and 7 both represent Sunday)
private const string ValidCrontabPattern = @"^(\*|(\*\/([1-9][0-9]*))|([0-5]?\d)(-[0-5]?\d)?)(,([0-5]?\d)(-[0-5]?\d)?)*(\s+)(\*|(\*\/([1-9][0-9]*))|([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)((,([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)((,([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|1[0-2])(-([1-9]|1[0-2]))?)((,([1-9]|1[0-2])(-([1-9]|1[0-2]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|[0-7](-[0-7])?)((,[0-7](-[0-7])?)*)?$";
// - Weekdays: 0-7 (0 and 7 both represent Sunday) or MON-SUN
private const string ValidCrontabPattern = @"^(\*(\/([1-9][0-9]*))?|([0-5]?\d)|([0-5]?\d)-([0-5]?\d)(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([0-5]?\d)|([0-5]?\d)-([0-5]?\d)(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([01]?\d|2[0-3])|([01]?\d|2[0-3])-([01]?\d|2[0-3])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([01]?\d|2[0-3])|([01]?\d|2[0-3])-([01]?\d|2[0-3])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([1-9]|[12]\d|3[01])|([1-9]|[12]\d|3[01])-([1-9]|[12]\d|3[01])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([1-9]|[12]\d|3[01])|([1-9]|[12]\d|3[01])-([1-9]|[12]\d|3[01])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([1-9]|1[0-2])|([1-9]|1[0-2])-([1-9]|1[0-2])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([1-9]|1[0-2])|([1-9]|1[0-2])-([1-9]|1[0-2])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|[0-7]|(MON|TUE|WED|THU|FRI|SAT|SUN)|[0-7]-[0-7](\/([1-9][0-9]*))?|(MON|TUE|WED|THU|FRI|SAT|SUN)-(MON|TUE|WED|THU|FRI|SAT|SUN)(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|[0-7]|(MON|TUE|WED|THU|FRI|SAT|SUN)|[0-7]-[0-7](\/([1-9][0-9]*))?|(MON|TUE|WED|THU|FRI|SAT|SUN)-(MON|TUE|WED|THU|FRI|SAT|SUN)(\/([1-9][0-9]*))?))*$";

private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None;
private string? _crontab;
private int? _interval;
private SentryMonitorInterval? _unit;

#if NET9_0_OR_GREATER
[GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)]
[GeneratedRegex(ValidCrontabPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)]
private static partial Regex ValidCrontab { get; }
#elif NET8_0
[GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)]
[GeneratedRegex(ValidCrontabPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)]
private static partial Regex ValidCrontabRegex();
private static readonly Regex ValidCrontab = ValidCrontabRegex();
#else
private static readonly Regex ValidCrontab = new(ValidCrontabPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant);
private static readonly Regex ValidCrontab = new(ValidCrontabPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);
#endif

/// <summary>
Expand Down
80 changes: 65 additions & 15 deletions test/Sentry.Tests/SentryMonitorOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ day of month 1-31
/ step values
*/
[Theory]
[InlineData("* * * * *")]
[InlineData("0 0 1 1 *")]
[InlineData("0 0 1 * 0")]
[InlineData("59 23 31 12 7")]
[InlineData("0 */2 * * *")]
[InlineData("0 8-10 * * *")]
[InlineData("0 6,8,9 * * *")]
[InlineData("* * * * *")] // Every minute
[InlineData("0 0 1 1 *")] // At midnight on January 1st
[InlineData("0 0 1 * 0")] // At midnight on the first day of the month if it's Sunday
[InlineData("59 23 31 12 7")] // At 23:59 on December 31st if it's Sunday
[InlineData("0 */2 * * *")] // Every 2 hours
[InlineData("0 8-10 * * *")] // At 8, 9, and 10 AM every day
[InlineData("0 6,8,9 * * *")] // At 6, 8, and 9 AM every day
// Step values (*/n)
[InlineData("*/15 * * * *")] // Every 15 minutes
[InlineData("0 */6 * * *")] // Every 6 hours
Expand Down Expand Up @@ -53,6 +53,34 @@ day of month 1-31
[InlineData("0 0 1 1 0")] // Minimum values
[InlineData("*/1 * * * *")] // Step of 1
[InlineData("* * 31 */2 *")] // 31st of every other month
// Weekday names
[InlineData("0 0 * * MON")] // Monday only (weekday name)
[InlineData("0 9 * * MON-FRI")] // 9 AM, Monday to Friday (weekday range)
[InlineData("0 18 * * MON-FRI")] // 6 PM, Monday to Friday (weekday range)
[InlineData("0 0 * * MON-FRI")] // Midnight, Monday to Friday (weekday range)
[InlineData("0 20 * * MON-FRI")] // 8 PM, Monday to Friday (weekday range)
// Step values with ranges
[InlineData("0-30/15 * * * *")] // Every 15 minutes from 0-30
[InlineData("0-59/10 * * * *")] // Every 10 minutes from 0-59
[InlineData("0-45/5 * * * *")] // Every 5 minutes from 0-45
[InlineData("* 8-18/2 * * *")] // Every 2 hours from 8-18
[InlineData("* 0-23/6 * * *")] // Every 6 hours from 0-23
[InlineData("* 9-17/1 * * *")] // Every hour from 9-17
[InlineData("* * 1-15/3 * *")] // Every 3 days from 1-15
[InlineData("* * 1-31/7 * *")] // Every 7 days from 1-31
[InlineData("* * 10-20/2 * *")] // Every 2 days from 10-20
[InlineData("* * * 1-6/2 *")] // Every 2 months from January-June
[InlineData("* * * 1-12/3 *")] // Every 3 months from January-December
[InlineData("* * * 3-9/1 *")] // Every month from March-September
[InlineData("* * * * 1-5/2")] // Every 2 weekdays from 1-5 (Mon-Fri)
[InlineData("* * * * 0-6/3")] // Every 3 days of week from (Sun-Sat)
[InlineData("* * * * MON-FRI/2")] // Every 2 weekdays from Mon-Fri
[InlineData("* * * * MON-SUN/3")] // Every 3 days from Mon-Sun
// Complex combinations with step values and ranges
[InlineData("0-30/15 8-18/2 * * *")] // Every 15 min from 0-30, every 2 hours from 8-18
[InlineData("0-45/5 9-17/1 1-15/3 * *")] // Complex combination
[InlineData("*/10 8-18/4 1-31/7 1-12/3 MON-FRI/2")] // All fields with step values and ranges
[InlineData("1-2/3 * * * *")] // Step value larger than range
public void Interval_ValidCrontab_DoesNotThrow(string crontab)
{
// Arrange
Expand All @@ -74,14 +102,14 @@ public void Interval_SetMoreThanOnce_Throws()
}

[Theory]
[InlineData("")]
[InlineData("not a crontab")]
[InlineData("* * a * *")]
[InlineData("60 * * * *")]
[InlineData("* 24 * * *")]
[InlineData("* * 32 * *")]
[InlineData("* * * 13 *")]
[InlineData("* * * * 8")]
[InlineData("")] // Empty string
[InlineData("not a crontab")] // Not a valid crontab format
[InlineData("* * a * *")] // Invalid character for day-of-month
[InlineData("60 * * * *")] // Minute value exceeds 59
[InlineData("* 24 * * *")] // Hour value exceeds 23
[InlineData("* * 32 * *")] // Day of month value exceeds 31
[InlineData("* * * 13 *")] // Month value exceeds 12
[InlineData("* * * * 8")] // Day of week value exceeds 7
// Invalid step values
[InlineData("*/0 * * * *")] // Step value cannot be 0
// Invalid ranges
Expand All @@ -101,6 +129,28 @@ public void Interval_SetMoreThanOnce_Throws()
[InlineData("*/* * * * *")] // Invalid step format
[InlineData(",1,2 * * * *")] // Leading comma
[InlineData("1,2, * * * *")] // Trailing comma
// Invalid step values with ranges
[InlineData("0-60/15 * * * *")] // Minute range exceeds 59
[InlineData("0-30/0 * * * *")] // Step value cannot be 0
[InlineData("0-30/-5 * * * *")] // Negative step value
[InlineData("* 8-25/2 * * *")] // Hour range exceeds 23
[InlineData("* 8-18/0 * * *")] // Step value cannot be 0
[InlineData("* * 0-31/3 * *")] // Day cannot be 0
[InlineData("* * 1-32/3 * *")] // Day range exceeds 31
[InlineData("* * * 0-12/2 *")] // Month cannot be 0
[InlineData("* * * 1-13/2 *")] // Month range exceeds 12
[InlineData("* * * * 0-8/2")] // Weekday range exceeds 7
[InlineData("* * * * MON-FRI/0")] // Step value cannot be 0
[InlineData("0-30//15 * * * *")] // Double slash
[InlineData("0-30/15/ * * * *")] // Trailing slash
[InlineData("0-30/15- * * * *")] // Incomplete range
// Invalid single value with step (should only allow step with * or ranges)
[InlineData("30/5 * * * *")] // Single minute with step
[InlineData("* 8/2 * * *")] // Single hour with step
[InlineData("* * 15/3 * *")] // Single day with step
[InlineData("* * * 6/2 *")] // Single month with step
[InlineData("* * * * 3/2")] // Single weekday with step
[InlineData("* * * * MON/2")] // Single weekday name with step
public void CaptureCheckIn_InvalidCrontabSet_Throws(string crontab)
{
// Arrange
Expand Down
Loading