Skip to content
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

[#396] Read and set LogLevel from environment variable #474

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ rustdocflags = ["-D", "warnings"]
[env]
RUST_TEST_THREADS = "1"
RUST_BACKTRACE = "1"
IOX2_LOG_LEVEL = "Trace"

[target.debug]
rustflags = ["-Z", "sanitizer=address"]
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

75 changes: 44 additions & 31 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

iceoryx2 stores all data in shared memory, which imposes certain restrictions.
Only data that is self-contained and does not use pointers to reference itself
is allowed. This is because shared memory is mapped at different offsets in
each process, rendering absolute pointers invalid. Additionally, if the data
is allowed. This is because shared memory is mapped at different offsets in each
process, rendering absolute pointers invalid. Additionally, if the data
structure uses the heap, it is stored locally within a process and cannot be
accessed by other processes. As a result, data types such as `String`, `Vec`,
or `HashMap` cannot be used as payload types.
accessed by other processes. As a result, data types such as `String`, `Vec`, or
`HashMap` cannot be used as payload types.

Additionally, every data type must be annotated with `#[repr(C)]`. The Rust
compiler may reorder the members of a struct, which can lead to undefined
behavior if another process expects a different ordering.

To address this, iceoryx2 provides shared-memory-compatible data types. You
can refer to the [complex data types example](examples/rust/complex_data_types),
To address this, iceoryx2 provides shared-memory-compatible data types. You can
refer to the [complex data types example](examples/rust/complex_data_types),
which demonstrates the use of `FixedSizeByteString` and `FixedSizeVec`.

## How To Define Custom Data Types (Rust)
Expand Down Expand Up @@ -58,24 +58,22 @@ insufficient, a new publisher with a larger `max_slice_len` can be created.

<!-- markdownlint-disable -->

> [!IMPORTANT]
> Be aware that the history of the old publisher is lost when it is
> [!IMPORTANT] Be aware that the history of the old publisher is lost when it is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no, does the script have some autocorrection?
This breaks the github alerts syntax. > [!IMPORTANT] must be on a separate line than the content

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zmostafa can you please revert those changes. It breaks the alert syntax. Please also have a look at the other open point.

Copy link
Author

@zmostafa zmostafa Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elBoberido hmmm, I think i did, are you sure you are looking at the latest commit?
I will check the other point about reverting the lazy init, did not see that

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zmostafa I hope to look at the latest commit 😅

I did a Ctrl+F5, which should reload everything and dismiss caches but I still see > [!IMPORTANT] on the same line as Be aware that ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note I believe this happens because the auto-corrector does not recognise GitHub-style admonitions, so you need to manually ensure these are on their own line otherwise the auto corrector will try and do line length correction with it on the same line.

Not ideal, I know, but the idea is for the corrector to fix around 90% of the issues which should save more time than manually fixing these small edge cases (e.g. manually fixing all line lengths).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we skip line breaks in the auto-corrector? It's just a styling issue, not a rendering issue and it seems to break the github admonitions.

Copy link
Contributor

@orecham orecham Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is the line length corrector causing this. If disabled, then users would need to ensure line length limits were maintained, either manually or with an IDE. I deduced that this would be a larger annoyance for contributors than fixing these more limited edge cases.

Of course, we could check if the auto corrector could be updated to recognise the GitHub admonitions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I would argue that one is not only an annoyance but breaks the rendering of the readme if the reviewer does not pay attention and the other one is a choice by ourselves and does not break the rendering if the line is too long. I would rather deactivate the line length detection than having to keep in mind to check for broken admonitions in the PR.

> removed.

> [!NOTE]
> We are also working on an API that does not require the user to
> [!NOTE] We are also working on an API that does not require the user to
> explicitly create a new publisher whenever the memory is insufficient. It
> would also solve the history issue.

<!-- markdownlint-enable -->

## How To Make 32-bit and 64-bit iceoryx2 Applications Interoperatable

This is currently not possible since we cannot guarantee to have the same
layout of the data structures in the shared memory. On 32-bit architectures
64-bit POD are aligned to a 4 byte boundary but to a 8 byte boundary on
64-bit architectures. Some additional work is required to make 32-bit and
64-bit applications interoperabel.
This is currently not possible since we cannot guarantee to have the same layout
of the data structures in the shared memory. On 32-bit architectures 64-bit POD
are aligned to a 4 byte boundary but to a 8 byte boundary on 64-bit
architectures. Some additional work is required to make 32-bit and 64-bit
applications interoperabel.

## My Transmission Type Is Too Large, Encounter Stack Overflow On Initialization

Expand All @@ -89,8 +87,8 @@ than the available stack size.
## 100% CPU Load When Using The WaitSet

The WaitSet wakes up whenever an attachment, such as a `Listener` or a `socket`,
has something to read. If you do not handle all notifications, for example,
with `Listener::try_wait_one()`, the WaitSet will wake up immediately again,
has something to read. If you do not handle all notifications, for example, with
`Listener::try_wait_one()`, the WaitSet will wake up immediately again,
potentially causing an infinite loop and resulting in 100% CPU usage.

## Does iceoryx2 Offer an Async API?
Expand Down Expand Up @@ -123,17 +121,31 @@ But you can also use a crate like [ctrlc](https://docs.rs/ctrlc/latest/ctrlc/).
## How to use `log` or `tracing` as default log backend

* **log**, add the feature flag `logger_log` to the dependency in `Cargo.toml`
```toml
iceoryx2 = { version = "0.1.0", features = ["logger_log"]}
```
```toml
iceoryx2 = { version = "0.1.0", features = ["logger_log"]}
```
* **tracing**, add the feature flag `logger_tracing` to the dependency in
`Cargo.toml`
```toml
iceoryx2 = { version = "0.1.0", features = ["logger_tracing"]}
```
```toml
iceoryx2 = { version = "0.1.0", features = ["logger_tracing"]}
```

## Supported log levels

iceoryx2 support different log levels
`Trace, Debug, Info, Warning, Error, Fatal`

## How to set the log level

iceoryx2 gets its default logging level from `.cargo/config.toml` , but this can
be over ridden by environment variable `IOX2_LOG_LEVEL` the developer can set
this environment variable with one of the supported levels.

`export IOX2_LOG_LEVEL=Trace`

Developers can also set the logging level from within the code which will take
precedence over the environment variable:

```rust
use iceoryx2::prelude::*

Expand All @@ -144,10 +156,10 @@ set_log_level(LogLevel::Trace);

## A crash leads to the failure `PublishSubscribeOpenError(UnableToOpenDynamicServiceInformation)`

When an application crashes, some resources may remain in the system and need
to be cleaned up. This issue is detected whenever a new iceoryx2 instance is
created, removed, or when someone opens the service that the crashed process
had previously opened. On the command line, you may see a message like this:
When an application crashes, some resources may remain in the system and need to
be cleaned up. This issue is detected whenever a new iceoryx2 instance is
created, removed, or when someone opens the service that the crashed process had
previously opened. On the command line, you may see a message like this:

```ascii
6 [W] "Node::<iceoryx2::service::ipc::Service>::cleanup_dead_nodes()"
Expand All @@ -157,9 +169,9 @@ had previously opened. On the command line, you may see a message like this:
```

However, for successful cleanup, the process attempting the cleanup must have
sufficient permissions to remove the stale resources of the dead process. If
the cleanup fails due to insufficient permissions, the process that attempted
the cleanup will continue without removing the resources.
sufficient permissions to remove the stale resources of the dead process. If the
cleanup fails due to insufficient permissions, the process that attempted the
cleanup will continue without removing the resources.

Generally, it is not necessary to manually clean up these resources, as other
processes should detect and handle the cleanup when creating or removing nodes,
Expand All @@ -169,6 +181,7 @@ Nevertheless, there are three different approaches to initiate stale resource
cleanup:

1. **Using the iceoryx2 API**:

```rust
Node::<ipc::Service>::list(Config::global_config(), |node_state| {
if let NodeState::<ipc::Service>::Dead(view) = node_state {
Expand All @@ -184,6 +197,6 @@ cleanup:
2. **Using the command line tool**: `iox2 node -h` (NOT YET IMPLEMENTED)

3. **Manual cleanup**: Stop all running services and remove all shared memory
files with the `iox2` prefix from:
files with the `iox2` prefix from:
* POSIX: `/dev/shm/`, `/tmp/iceoryx2`
* Windows: `c:\Temp\iceoryx2`
1 change: 1 addition & 0 deletions doc/release-notes/iceoryx2-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* Bazel support for the Rust crates [#349](https://github.com/eclipse-iceoryx/iceoryx2/issues/349)
* Remove ACL dependency [#457](https://github.com/eclipse-iceoryx/iceoryx2/issues/457)
* Publish Subscribe Header contains number of elements contained in a `Sample` [#498](https://github.com/eclipse-iceoryx/iceoryx2/issues/498)
* Read LogLevel from environment variable [#396](https://github.com/eclipse-iceoryx/iceoryx2/issues/396)

### Workflow

Expand Down
1 change: 1 addition & 0 deletions iceoryx2-bb/log/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ rust_library(
deps = [
"//iceoryx2-pal/concurrency-sync:iceoryx2-pal-concurrency-sync",
"@crate_index//:termsize",
"@crate_index//:once_cell",
],
)
1 change: 1 addition & 0 deletions iceoryx2-bb/log/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ iceoryx2-pal-concurrency-sync = { workspace = true }
termsize = { workspace = true }
log = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }
once_cell = { workspace = true }
44 changes: 38 additions & 6 deletions iceoryx2-bb/log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ use std::{
sync::{atomic::Ordering, Once},
};

use once_cell::sync::Lazy;
use std::env;

#[cfg(feature = "logger_tracing")]
static DEFAULT_LOGGER: logger::tracing::Logger = logger::tracing::Logger::new();

Expand All @@ -159,11 +162,17 @@ static DEFAULT_LOGGER: logger::log::Logger = logger::log::Logger::new();
#[cfg(not(any(feature = "logger_log", feature = "logger_tracing")))]
static DEFAULT_LOGGER: logger::console::Logger = logger::console::Logger::new();

const DEFAULT_LOG_LEVEL: u8 = LogLevel::Info as u8;
const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info;
pub static ENV_LOG_LEVEL: Lazy<LogLevel> = Lazy::new(|| {
env::var("IOX2_LOG_LEVEL")
.map(|log_level| LogLevel::from_str_fuzzy(&log_level))
.unwrap_or(LogLevel::Info)
});

static mut LOGGER: Option<&'static dyn Log> = None;
static LOG_LEVEL: IoxAtomicU8 = IoxAtomicU8::new(DEFAULT_LOG_LEVEL);
static INIT: Once = Once::new();
static LOG_LEVEL: IoxAtomicU8 = IoxAtomicU8::new(DEFAULT_LOG_LEVEL as u8);
static INIT_LOGGER: Once = Once::new();
static INIT_LOG_LEVEL: Once = Once::new();

pub trait Log: Send + Sync {
/// logs a message
Expand All @@ -182,32 +191,55 @@ pub enum LogLevel {
Fatal = 5,
}

impl LogLevel {
fn from_str_fuzzy(s: &str) -> LogLevel {
match s.to_lowercase().as_str() {
"trace" => LogLevel::Trace,
"debug" => LogLevel::Debug,
"info" => LogLevel::Info,
"warn" => LogLevel::Warn,
"error" => LogLevel::Error,
"fatal" => LogLevel::Fatal,
_ => {
println!("Error: you are using unknown logging level {:?}", s);
println!("Warning: setting log level as : Info");
DEFAULT_LOG_LEVEL
}
}
}
}

/// Sets the current log level. This is ignored for external frameworks like `log` or `tracing`.
/// Here you have to use the log-level settings of that framework.
pub fn set_log_level(v: LogLevel) {
INIT_LOG_LEVEL.call_once(|| {
LOG_LEVEL.store(*ENV_LOG_LEVEL as u8, Ordering::Relaxed);
});
LOG_LEVEL.store(v as u8, Ordering::Relaxed);
}

/// Returns the current log level
pub fn get_log_level() -> u8 {
INIT_LOG_LEVEL.call_once(|| {
LOG_LEVEL.store(*ENV_LOG_LEVEL as u8, Ordering::Relaxed);
});
LOG_LEVEL.load(Ordering::Relaxed)
}

/// Sets the [`Log`]ger. Can be only called once at the beginning of the program. If the
/// [`Log`]ger is already set it returns false and does not update it.
pub fn set_logger<T: Log + 'static>(value: &'static T) -> bool {
let mut set_logger_success = false;
INIT.call_once(|| {
INIT_LOGGER.call_once(|| {
unsafe { LOGGER = Some(value) };
set_logger_success = true;
});

set_logger_success
}

/// Returns a reference to the [`Log`]ger.
pub fn get_logger() -> &'static dyn Log {
INIT.call_once(|| {
INIT_LOGGER.call_once(|| {
unsafe { LOGGER = Some(&DEFAULT_LOGGER) };
});

Expand Down
Loading