Skip to content

Commit 6bfc290

Browse files
committed
feat(logs): allow slugs in logs list
1 parent 2a127c8 commit 6bfc290

8 files changed

Lines changed: 88 additions & 19 deletions

File tree

src/api/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ pub struct FetchEventsOptions<'a> {
14541454
/// Fields to include in the response
14551455
pub fields: &'a [&'a str],
14561456
/// Project ID to filter events by
1457-
pub project_id: &'a str,
1457+
pub project: Option<&'a str>,
14581458
/// Cursor for pagination
14591459
pub cursor: Option<&'a str>,
14601460
/// Query string to filter events
@@ -1480,8 +1480,14 @@ impl<'a> FetchEventsOptions<'a> {
14801480
params.push(format!("cursor={}", QueryArg(cursor)));
14811481
}
14821482

1483-
params.push(format!("project={}", QueryArg(self.project_id)));
1484-
params.push(format!("query={}", QueryArg(self.query)));
1483+
if let Some(project) = self.project {
1484+
if !project.is_empty() {
1485+
params.push(format!("project={}", QueryArg(project)));
1486+
}
1487+
}
1488+
if !self.query.is_empty() {
1489+
params.push(format!("query={}", QueryArg(self.query)));
1490+
}
14851491
params.push(format!("per_page={}", self.per_page));
14861492
params.push(format!("statsPeriod={}", QueryArg(self.stats_period)));
14871493
params.push(format!("sort={}", QueryArg(self.sort)));

src/commands/logs/list.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ fn validate_max_rows(s: &str) -> Result<usize> {
2020
}
2121
}
2222

