Skip to content

Commit 7a99398

Browse files
committed
fixes
1 parent ff53f3e commit 7a99398

File tree

11 files changed

+166
-136
lines changed

11 files changed

+166
-136
lines changed

src/api/mod.rs

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,60 +1225,14 @@ impl<'a> AuthenticatedApi<'a> {
12251225

12261226
Ok(rv)
12271227
}
1228-
}
1229-
1230-
/// Options for fetching organization events
1231-
#[derive(Debug, Default)]
1232-
pub struct FetchEventsOptions<'a> {
1233-
/// Project ID to filter events by
1234-
pub project_id: Option<&'a str>,
1235-
/// Cursor for pagination
1236-
pub cursor: Option<&'a str>,
1237-
/// Query string to filter events
1238-
pub query: Option<&'a str>,
1239-
/// Number of events per page (default: 100)
1240-
pub per_page: Option<usize>,
1241-
/// Time period for stats (default: "1h")
1242-
pub stats_period: Option<&'a str>,
1243-
/// Sort order (default: "-timestamp")
1244-
pub sort: Option<&'a str>,
1245-
}
12461228

1247-
impl<'a> AuthenticatedApi<'a> {
12481229
/// Fetch organization events from the specified dataset
12491230
pub fn fetch_organization_events(
12501231
&self,
12511232
org: &str,
1252-
dataset: &str,
1253-
fields: &[&str],
1254-
options: FetchEventsOptions,
1233+
options: &FetchEventsOptions,
12551234
) -> ApiResult<Vec<LogEntry>> {
1256-
let mut params = vec![format!("dataset={}", QueryArg(dataset))];
1257-
1258-
for field in fields {
1259-
params.push(format!("field={}", QueryArg(field)));
1260-
}
1261-
1262-
if let Some(cursor) = options.cursor {
1263-
params.push(format!("cursor={}", QueryArg(cursor)));
1264-
}
1265-
1266-
if let Some(project_id) = options.project_id {
1267-
params.push(format!("project={}", QueryArg(project_id)));
1268-
}
1269-
1270-
if let Some(query) = options.query {
1271-
params.push(format!("query={}", QueryArg(query)));
1272-
}
1273-
1274-
params.push(format!("per_page={}", options.per_page.unwrap_or(100)));
1275-
params.push(format!(
1276-
"statsPeriod={}",
1277-
options.stats_period.unwrap_or("1h")
1278-
));
1279-
params.push("referrer=sentry-cli-tail".to_owned());
1280-
params.push(format!("sort={}", options.sort.unwrap_or("-timestamp")));
1281-
1235+
let params = options.to_query_params();
12821236
let url = format!(
12831237
"/organizations/{}/events/?{}",
12841238
PathArg(org),
@@ -1459,6 +1413,71 @@ impl<'a> AuthenticatedApi<'a> {
14591413
}
14601414
}
14611415

1416+
/// Available datasets for fetching organization events
1417+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1418+
pub enum Dataset {
1419+
/// Our logs dataset
1420+
OurLogs,
1421+
}
1422+
1423+
impl Dataset {
1424+
/// Returns the string representation of the dataset
1425+
pub fn as_str(&self) -> &'static str {
1426+
match self {
1427+
Dataset::OurLogs => "ourlogs",
1428+
}
1429+
}
1430+
}
1431+
/// Options for fetching organization events
1432+
pub struct FetchEventsOptions<'a> {
1433+
/// Dataset to fetch events from
1434+
pub dataset: Dataset,
1435+
/// Fields to include in the response
1436+
pub fields: &'a [&'a str],
1437+
/// Project ID to filter events by
1438+
pub project_id: Option<&'a str>,
1439+
/// Cursor for pagination
1440+
pub cursor: Option<&'a str>,
1441+
/// Query string to filter events
1442+
pub query: Option<&'a str>,
1443+
/// Number of events per page (default: 100)
1444+
pub per_page: Option<usize>,
1445+
/// Time period for stats (default: "1h")
1446+
pub stats_period: Option<&'a str>,
1447+
/// Sort order (default: "-timestamp")
1448+
pub sort: Option<&'a str>,
1449+
}
1450+
1451+
impl<'a> FetchEventsOptions<'a> {
1452+
/// Generate query parameters as a vector of strings
1453+
pub fn to_query_params(&self) -> Vec<String> {
1454+
let mut params = vec![format!("dataset={}", QueryArg(self.dataset.as_str()))];
1455+
1456+
for field in self.fields {
1457+
params.push(format!("field={}", QueryArg(field)));
1458+
}
1459+
1460+
if let Some(cursor) = self.cursor {
1461+
params.push(format!("cursor={}", QueryArg(cursor)));
1462+
}
1463+
1464+
if let Some(project_id) = self.project_id {
1465+
params.push(format!("project={}", QueryArg(project_id)));
1466+
}
1467+
1468+
if let Some(query) = self.query {
1469+
params.push(format!("query={}", QueryArg(query)));
1470+
}
1471+
1472+
params.push(format!("per_page={}", self.per_page.unwrap_or(100)));
1473+
params.push(format!("statsPeriod={}", self.stats_period.unwrap_or("1h")));
1474+
1475+
params.push(format!("sort={}", self.sort.unwrap_or("-timestamp")));
1476+
1477+
params
1478+
}
1479+
}
1480+
14621481
impl RegionSpecificApi<'_> {
14631482
fn request(&self, method: Method, url: &str) -> ApiResult<ApiRequest> {
14641483
self.api

src/commands/logs/common_args.rs

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/commands/logs/list.rs

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,59 @@
11
use anyhow::Result;
22
use clap::Args;
33

4-
use crate::api::{Api, FetchEventsOptions};
4+
use crate::api::{Api, Dataset, FetchEventsOptions};
55
use crate::config::Config;
66
use crate::utils::formatting::Table;
77

8-
use super::common_args::CommonLogsArgs;
8+
/// Fields to fetch from the logs API
9+
const LOG_FIELDS: &[&str] = &[
10+
"sentry.item_id",
11+
"trace",
12+
"severity",
13+
"timestamp",
14+
"message",
15+
];
916

1017
/// Arguments for listing logs
1118
#[derive(Args)]
1219
pub(super) struct ListLogsArgs {
13-
#[command(flatten)]
14-
pub(super) common: CommonLogsArgs,
20+
#[arg(short = 'o', long = "org")]
21+
#[arg(help = "The organization ID or slug.")]
22+
org: Option<String>,
1523

16-
#[arg(long = "max-rows")]
17-
#[arg(help = "Maximum number of rows to print.")]
18-
pub(super) max_rows: Option<usize>,
24+
#[arg(short = 'p', long = "project")]
25+
#[arg(help = "The project ID (slug not supported).")]
26+
project: Option<String>,
1927

20-
#[arg(long = "per-page", default_value = "100")]
21-
#[arg(help = "Number of log entries per request (max 1000).")]
22-
pub(super) per_page: usize,
28+
#[arg(long = "max-rows", default_value = "100")]
29+
#[arg(help = "Maximum number of log entries to fetch and display (max 1000).")]
30+
max_rows: usize,
2331

2432
#[arg(long = "query", default_value = "")]
2533
#[arg(help = "Query to filter logs. Example: \"level:error\"")]
26-
pub(super) query: String,
27-
28-
#[arg(long = "live")]
29-
#[arg(help = "Live-tail logs (not implemented yet).")]
30-
pub(super) live: bool,
34+
query: String,
3135
}
3236

3337
pub(super) fn execute(args: ListLogsArgs) -> Result<()> {
3438
let config = Config::current();
3539
let (default_org, default_project) = config.get_org_and_project_defaults();
3640

37-
let org = args.common.org.or(default_org).ok_or_else(|| {
38-
anyhow::anyhow!("No organization specified. Use --org or set a default in config.")
39-
})?;
40-
let project = args.common.project.or(default_project).ok_or_else(|| {
41-
anyhow::anyhow!("No project specified. Use --project or set a default in config.")
42-
})?;
41+
let org = args
42+
.org
43+
.as_ref()
44+
.or(default_org.as_ref())
45+
.ok_or_else(|| {
46+
anyhow::anyhow!("No organization specified. Use --org or set a default in config.")
47+
})?
48+
.to_owned();
49+
let project = args
50+
.project
51+
.as_ref()
52+
.or(default_project.as_ref())
53+
.ok_or_else(|| {
54+
anyhow::anyhow!("No project specified. Use --project or set a default in config.")
55+
})?
56+
.to_owned();
4357

4458
let api = Api::current();
4559

@@ -48,25 +62,32 @@ pub(super) fn execute(args: ListLogsArgs) -> Result<()> {
4862
} else {
4963
Some(args.query.as_str())
5064
};
51-
let fields = [
52-
"sentry.item_id",
53-
"trace",
54-
"severity",
55-
"timestamp",
56-
"message",
57-
];
5865

66+
execute_single_fetch(&api, &org, &project, query, LOG_FIELDS, &args)
67+
}
68+
69+
fn execute_single_fetch(
70+
api: &Api,
71+
org: &str,
72+
project: &str,
73+
query: Option<&str>,
74+
fields: &[&str],
75+
args: &ListLogsArgs,
76+
) -> Result<()> {
5977
let options = FetchEventsOptions {
60-
project_id: Some(&project),
78+
dataset: Dataset::OurLogs,
79+
fields,
80+
project_id: Some(project),
81+
cursor: None,
6182
query,
62-
per_page: Some(args.per_page),
83+
per_page: Some(args.max_rows),
6384
stats_period: Some("1h"),
64-
..Default::default()
85+
sort: Some("-timestamp"),
6586
};
6687

6788
let logs = api
6889
.authenticated()?
69-
.fetch_organization_events(&org, "ourlogs", &fields, options)?;
90+
.fetch_organization_events(org, &options)?;
7091

7192
let mut table = Table::new();
7293
table
@@ -77,9 +98,7 @@ pub(super) fn execute(args: ListLogsArgs) -> Result<()> {
7798
.add("Message")
7899
.add("Trace");
79100

80-
let max_rows = std::cmp::min(logs.len(), args.max_rows.unwrap_or(usize::MAX));
81-
82-
if let Some(logs) = logs.get(..max_rows) {
101+
if let Some(logs) = logs.get(..args.max_rows) {
83102
for log in logs {
84103
let row = table.add_row();
85104
row.add(&log.item_id)

src/commands/logs/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
pub mod common_args;
2-
31
mod list;
42

53
use self::list::ListLogsArgs;
@@ -19,12 +17,12 @@ pub(super) struct LogsArgs {
1917
#[derive(Subcommand)]
2018
#[command(about = "Manage logs in Sentry")]
2119
#[command(long_about = "Manage and query logs in Sentry. \
22-
This command provides access to log entries and supports live-tailing functionality.")]
20+
This command provides access to log entries.")]
2321
enum LogsSubcommand {
2422
#[command(about = LIST_ABOUT)]
2523
#[command(long_about = format!("{LIST_ABOUT}. \
26-
Query and filter log entries from your Sentry projects. \
27-
Supports filtering by time period, log level, and custom queries."))]
24+
Query and filter log entries from your Sentry projects. \
25+
Supports filtering by time period, log level, and custom queries."))]
2826
List(ListLogsArgs),
2927
}
3028

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ Options:
1313
The organization ID or slug.
1414

1515
-p, --project <PROJECT>
16-
The project ID or slug.
17-
18-
--live
19-
Live-tail logs (not implemented yet).
16+
The project ID (slug not supported).
2017

2118
--header <KEY:VALUE>
2219
Custom headers that should be attached to all requests
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
$ sentry-cli logs list --org wat-org --project wat-project --max-rows 0
1+
$ sentry-cli logs list --org wat-org --project 12345 --max-rows 0
22
? success
33
No logs found

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ Usage: sentry-cli[EXE] logs list [OPTIONS]
66

77
Options:
88
--max-rows <MAX_ROWS>
9-
Maximum number of rows to print.
10-
11-
--per-page <PER_PAGE>
12-
Number of log entries per request (max 1000). [default: 100]
9+
Maximum number of log entries to fetch and display (max 1000). [default: 100]
1310

1411
--query <QUERY>
1512
Query to filter logs. Example: "level:error" [default: ]
@@ -18,10 +15,7 @@ Options:
1815
The organization ID or slug.
1916

2017
-p, --project <PROJECT>
21-
The project ID or slug.
22-
23-
--live
24-
Live-tail logs (not implemented yet).
18+
The project ID (slug not supported).
2519

2620
--header <KEY:VALUE>
2721
Custom headers that should be attached to all requests
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$ sentry-cli logs list --org wat-org --project 12345 --max-rows 50
2+
? success
3+
+------------------+---------------------+----------+----------------------+---------------------+
4+
| Item ID | Timestamp | Severity | Message | Trace |
5+
+------------------+---------------------+----------+----------------------+---------------------+
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
$ sentry-cli logs list --org wat-org --project wat-project
1+
$ sentry-cli logs list --org wat-org --project 12345
22
? success
3-
+------------------+---------------------+----------+----------------------+---------------------+
4-
| Item ID | Timestamp | Severity | Message | Trace |
5-
+------------------+---------------------+----------+----------------------+---------------------+
6-
| test-item-id-001 | 2025-01-15T10:30:00 | info | test_log_message_001 | test-trace-id-abc123 |
7-
| test-item-id-002 | 2025-01-15T10:31:00 | error | test_error_message_002 | test-trace-id-def456 |
3+
+------------------+---------------------+----------+--------------------------+---------------------+
4+
| Item ID | Timestamp | Severity | Message | Trace |
5+
+------------------+---------------------+----------+--------------------------+---------------------+
6+
| test-item-id-001 | 2025-01-15T10:30:00 | info | test_log_message_001 | test-trace-id-abc123 |
7+
| test-item-id-002 | 2025-01-15T10:31:00 | error | test_error_message_002 | test-trace-id-def456 |
88
| test-item-id-003 | 2025-01-15T10:32:00 | warning | test_warning_message_003 | test-trace-id-ghi789 |
9-
+------------------+---------------------+----------+----------------------+---------------------+
9+
+------------------+---------------------+----------+--------------------------+---------------------+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
$ sentry-cli logs list --org wat-org --project 12345 --max-rows 0
2+
? success
3+
No logs found

0 commit comments

Comments
 (0)