Skip to content

Commit 3bf4ded

Browse files
authored
Add service filter, use realtime for timestamp, add category to desktop file, improve summary
1 parent b43ad6c commit 3bf4ded

File tree

17 files changed

+162
-43
lines changed

17 files changed

+162
-43
lines changed

.eslintrc.cjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ module.exports = {
1212
parserOptions: {
1313
ecmaVersion: "latest",
1414
},
15+
rules: {
16+
"vue/require-v-for-key": "warn",
17+
},
1518
};

.github/workflows/release.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,3 @@ jobs:
3434
draft: true
3535
prerelease: true
3636
generate_release_notes: true
37-
38-
#TODO: Use tag as version for tauri packages

TODO.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
- [x] Priority
1515
- [x] Message text
16-
- [ ] Unit
16+
- [x] Unit
1717
- [ ] Time range
18+
- [ ] Transports
1819

1920
## Quick filter bar
2021

@@ -40,7 +41,7 @@
4041
## Automate relase process
4142

4243
- [x] Document build & publish process
43-
- [ ] Automate process
44+
- [ ] Automate process, partially done, missing aur upload
4445

4546
## Investigate possibility of flatpak
4647

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@
3333
"vite": "~3.2",
3434
"vue-tsc": "~1.0"
3535
}
36-
}
36+
}

package/journal-viewer.desktop

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#!/usr/bin/env xdg-open
21
[Desktop Entry]
32
Name=Journal Viewer
43
Comment=Visualize systemd logs
54
Exec=journal-viewer
65
Terminal=false
76
Icon=logviewer
87
StartupNotify=false
9-
Type=Application
8+
Type=Application
9+
Categories=Settings;

src-tauri/src/journal.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ use crate::journal_fields;
33
use crate::libsdjournal::*;
44
use crate::query::Query;
55
use crate::query_builder::QueryBuilder;
6+
use crate::unit::Unit;
67
use bitflags::bitflags;
78
use libc::c_void;
9+
use std::process::Command;
810

