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

JDK21: Further cleanups and improvements #94

Merged
merged 13 commits into from
Mar 20, 2024
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,26 @@ the PATCH component is omitted when its value is `0`.

## Unreleased

### `penna-api`

#### Changed

- Replace configuration interfaces([#89](https://github.com/hkupty/penna/pull/89))

### `penna-core`

#### Changed

- Replace internal MDC storage implementation([#83](https://github.com/hkupty/penna/pull/83))
- Remove ad-hoc configuration mechanism([#89](https://github.com/hkupty/penna/pull/89))
- Remove sink proxy([#94](https://github.com/hkupty/penna/pull/94))
- Minor cleanups and adjusts for JDK21 ([#94](https://github.com/hkupty/penna/pull/94))

### `penna-yaml-config`

#### Changed

- Restructure configuration; implement new interface([#89](https://github.com/hkupty/penna/pull/89))

## 0.7.2

Expand Down
75 changes: 22 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ In order to use it, add it to the [build manager of your preference](https://mvn

```groovy
// gradle
runtimeOnly 'com.hkupty.penna:penna-core:0.7.0'
runtimeOnly 'com.hkupty.penna:penna-core:0.8.0'

// Penna doesn't have any strict dependencies aside from slf4j.
implementation 'org.slf4j:slf4j-api:2.0.9'
implementation 'org.slf4j:slf4j-api:2.0.12'
```

:warning: Note that Penna is built targeting JVM 17+.
:warning: Note that Penna is built targeting JVM 21+.

By default, you will get log level `INFO` enabled as well as the following fields:
- `timestamp`
Expand All @@ -58,68 +58,37 @@ By default, you will get log level `INFO` enabled as well as the following field
- `data` (slf4j's 2.0 `.addKeyValue()`)
- `throwable`

Penna has support for logging also a `Counter` to each message, individually marking each message with a monotonically increasing
`long` counting from process startup, but that is disabled by default.

If you want to configure it, Penna provides a separate convenience library for configuring your log levels in yaml files:
```yaml
# resources/penna.yaml

# This is for configuring the root level
penna:
# We don't need to set level because by default it is set to INFO
fields:
# Not that it matter for json, but the key-value pairs below will be rendered in this order.
# So, for human readability in the console, one can tweak the position of the fields:
- level
- logger
- thread
- data
- mdc
- markers
- message
- throwable
loggers:
# This map will match the logger with the same literal name and all its children loggers, so
# com.mycompany.myapp as well as com.mycompany.myapp.controllers.MyGreatController and so on..
com.mycompany.myapp:
level: debug
# There's no need to set fields here since it will inherit from the root logger
com.vendor.noisylib:
level: warn
fields:
# USE WITH CAUTION! You can opt to remove/add fields to the message in different loggers
# In this example, we're removing `thread`, `data`, `mdc` and `markers` and adding the `counter` field.
# This means the log messages from `com.vendor.noisylib` will be rendered differently.
- level
- logger
- message
- throwable
---
# Since version 0.8, penna-yaml-config supports setting up a file watcher
# so any updates to this file will be reflected immediately
watch: true
loggers:
# All the loggers under `com.yourapp` will be configured to debug level.
com.yourapp: { level: debug }
org.noisylibrary: { level: warn }
```

If you want to use [penna-yaml-config](penna-yaml-config/README.md), you have to add it as a dependency:

```groovy
runtimeOnly 'com.hkupty.penna:penna-yaml-config:0.7.0'
runtimeOnly 'com.hkupty.penna:penna-yaml-config:0.8.0'

// We have to add a yaml parser to the classpath for `penna-yaml-config` to work properly.
// Currently we only support `jackson-dataformat-yaml`, but we plan on adding support for other libraries.
runtimeOnly 'com.fasterxml.jackson.core:jackson-core:2.14.2'
runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:2.14.2'
runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2'
```
// penna-yaml-config is a thin layer and uses a yaml parsing libray under the hood.
// You can chose among jackson, snakeyaml (yaml 1.1) or snakeyaml engine (yaml 1.2)

## Test logs
// Jackson
runtimeOnly 'com.fasterxml.jackson.core:jackson-core:2.17.0'
runtimeOnly 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
runtimeOnly 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.0'

Starting from Penna 0.7, Penna offers a `penna-dev` module which reformats the log to standard formatted strings instead of json.
By adding `penna-dev` to your test runtime it will automatically bind the new logger on top of `penna-core`

```groovy
// penna-core is still needed as a runtime dependency
runtimeOnly 'com.hkupty.penna:penna-core:0.7.0'
// Snakeyaml
runtimeOnly 'org.yaml:snakeyaml:2.2'

// penna-dev should only be added to the test runtime
testRuntimeOnly 'com.hkupty.penna:penna-dev:0.7.0'
// Snakeyaml engine
runtimeOnly 'org.snakeyaml:snakeyaml-engine:2.7'
```

## Principles
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.7.2
version=0.8.0-rc1
3 changes: 1 addition & 2 deletions penna-api/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import penna.api.configv2.Provider;
import penna.api.config.Provider;

/**
* Penna API is a thin set of classes and records that are common-ground between the {@code penna.core} project
Expand All @@ -13,5 +13,4 @@

exports penna.api.models;
exports penna.api.config;
exports penna.api.configv2;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package penna.api.configv2;

import penna.api.config.Config;
package penna.api.config;

/**
* Class exists to contextually bind a configuration object to a logger by its name.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package penna.api.configv2;

import penna.api.config.Config;
package penna.api.config;

import java.util.Objects;
import java.util.ServiceLoader;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package penna.api.configv2;
package penna.api.config;

/**
* This interface allows for additional configuration providers to interact with the logger setup.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package penna.api.configv2;

import penna.api.config.Config;
package penna.api.config;

/**
* A Config Storage defines a class that stores the configurations for all logs in the hierarchy.
Expand Down
9 changes: 0 additions & 9 deletions penna-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
import penna.core.slf4j.PennaServiceProvider;

module penna.core {
uses penna.core.sink.NonStandardSink;
// Depends explicitly on the SLF4J api
requires org.slf4j;
requires transitive penna.api;

// Exposes a service provider for SLF4j
provides org.slf4j.spi.SLF4JServiceProvider with PennaServiceProvider;


// Penna subprojects also have access to minilog
// when/if breaking apart from slf4j, expose the full logger
exports penna.core.minilog to penna.config.yaml;
exports penna.core.sink to penna.dev;
exports penna.core.slf4j to penna.dev;
exports penna.core.models to penna.dev;
}
2 changes: 1 addition & 1 deletion penna-core/src/main/java/penna/core/internals/Clock.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private Clock() {}
* increment the clock every X nanoseconds as defined by {@link Clock#REFRESH_RATE} and after every
* Y iterations, as defined by {@link Clock#PRECISION}, we sync back with the system clock.
*/
private static final Thread clockThread = ThreadCreator.newThread("penna-clock-ticker", () -> {
private static final Thread clockThread = Thread.ofVirtual().name("penna-clock-ticker").start(() -> {
while(!Thread.currentThread().isInterrupted()) {
var ts = timestamp.incrementAndGet();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
public final class DirectJson implements Closeable {
private static final int INITIAL_BUFFER_SIZE = 2 * 1024;
private int highWatermark = (int) Math.ceil(INITIAL_BUFFER_SIZE * 0.8);
private static final byte[] LINE_BREAK = System.getProperty("line.separator").getBytes(StandardCharsets.UTF_8);
private static final byte[] LINE_BREAK = System.lineSeparator().getBytes(StandardCharsets.UTF_8);
private static final byte QUOTE = '"';
private static final byte ENTRY_SEP = ':';
private static final byte KV_SEP = ',';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package penna.core.internals;

import org.jetbrains.annotations.VisibleForTesting;
import penna.core.logger.LogUnitContext;
import penna.core.models.PennaLogEvent;
import penna.core.sink.SinkManager;
import penna.core.sink.CoreSink;
import penna.core.sink.Sink;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

/**
* This Object Pool ensures each thread will have access to a single pooled {@link LogUnitContext},
Expand All @@ -26,7 +29,7 @@ public final class LogUnitContextPool {
private final LogUnitContext[] objectGroup;

private LogUnitContext leafObject(int index) {
return new LogUnitContext(this, index, SinkManager.Instance.get(), new PennaLogEvent());
return new LogUnitContext(this, index, CoreSink.getSink(), new PennaLogEvent());
}

public LogUnitContextPool() {
Expand All @@ -39,6 +42,13 @@ public LogUnitContextPool() {
}
}

@VisibleForTesting
void refillThePool(Supplier<Sink> sinkSupplier) {
for (int i = 0; i < objectGroup.length; i++) {
objectGroup[i] = new LogUnitContext(this, i, sinkSupplier.get(), objectGroup[i].logEvent());
}
}

public void release(int index) {
locks[index].unlock();
}
Expand Down
24 changes: 0 additions & 24 deletions penna-core/src/main/java/penna/core/internals/ThreadCreator.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package penna.core.internals.store;

import java.io.Serial;
import java.util.Map;
import java.util.TreeMap;

Expand All @@ -13,6 +14,9 @@
*/
public final class StringTreeMap extends TreeMap<String, String> implements StringMap {

@Serial
private static final long serialVersionUID = 23827L;

public StringTreeMap() {
super();
}
Expand Down
18 changes: 8 additions & 10 deletions penna-core/src/main/java/penna/core/logger/LoggerStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ public static Node create(String component, Config config) {
public final Lock lock = new ReentrantLock();


void setConfigAndUpdateRecursively(Config baseConfig) {
void setConfigAndUpdateRecursively(@NotNull Config baseConfig) {
lock.lock();
try {
configRef = baseConfig;

if (loggerRef != null && baseConfig != null) {
if (loggerRef != null) {
loggerRef.updateConfig(baseConfig);
}
} finally {
Expand All @@ -74,14 +74,18 @@ void setConfigAndUpdateRecursively(Config baseConfig) {
if (children[1] != null) {children[1].updateRecursively(baseConfig);}
}

void updateRecursively(Config baseConfig) {
void updateRecursively(@NotNull Config baseConfig) {
lock.lock();
try {
if (configRef != null) {
// TODO: This might be a little problematic. Needs to be investigated further
// A Configuration object might need a stamp so we can differentiate
// a valid replacement (i.e. a newer version is updating old data) from an
// invalid one.
configRef = baseConfig;
}

if (loggerRef != null && baseConfig != null) {
if (loggerRef != null) {
loggerRef.updateConfig(baseConfig);
}
} finally {
Expand Down Expand Up @@ -184,12 +188,6 @@ public Config getConfig(@NotNull String prefix) {
}

public void replaceConfig(@NotNull Config newConfig) {
root.lock.lock();
try {
root.configRef = newConfig;
} finally {
root.lock.unlock();
}
root.updateRecursively(newConfig);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package penna.core.logger.guard;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.event.Level;
import org.slf4j.spi.LoggingEventBuilder;
import org.slf4j.spi.NOPLoggingEventBuilder;
Expand All @@ -20,7 +21,8 @@
public sealed interface LevelGuard permits DebugLevelGuard, ErrorLevelGuard, InfoLevelGuard, NOPGuard, TraceLevelGuard, WarnLevelGuard {

final class Shared {
private static final LogUnitContextPool logUnits = new LogUnitContextPool();
@VisibleForTesting
public static final LogUnitContextPool logUnits = new LogUnitContextPool();
}

final class FromConfig {
Expand Down
Loading