-
-
Notifications
You must be signed in to change notification settings - Fork 232
feat(logs): Introduce logs command #2664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a960c3e
to
8d7b8a6
Compare
8d7b8a6
to
7f3cb0f
Compare
7f3cb0f
to
ff53f3e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code mostly looks reasonable to me, thanks! I have left a few mostly minor suggestions.
My main concern, though, and why I am requesting changes, is that I could not get this to run locally. I tried using the following command, but was getting a 400
error. I suspect that perhaps the endpoint we are hitting only takes project IDs, not slugs.
➜ sentry-cli git:(vg/add-logs-command) cargo run logs list --org sentry -p sentry
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.46s
Running `target/debug/sentry-cli logs list --org sentry -p sentry`
error: API request failed
Caused by:
sentry reported an error: Invalid project parameter. Values must be numbers. (http status: 400)
7a99398
to
57ca614
Compare
love this! what do you think about our other data types? what would be the best interface? something like this?
|
@JoshFerge Yep, something like that we already had in mind, i have a second draft PR that builds on top of the work from this one (#2666) and adds live tailing only for logs, but after that it should be relatively straight forward to support tailing for other data types. EDIT: not sure how "easy" it is to support traces and all, but issues should be no problem 😅 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking better, still have a couple questions though
? success | ||
+------------------+---------------------+----------+----------------------+---------------------+ | ||
| Item ID | Timestamp | Severity | Message | Trace | | ||
+------------------+---------------------+----------+----------------------+---------------------+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This output looks wrong. I would expect it to be the same as logs-list-with-data.trycmd
. Why is it different?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow this test wasn't even running because i have screwed it up, now all of the tests should be fixed
src/api/mod.rs
Outdated
|
||
impl Dataset { | ||
/// Returns the string representation of the dataset | ||
pub fn as_str(&self) -> &'static str { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can make this private
pub fn as_str(&self) -> &'static str { | |
fn as_str(&self) -> &'static str { |
Hey @vgrozdanic, I just attempted to test this PR locally, but it still does not seem to work as expected. I wanted to list these three logs. ![]() To list the logs, I ran the following command.
As you can see, the command says no logs were found, even though we should have found the three logs we can see on the page in Sentry. If you rerun the same command using
|
@szokeasaurusrex rust skill issue - i have found a bug in my code, working on a fix... I have found a couple of more mistakes in my code, but soon will push the updated version. Sorry to bother you this much, as you can tell, i haven't written much rust so far 😅 |
It's okay, no worries @vgrozdanic! But please, once you have fixed the problem, make sure you have a test case which validates that the command handles this type of response correctly |
a038c26
to
2ea949b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, still some comments to address before merge (but I'll take care of it)
src/commands/logs/list.rs
Outdated
let logs_to_show = &logs[..args.max_rows.min(logs.len())]; | ||
for log in logs_to_show { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's please rewrite this way, it is easier to read (this is what I meant with my take
suggestion in Slack)
let logs_to_show = &logs[..args.max_rows.min(logs.len())]; | |
for log in logs_to_show { | |
for log in logs.iter().take(args.max_rows) { |
? success | ||
Manage and query logs in Sentry. This command provides access to log entries. | ||
|
||
Usage: sentry-cli logs [OPTIONS] [COMMAND] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As [EXE]
adds .exe
on Windows, but evaluates to an empty string on other platforms, I believe that we can remove the logs-help-windows.trycmd
if we make this edit here
Usage: sentry-cli logs [OPTIONS] [COMMAND] | |
Usage: sentry-cli[EXE] logs [OPTIONS] [COMMAND] |
tests/integration/logs.rs
Outdated
#[cfg(not(windows))] | ||
manager.register_trycmd_test("logs/logs-help.trycmd"); | ||
#[cfg(windows)] | ||
manager.register_trycmd_test("logs/logs-help-windows.trycmd"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you make the requested change to logs-help.trycmd
, this should work
#[cfg(not(windows))] | |
manager.register_trycmd_test("logs/logs-help.trycmd"); | |
#[cfg(windows)] | |
manager.register_trycmd_test("logs/logs-help-windows.trycmd"); | |
manager.register_trycmd_test("logs/logs-help.trycmd"); |
src/commands/logs/mod.rs
Outdated
#[command(about = LIST_ABOUT)] | ||
#[command(long_about = format!("{LIST_ABOUT}. \ | ||
Query and filter log entries from your Sentry projects. \ | ||
Supports filtering by time period, log level, and custom queries."))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filtering by time period does not seem to be possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While playing around with this PR, I noticed that it appears we only support displaying logs from the last hour. We should probably instead allow the time period to be user-configured, either in this PR or in a future PR.
If we add the ability to user-configure the time period in a future PR, this PR should state clearly in the help text that for now, only logs from the past hour are displayed
src/commands/logs/list.rs
Outdated
cursor: None, | ||
query, | ||
per_page: Some(args.max_rows), | ||
stats_period: Some("1h"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value should either be user-configurable, or we should state in the command's --help
text that we only display logs from the last hour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So since the way we query the API is descending by time, and we limit to 100 rows via the per_page
parameter, we will always get the 100 most recent logs. I did a quick test on our API with 90d and 1h, and the timing is not really different, so I'd propose defaulting to 90d and not making it user-configurable for now, that would always give the user the most recent logs. What do you think about that @szokeasaurusrex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shellmayr, is the stats_period
even needed in the query string? If not, let's just delete the struct field completely and not send the parameter, assuming that the default time period the server assumes is reasonable. Otherwise, I would just hardcode the 90d
default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szokeasaurusrex I think it defaults to 14 days if not explicitly set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm okay, I guess either the server default or a 90 day default is reasonable in that case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather it be explicit, so the CLI doesn't rely on current, unspecified behaviour - that way the behaviour should stay the same if something changes in the API configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very wise
This reverts commit 519d55c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs a couple final polishing touches but otherwise looks close to done! Thanks Simon
src/api/mod.rs
Outdated
/// Query string to filter events | ||
pub query: Option<&'a str>, | ||
/// Number of events per page (default: 100) | ||
pub per_page: Option<usize>, | ||
/// Time period for stats (default: "1h") | ||
pub stats_period: Option<&'a str>, | ||
/// Sort order (default: "-timestamp") | ||
pub sort: Option<&'a str>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, all of these fields appear to always be being set to a Some
variant, so the default value never ends up being used.
Therefore, I would like to see the struct fields be made non-optional, so we can also get rid of the defaults in the code, and make the API simpler. We can always later make the fields optional, as needed.
/// Query string to filter events | |
pub query: Option<&'a str>, | |
/// Number of events per page (default: 100) | |
pub per_page: Option<usize>, | |
/// Time period for stats (default: "1h") | |
pub stats_period: Option<&'a str>, | |
/// Sort order (default: "-timestamp") | |
pub sort: Option<&'a str>, | |
/// Query string to filter events | |
pub query: &'a str, | |
/// Number of events per page | |
pub per_page: usize, | |
/// Time period for stats | |
pub stats_period: &'a str, | |
/// Sort order | |
pub sort: &'a str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szokeasaurusrex Calls to the endpoint don't need to have a query, stats_period, per_page or sort. What are we achieving by making them non-optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to keep the Sentry CLI code simple. Making these optional means we have to add code paths to Sentry CLI, which for now, are not being used, because we always set the query
, stats_period
, per_page
, and sort
. Rust, however, still forces us to handle the cases of these being None
(even though, in reality, they never are None
).
As a result, we have code paths, which are completely unused, to handle the None
cases. In the case of the query
, we only provide it if it is there. For stats_period
, per_page
, and sort
, we have default values defined, which are never used. From reading the code, however, it is not immediately clear that these default values are never used.
The problem with keeping them as Option
is just that it increases complexity and it makes it harder to see where the actual values get defined. I understand that the values are optional from the server perspective, but that absolutely does not mean that we need to make them optional in the CLI's API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shellmayr As an alternative, I would also be okay with just hardcoding the defaults and removing these fields from the struct completely. I'm okay with whatever you think is more reasonable, just want to avoid the Option
when its adding currently unnecessary complexity by handling cases which never occur.
params.push(format!("statsPeriod={}", self.stats_period.unwrap_or("1h"))); | ||
|
||
params.push(format!("sort={}", self.sort.unwrap_or("-timestamp"))); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Default Parameter Inclusion Bug
The FetchEventsOptions::to_query_params
method incorrectly includes per_page
, statsPeriod
, and sort
parameters in the query string with default values, even when their Option
fields are None
. This is due to the use of unwrap_or()
, which forces their inclusion, contradicting their intended optional behavior and the API's design. In contrast, cursor
and query
parameters are correctly added only when explicitly provided.
params.push(format!("project={}", QueryArg(self.project_id))); | ||
params.push(format!("query={}", QueryArg(self.query))); | ||
params.push(format!("per_page={}", self.per_page)); | ||
params.push(format!("statsPeriod={}", QueryArg(self.stats_period))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Query Parameter Inclusion Bug
The FetchEventsOptions::to_query_params
method unconditionally adds a query=
parameter to the URL, even when self.query
is empty. This is inconsistent with existing API patterns (e.g., list_organization_project_issues
) and causes integration test failures, as test mocks expect the query
parameter to be omitted when no query is provided. The query
parameter should only be included if self.query
is not empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀 🚀 🚀
Thanks for bearing with me @vgrozdanic and @shellmayr!
tests/integration/logs.rs
Outdated
.mock_endpoint( | ||
MockEndpointBuilder::new( | ||
"GET", | ||
"/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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm @shellmayr, I might have been wrong about the query
not needing to be optional. Looks like this test is failing now because the CLI is adding an empty query=&
parameter to this URL, causing the 501
error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that should be fine, fixing that test now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is the same problem with the test above it
Add a new
logs
subcommand to the Sentry CLI, allowing users to manage and query logs from their organizations.This includes a
list
command to fetch and display log entries with options for filtering and pagination.Implemented common arguments for logs and integrated them into the command structure. Also added integration tests for the new functionality.
Part of #2661
Live tailing will be done as a part of separate PR to make this a bit easier to review, it's already very large change.