911
bitflags! {
1012
#[repr(C)]
@@ -74,6 +76,12 @@ impl Journal {
7476

7577
loop {
7678
let more = sd_journal_previous(self.ptr)?;
79+
80+
if !more {
81+
debug!("No more entries");
82+
break;
83+
}
84+
7785
if let Ok(updated_timestamp) = self.get_field(journal_fields::SOURCE_REALTIME_TIMESTAMP)
7886
{
7987
last_timestamp = updated_timestamp.parse().unwrap();
@@ -87,11 +95,6 @@ impl Journal {
8795
}
8896
}
8997

90-
if !more {
91-
debug!("No more entries");
92-
break;
93-
}
94-
9598
if q.limit > 0 && count >= q.limit {
9699
debug!("Reached limit of {}", q.limit);
97100
break;
@@ -105,14 +108,26 @@ impl Journal {
105108
let mut row: Vec<String> = Vec::with_capacity(q.fields.len());
106109

107110
for field in q.fields.iter() {
108-
match self.get_field(field) {
109-
Ok(data) => {
110-
row.push(data);
111-
}
112-
Err(e) => {
113-
row.push(String::new());
114-
warn!("Could not find the field: {}, JournalError: {}", &field, e);
111+
match field.as_str() {
112+
"__REALTIME" => {
113+
let mut realtime: u64 = 0;
114+
match sd_journal_get_realtime_usec(self.ptr, &mut realtime) {
115+
Err(JournalError(e)) => {
116+
row.push(String::new());
117+
warn!("Could not get realtime field, error: {}", e);
118+
}
119+
Ok(()) => row.push(realtime.to_string()),
120+
}
115121
}
122+
_ => match self.get_field(field) {
123+
Ok(data) => {
124+
row.push(data);
125+
}
126+
Err(e) => {
127+
row.push(String::new());
128+
warn!("Could not find the field: {}, JournalError: {}", &field, e);
129+
}
130+
},
116131
}
117132
}
118133

@@ -182,6 +197,20 @@ impl Journal {
182197
}
183198
}
184199
}
200+
201+
pub fn list_services() -> Vec<Unit> {
202+
let output = Command::new("systemctl")
203+
.arg("list-unit-files")
204+
.arg("*.service")
205+
.arg("-o")
206+
.arg("json")
207+
.output()
208+
.expect("Failed to execute command");
209+
210+
let stdout = String::from_utf8(output.stdout).unwrap();
211+
212+
serde_json::from_str(&stdout).unwrap()
213+
}
185214
}
186215

187216
impl Drop for Journal {

src-tauri/src/libsdjournal.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88

99
#[derive(Debug, Serialize)]
1010
#[serde(rename_all = "camelCase")]
11-
pub struct JournalError(i32);
11+
pub struct JournalError(pub i32);
1212

1313
impl Display for JournalError {
1414
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -171,7 +171,7 @@ pub fn sd_journal_seek_tail(sd_journal: *mut c_void) -> Result<(), JournalError>
171171
Ok(())
172172
}
173173

174-
pub fn sd_journal_add_conjunction(sd_journal: *mut c_void) -> Result<bool, JournalError> {
174+
pub fn sd_journal_add_conjunction(sd_journal: *mut c_void) -> Result<(), JournalError> {
175175
let ret: libc::c_int;
176176

177177
unsafe {
@@ -182,10 +182,10 @@ pub fn sd_journal_add_conjunction(sd_journal: *mut c_void) -> Result<bool, Journ
182182
return Err(JournalError(ret));
183183
}
184184

185-
Ok(ret > 0)
185+
Ok(())
186186
}
187187

188-
pub fn sd_journal_add_disjunction(sd_journal: *mut c_void) -> Result<bool, JournalError> {
188+
pub fn sd_journal_add_disjunction(sd_journal: *mut c_void) -> Result<(), JournalError> {
189189
let ret: libc::c_int;
190190

191191
unsafe {
@@ -196,11 +196,28 @@ pub fn sd_journal_add_disjunction(sd_journal: *mut c_void) -> Result<bool, Journ
196196
return Err(JournalError(ret));
197197
}
198198

199-
Ok(ret > 0)
199+
Ok(())
200200
}
201201

202202
pub fn sd_journal_flush_matches(sd_journal: *mut c_void) {
203203
unsafe {
204204
libsdjournal_bindings::sd_journal_flush_matches(sd_journal);
205205
}
206206
}
207+
208+
pub fn sd_journal_get_realtime_usec(
209+
sd_journal: *mut c_void,
210+
microseconds: &mut u64,
211+
) -> Result<(), JournalError> {
212+
let ret: libc::c_int;
213+
214+
unsafe {
215+
ret = libsdjournal_bindings::sd_journal_get_realtime_usec(sd_journal, microseconds);
216+
}
217+
218+
if ret < 0 {
219+
return Err(JournalError(ret));
220+
}
221+
222+
Ok(())
223+
}

src-tauri/src/libsdjournal_bindings.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,7 @@ extern "C" {
4949

5050
//void sd_journal_flush_matches(sd_journal *j);
5151
pub fn sd_journal_flush_matches(sd_journal: *mut c_void);
52+
53+
//int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *usec);
54+
pub fn sd_journal_get_realtime_usec(sd_journal: *mut c_void, microseconds: *mut u64) -> c_int;
5255
}

src-tauri/src/main.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod libsdjournal;
1111
mod libsdjournal_bindings;
1212
mod query;
1313
mod query_builder;
14+
mod unit;
1415

1516
#[macro_use]
1617
extern crate log;
@@ -23,6 +24,7 @@ use journal_entries::JournalEntries;
2324
use libsdjournal::JournalError;
2425
use serde::Deserialize;
2526
use tauri::async_runtime::Mutex;
27+
use unit::Unit;
2628

2729
fn main() {
2830
let env = Env::default()
@@ -41,7 +43,11 @@ fn main() {
4143
info!("Starting journal logger");
4244
tauri::Builder::default()
4345
.manage(Mutex::new(j))
44-
.invoke_handler(tauri::generate_handler![get_logs, get_summary])
46+
.invoke_handler(tauri::generate_handler![
47+
get_logs,
48+
get_summary,
49+
get_services
50+
])
4551
.run(tauri::generate_context!())
4652
.expect("error while running tauri application");
4753
}
@@ -54,6 +60,7 @@ pub struct JournalQuery {
5460
limit: u64,
5561
quick_search: String,
5662
reset_position: bool,
63+
service: String,
5764
}
5865

5966
#[tauri::command]
@@ -70,6 +77,7 @@ async fn get_logs(
7077
.with_quick_search(query.quick_search)
7178
.reset_position(query.reset_position)
7279
.with_priority_above_or_equal_to(query.priority)
80+
.with_unit(query.service)
7381
.build();
7482

7583
let lock = journal.lock().await;
@@ -79,8 +87,14 @@ async fn get_logs(
7987
Ok(logs)
8088
}
8189

90+
#[derive(Debug, Deserialize)]
91+
#[serde(rename_all = "camelCase")]
92+
pub struct SummaryQuery {
93+
priority: u32,
94+
}
95+
8296
#[tauri::command]
83-
async fn get_summary(query: JournalQuery) -> Result<JournalEntries, JournalError> {
97+
async fn get_summary(query: SummaryQuery) -> Result<JournalEntries, JournalError> {
8498
debug!("Getting summary...");
8599
let j = Journal::open(
86100
OpenFlags::SD_JOURNAL_LOCAL_ONLY
@@ -92,7 +106,7 @@ async fn get_summary(query: JournalQuery) -> Result<JournalEntries, JournalError
92106
let from = Utc::now() - Duration::days(1);
93107
let mut qb = QueryBuilder::default();
94108
let q = qb
95-
.with_fields(vec![journal_fields::SOURCE_REALTIME_TIMESTAMP.into()])
109+
.with_fields(vec!["__REALTIME".into()])
96110
.with_limit(10_000)
97111
.with_date_from(from.timestamp_micros() as u64)
98112
.with_priority_above_or_equal_to(query.priority)
@@ -103,3 +117,12 @@ async fn get_summary(query: JournalQuery) -> Result<JournalEntries, JournalError
103117

104118
Ok(logs)
105119
}
120+
121+
#[tauri::command]
122+
async fn get_services() -> Result<Vec<Unit>, JournalError> {
123+
debug!("Getting services...");
124+
let services = Journal::list_services();
125+
debug!("found {} services", services.len());
126+
127+
Ok(Journal::list_services())
128+
}

src-tauri/src/query_builder.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ impl QueryBuilder {
2121
"syslog".into(),
2222
"journal".into(),
2323
"stdout".into(),
24+
// "kernel".into(),
2425
],
2526
from_epoch: 0,
2627
quick_search: "".into(),
@@ -103,13 +104,12 @@ impl QueryBuilder {
103104
self
104105
}
105106

106-
pub fn with_unit(&mut self, unit: &str) -> &mut Self {
107-
let mut full_unit: String = String::from(unit);
108-
if !full_unit.contains('.') {
109-
full_unit.push_str(".service");
107+
pub fn with_unit(&mut self, mut unit: String) -> &mut Self {
108+
if !unit.is_empty() && !unit.contains('.') {
109+
unit.push_str(".service");
110110
}
111111

112-
self.query.unit = full_unit;
112+
self.query.unit = unit;
113113
self
114114
}
115115

0 commit comments

Comments
 (0)