Skip to content

Commit

Permalink
Added support for logging Jolly state (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
apgoetz authored Aug 6, 2023
1 parent a3924c5 commit 6ea1f2a
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 6 deletions.
57 changes: 54 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pulldown-cmark = "0.9"
url = "2"
once_cell = "1.18.0"
resvg = "0.34.0"
env_logger = "0.10.0"
log = "0.4.19"

[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2"
Expand Down
4 changes: 4 additions & 0 deletions docs/advanced.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Below are a couple of advanced tips and tricks for using Jolly:

# Troubleshooting

Don't forget that Jolly can log detailed trace events of its performance using `env_logger`. For more details, see the [logging documentation](config.md#log)

# Copying Links

Sometimes you don't need to open a Jolly entry, just determine the location
Expand Down
56 changes: 56 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ accent_color= "orange"
[config.ui.search]
text_size = 40 # make search window bigger

[config.log]
file = 'path/to/logfile'
filters = "debug"

### Jolly entries below...

```
Expand Down Expand Up @@ -269,3 +273,55 @@ JOLLY_DEFAULT_THEME=Adwaita cargo build

As a general rule, the jolly build script will warn if the
`JOLLY_DEFAULT_THEME` doesn't seem to be installed at compile time.

# <a name="log"></a> [config.log]

The `[config.log]` table contains settings that control error logging
and debugging of Jolly. By default, Jolly will display error messages
in the UI and print them to `stderr`, but this behavior can be
customized.

**Important Note** *When you are trying to troubleshoot a bug in
Jolly, you may be asked to supply logfiles from operating
Jolly. Please be aware that at higher log levels, Jolly will include
the Jolly entry targets, and whichever text was entered in the search
window. This may be considered sensitive information and you should
always review logs before sharing them.*

To customize behavior, use the following fields:

| field name | data type | description |
|------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `file` | *string* | Additional filename to write logs to. Always appends. |
| `filters` | *string* OR *string array* | [env_logger](https://docs.rs/env_logger/latest/env_logger/index.html#enabling-logging) filters for logging, by default, only log `error` |


As an example log file configuration, consider the following snippet:

```toml
[config.log]

# Jolly logs are stored in log file below
file = 'path/to/logfile'

# messages from jolly crate at debug level, and cosmic_text crate at trace level
filters = ["jolly=debug", "cosmic_text=trace"]

```


## `file` &mdash; *string*

Specify a filename for Jolly to write logs to. If the file cannot be
accessed then an error is returned. Jolly will always append to this
file if it already exists. Jolly will also continue to write trace
messages to `stderr`.

## `filters` &mdash; *string* OR *string array*

The `filters` key can be used to specify one or more
[env_logger](https://docs.rs/env_logger/latest/env_logger/index.html#enabling-logging)
filters. These filters are used to determine which log level is
set. By default, only `errors` are logged, which also generally would
appear in the UI.

42 changes: 42 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,33 @@ use toml;

pub const LOGFILE_NAME: &str = "jolly.toml";

// helper enum to allow decoding a scalar into a single vec
// original hint from here:
// https://github.com/Mingun/ksc-rs/blob/8532f701e660b07b6d2c74963fdc0490be4fae4b/src/parser.rs#L18-L42
// (MIT LICENSE)
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum OneOrMany<T> {
/// Single value
One(T),
/// Array of values
Vec(Vec<T>),
}
impl<T> From<OneOrMany<T>> for Vec<T> {
fn from(from: OneOrMany<T>) -> Self {
match from {
OneOrMany::One(val) => vec![val],
OneOrMany::Vec(vec) => vec,
}
}
}

pub fn one_or_many<'de, T: Deserialize<'de>, D: serde::Deserializer<'de>>(
d: D,
) -> Result<Vec<T>, D::Error> {
OneOrMany::deserialize(d).map(Vec::from)
}

// represents the data that is loaded from the main configuration file
// it will always have some settings internally, even if the config
// file is not found. if there is an error parsing the config, a
Expand Down Expand Up @@ -171,4 +198,19 @@ mod tests {

assert_ne!(settings.ui.search, Default::default());
}

#[test]
fn test_one_or_many() {
use super::one_or_many;
use serde::de::value::{MapDeserializer, SeqDeserializer, UnitDeserializer};

let _: Vec<()> = one_or_many(UnitDeserializer::<serde::de::value::Error>::new()).unwrap();

let seq_de = SeqDeserializer::<_, serde::de::value::Error>::new(std::iter::once(()));
one_or_many::<Vec<()>, _>(seq_de).unwrap();

let map_de =
MapDeserializer::<_, serde::de::value::Error>::new(std::iter::once(("a", "b")));
one_or_many::<Vec<()>, _>(map_de).unwrap_err();
}
}
12 changes: 12 additions & 0 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ pub enum EntryType {
SystemEntry(String),
}

impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EntryType::FileEntry(_) => f.write_str("FileEntry"),
EntryType::SystemEntry(_) => f.write_str("SystemEntry"),
}
}
}

impl StoreEntry {
// parse a toml value into a store entry
pub fn from_value(name: String, val: toml::Value) -> Result<Self, Error> {
Expand Down Expand Up @@ -311,6 +320,9 @@ impl StoreEntry {
EntryType::SystemEntry(_) => platform::system,
};
let selection = self.format_selection(searchtext);

::log::info!(r#"Selected Entry {}("{}")"#, &self.entry, selection);

func(&selection).map_err(Error::PlatformError)
}

Expand Down
11 changes: 10 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use ::log::trace;
use iced::widget::text_input;
use iced::widget::{Text, TextInput};
use iced::{clipboard, event, keyboard, subscription, widget, window};
Expand All @@ -19,6 +20,7 @@ mod custom;
mod entry;
pub mod error;
mod icon;
mod log;
mod platform;
mod search_results;
mod settings;
Expand Down Expand Up @@ -69,6 +71,7 @@ pub struct Jolly {

impl Jolly {
fn move_to_err(&mut self, err: error::Error) -> Command<<Jolly as Application>::Message> {
::log::error!("{err}");
self.store_state = StoreLoadedState::Finished(err);
Command::none()
}
Expand All @@ -87,6 +90,9 @@ impl Jolly {
if self.modifiers.command() {
let result = entry.format_selection(&self.searchtext);
let msg = format!("copied to clipboard: {}", &result);

::log::info!("{msg}");

let cmds = [
clipboard::write(result),
self.move_to_err(error::Error::FinalMessage(msg)),
Expand Down Expand Up @@ -120,10 +126,11 @@ impl Application for Jolly {
jolly.store_state = match config.store {
Ok(store) => {
let msg = format!("Loaded {} entries", store.len());

StoreLoadedState::LoadSucceeded(store, msg)
}
Err(e) => {
eprintln!("{e}");
::log::error!("{e}");
StoreLoadedState::Finished(e)
}
};
Expand All @@ -143,6 +150,8 @@ impl Application for Jolly {
}

fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
trace!("Received Message::{:?}", message);

// first, match the messages that would cause us to quit regardless of application state
match message {
Message::ExternalEvent(event::Event::Keyboard(e)) => {
Expand Down
Loading

0 comments on commit 6ea1f2a

Please sign in to comment.