23+
/// Check if a project identifier is numeric (project ID) or string (project slug)
24+
fn is_numeric_project_id(project: &str) -> bool {
25+
project.chars().all(|c| c.is_ascii_digit())
26+
}
27+
2328
/// Fields to fetch from the logs API
2429
const LOG_FIELDS: &[&str] = &[
2530
"sentry.item_id",
@@ -37,7 +42,7 @@ pub(super) struct ListLogsArgs {
3742
org: Option<String>,
3843

3944
#[arg(short = 'p', long = "project")]
40-
#[arg(help = "The project ID (slug not supported).")]
45+
#[arg(help = "The project ID (numeric) or project slug (string).")]
4146
project: Option<String>,
4247

4348
#[arg(long = "max-rows", default_value = "100")]
@@ -70,29 +75,41 @@ pub(super) fn execute(args: ListLogsArgs) -> Result<()> {
7075

7176
let api = Api::current();
7277

73-
let query = if args.query.is_empty() {
74-
None
78+
let (query, project_id) = if is_numeric_project_id(project) {
79+
// For numeric project IDs, pass as project parameter
80+
let query = if args.query.is_empty() {
81+
String::new()
82+
} else {
83+
args.query.clone()
84+
};
85+
(query, Some(project.as_str()))
7586
} else {
76-
Some(args.query.as_str())
87+
// For project slugs, include in query string
88+
let query = if args.query.is_empty() {
89+
format!("project:{project}")
90+
} else {
91+
format!("project:{project} {}", args.query)
92+
};
93+
(query, None)
7794
};
7895

79-
execute_single_fetch(&api, org, project, query, LOG_FIELDS, &args)
96+
execute_single_fetch(&api, org, &query, project_id, LOG_FIELDS, &args)
8097
}
8198

8299
fn execute_single_fetch(
83100
api: &Api,
84101
org: &str,
85-
project: &str,
86-
query: Option<&str>,
102+
query: &str,
103+
project_id: Option<&str>,
87104
fields: &[&str],
88105
args: &ListLogsArgs,
89106
) -> Result<()> {
90107
let options = FetchEventsOptions {
91108
dataset: Dataset::Logs,
92109
fields,
93-
project_id: project,
110+
project: project_id,
94111
cursor: None,
95-
query: query.unwrap_or(""),
112+
query,
96113
per_page: args.max_rows,
97114
stats_period: "90d",
98115
sort: "-timestamp",

tests/integration/_cases/logs/logs-list-help.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Options:
1515
in key:value format.
1616

1717
-p, --project <PROJECT>
18-
The project ID (slug not supported).
18+
The project ID (numeric) or project slug (string).
1919

2020
--auth-token <AUTH_TOKEN>
2121
Use the given Sentry auth token.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```
2+
$ sentry-cli logs list --org wat-org --project 12345
3+
? success
4+
No logs found
5+
6+
```

tests/integration/_cases/logs/logs-list-no-logs-found.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```
2-
$ sentry-cli logs list --org wat-org --project 12345
2+
$ sentry-cli logs list --org wat-org --project myproject
33
? success
44
No logs found
55

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```
2+
$ sentry-cli logs list --project 12345
3+
? success
4+
+------------------+---------------------------+----------+--------------------------+----------------------+
5+
| Item ID | Timestamp | Severity | Message | Trace |
6+
+------------------+---------------------------+----------+--------------------------+----------------------+
7+
| test-item-id-001 | 2025-01-15T10:30:00+00:00 | info | test_log_message_001 | test-trace-id-abc123 |
8+
| test-item-id-002 | 2025-01-15T10:31:00+00:00 | error | test_error_message_002 | test-trace-id-def456 |
9+
| test-item-id-003 | 2025-01-15T10:32:00+00:00 | warning | test_warning_message_003 | test-trace-id-ghi789 |
10+
+------------------+---------------------------+----------+--------------------------+----------------------+
11+
12+
```

tests/integration/_cases/logs/logs-list-with-data.trycmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```
2-
$ sentry-cli logs list
2+
$ sentry-cli logs list --org wat-org --project myproject
33
? success
44
+------------------+---------------------------+----------+--------------------------+----------------------+
55
| Item ID | Timestamp | Severity | Message | Trace |

tests/integration/logs.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::integration::{MockEndpointBuilder, TestManager};
22

33
#[test]
4-
fn command_logs_with_api_calls() {
4+
fn command_logs_with_api_calls_project_slug() {
55
TestManager::new()
66
.mock_endpoint(
77
MockEndpointBuilder::new(
88
"GET",
9-
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&project=wat-project&query=&per_page=100&statsPeriod=90d&sort=-timestamp"
9+
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&query=project:myproject&per_page=100&statsPeriod=90d&sort=-timestamp"
1010
)
1111
.with_response_file("logs/get-logs.json"),
1212
)
@@ -15,19 +15,47 @@ fn command_logs_with_api_calls() {
1515
}
1616

1717
#[test]
18-
fn command_logs_no_logs_found() {
18+
fn command_logs_with_api_calls_project_id() {
1919
TestManager::new()
2020
.mock_endpoint(
2121
MockEndpointBuilder::new(
2222
"GET",
23-
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&project=12345&query=&per_page=100&statsPeriod=90d&sort=-timestamp"
23+
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&project=12345&per_page=100&statsPeriod=90d&sort=-timestamp"
24+
)
25+
.with_response_file("logs/get-logs.json"),
26+
)
27+
.register_trycmd_test("logs/logs-list-with-data-project-id.trycmd")
28+
.with_default_token();
29+
}
30+
31+
#[test]
32+
fn command_logs_no_logs_found_project_slug() {
33+
TestManager::new()
34+
.mock_endpoint(
35+
MockEndpointBuilder::new(
36+
"GET",
37+
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&query=project:myproject&per_page=100&statsPeriod=90d&sort=-timestamp"
2438
)
2539
.with_response_body(r#"{"data": []}"#),
2640
)
2741
.register_trycmd_test("logs/logs-list-no-logs-found.trycmd")
2842
.with_default_token();
2943
}
3044

45+
#[test]
46+
fn command_logs_no_logs_found_project_id() {
47+
TestManager::new()
48+
.mock_endpoint(
49+
MockEndpointBuilder::new(
50+
"GET",
51+
"/api/0/organizations/wat-org/events/?dataset=logs&field=sentry.item_id&field=trace&field=severity&field=timestamp&field=message&project=12345&per_page=100&statsPeriod=90d&sort=-timestamp"
52+
)
53+
.with_response_body(r#"{"data": []}"#),
54+
)
55+
.register_trycmd_test("logs/logs-list-no-logs-found-project-id.trycmd")
56+
.with_default_token();
57+
}
58+
3159
#[test]
3260
fn command_logs_zero_max_rows() {
3361
TestManager::new().register_trycmd_test("logs/logs-list-with-zero-max-rows.trycmd");

0 commit comments

Comments
 (0)