diff --git a/README.md b/README.md index af05125..35e3628 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,22 @@ metrics.set("memory_usage_mb", 512.5); metrics.set("queue_size", 0); ``` +## Apps + +A domain has one ingest key, the domain key, shared by all apps of that domain, such as a desktop app, an API and a landing page. +Set `app` to name this app, so the dashboard can filter logs and metrics by app: + +```java +var logdash = Logdash.builder() + .apiKey(System.getenv("LOGDASH_API_KEY")) // the domain key + .app("api") + .build(); +``` + +Every log and metric this instance sends carries `"app": "api"`. +With a domain key, the app is created on first use. +Older service keys ignore `app`. + ## View Your Data To see the logs or metrics, go to your project dashboard at [logdash.io](https://logdash.io/app/clusters) @@ -315,20 +331,19 @@ Logdash logdash = Logdash.builder() .build(); ``` -## Configuration - ### All Configuration Options | Parameter | Default | Description | |-------------------------|--------------------------|----------------------------------| | `apiKey` | `null` | Your Logdash API key (required) | +| `app` | `null` | App name sent with logs/metrics | | `baseUrl` | `https://api.logdash.io` | Logdash API endpoint | | `enableConsoleOutput` | `true` | Show logs/metrics in console | | `enableVerboseLogging` | `false` | Enable SDK debug output | | `requestTimeoutMs` | `15000` | HTTP request timeout | | `maxRetries` | `3` | Failed request retry attempts | | `retryDelayMs` | `500` | Base delay between retries | -| `maxConcurrentRequests` | `10` | Maximum concurrent HTTP requests | +| `maxConcurrentRequests` | `20` | Maximum concurrent HTTP requests | | `shutdownTimeoutMs` | `10000` | Graceful shutdown timeout | ## Performance Considerations @@ -337,7 +352,6 @@ Logdash logdash = Logdash.builder() - **Use structured logging** for better searchability and performance - **Avoid logging sensitive data** (passwords, tokens, PII) - **The SDK is async** - logging calls return immediately without blocking -- **Memory efficient** - automatic batching and resource management ## Getting Your API Key diff --git a/src/main/java/io/logdash/sdk/Logdash.java b/src/main/java/io/logdash/sdk/Logdash.java index ca0b6fa..4a1d7ea 100644 --- a/src/main/java/io/logdash/sdk/Logdash.java +++ b/src/main/java/io/logdash/sdk/Logdash.java @@ -245,6 +245,7 @@ public static final class Builder { private long requestTimeoutMs = 15000L; private long shutdownTimeoutMs = 10000L; private int maxConcurrentRequests = 20; + private String app; private Builder() { } @@ -342,6 +343,31 @@ public Builder maxConcurrentRequests(int maxConcurrentRequests) { return this; } + /** + * Sets the name of the app that sends the data, such as {@code desktop}, {@code api} or + * {@code landing}. + * + *
The domain key is shared by all apps of a domain, and {@code app} names this one, so the + * dashboard can filter logs and metrics by app. Every log and metric this instance sends + * carries it. With a domain key, the app is created on first use. Older service keys ignore + * {@code app}. + * + *
{@code
+ * var logdash = Logdash.builder()
+ * .apiKey(System.getenv("LOGDASH_API_KEY"))
+ * .app("api")
+ * .build();
+ * }
+ *
+ * @param app the app name (defaults to none; blank means none; the server cuts it to 64
+ * characters)
+ * @return this builder
+ */
+ public Builder app(String app) {
+ this.app = app;
+ return this;
+ }
+
/**
* Builds the Logdash instance with the configured settings.
*
@@ -366,7 +392,8 @@ public Logdash build() {
retryDelayMs,
requestTimeoutMs,
shutdownTimeoutMs,
- maxConcurrentRequests);
+ maxConcurrentRequests,
+ app);
return new Logdash(config);
}
}
diff --git a/src/main/java/io/logdash/sdk/config/LogdashConfig.java b/src/main/java/io/logdash/sdk/config/LogdashConfig.java
index 2988d37..66566d1 100644
--- a/src/main/java/io/logdash/sdk/config/LogdashConfig.java
+++ b/src/main/java/io/logdash/sdk/config/LogdashConfig.java
@@ -20,6 +20,8 @@
* @param requestTimeoutMs HTTP request timeout in milliseconds
* @param shutdownTimeoutMs Maximum time to wait for graceful shutdown
* @param maxConcurrentRequests Maximum number of concurrent HTTP requests
+ * @param app Name of the app sent with every log and metric, trimmed; null when
+ * not set or blank
*/
public record LogdashConfig(
String apiKey,
@@ -30,7 +32,8 @@ public record LogdashConfig(
long retryDelayMs,
long requestTimeoutMs,
long shutdownTimeoutMs,
- int maxConcurrentRequests) {
+ int maxConcurrentRequests,
+ String app) {
private static final int MIN_API_KEY_LENGTH = 3;
private static final int MAX_API_KEY_LENGTH = 256;
@@ -43,6 +46,7 @@ public record LogdashConfig(
requestTimeoutMs,
shutdownTimeoutMs,
maxConcurrentRequests);
+ app = app == null || app.isBlank() ? null : app.strip();
}
/**
@@ -120,6 +124,7 @@ public static final class Builder {
private long requestTimeoutMs = 15000L;
private long shutdownTimeoutMs = 10000L;
private int maxConcurrentRequests = 10;
+ private String app = null;
private Builder() {
}
@@ -169,6 +174,11 @@ public Builder maxConcurrentRequests(int maxConcurrentRequests) {
return this;
}
+ public Builder app(String app) {
+ this.app = app;
+ return this;
+ }
+
/**
* Builds the LogdashConfig instance with the configured settings.
*
@@ -185,7 +195,8 @@ public LogdashConfig build() {
retryDelayMs,
requestTimeoutMs,
shutdownTimeoutMs,
- maxConcurrentRequests);
+ maxConcurrentRequests,
+ app);
}
}
}
diff --git a/src/main/java/io/logdash/sdk/transport/HttpTransport.java b/src/main/java/io/logdash/sdk/transport/HttpTransport.java
index 217537b..edecad6 100644
--- a/src/main/java/io/logdash/sdk/transport/HttpTransport.java
+++ b/src/main/java/io/logdash/sdk/transport/HttpTransport.java
@@ -61,7 +61,7 @@ public final class HttpTransport implements LogdashTransport, AutoCloseable {
*/
public HttpTransport(LogdashConfig config) {
this.config = config;
- this.serializer = new JsonSerializer();
+ this.serializer = new JsonSerializer(config.app());
this.rateLimiter = new Semaphore(config.maxConcurrentRequests(), true);
this.executorService = createOptimizedExecutorService();
this.httpClient = createOptimizedHttpClient();
diff --git a/src/main/java/io/logdash/sdk/util/JsonSerializer.java b/src/main/java/io/logdash/sdk/util/JsonSerializer.java
index a65535d..974a78e 100644
--- a/src/main/java/io/logdash/sdk/util/JsonSerializer.java
+++ b/src/main/java/io/logdash/sdk/util/JsonSerializer.java
@@ -11,6 +11,7 @@
import io.logdash.sdk.log.LogEntry;
import io.logdash.sdk.metrics.MetricEntry;
+import java.util.HashMap;
import java.util.Map;
/**
@@ -25,6 +26,24 @@
public final class JsonSerializer {
private static final ObjectMapper OBJECT_MAPPER = createObjectMapper();
+ private final String app;
+
+ /**
+ * Creates a serializer that sends no app.
+ */
+ public JsonSerializer() {
+ this(null);
+ }
+
+ /**
+ * Creates a serializer that adds the app to every log and metric.
+ *
+ * @param app the app name, or null to leave the {@code app} key out
+ */
+ public JsonSerializer(String app) {
+ this.app = app;
+ }
+
private static ObjectMapper createObjectMapper() {
return JsonMapper.builder()
.addModule(new JavaTimeModule())
@@ -44,11 +63,15 @@ private static ObjectMapper createObjectMapper() {
public String serialize(LogEntry logEntry) {
try {
var logData =
- Map.of(
- "message", sanitizeString(logEntry.message()),
- "level", logEntry.level().getValue(),
- "createdAt", logEntry.createdAt(),
- "sequenceNumber", logEntry.sequenceNumber());
+ new HashMap