diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 33b8b9f..79f6326 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,10 +2,10 @@ name: CI/CD Pipeline
on:
push:
- branches: [ main ]
+ branches: [ main, dev ]
tags: [ 'v*' ]
pull_request:
- branches: [ main ]
+ branches: [ main, dev ]
permissions:
contents: write
@@ -263,12 +263,9 @@ jobs:
exit 1
fi
- - name: Check if version exists
+ - name: Check if version exists in GitHub Packages
run: |
- VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
- echo "version=$VERSION" >> $GITHUB_OUTPUT
- echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
-
+ VERSION=${{ steps.version.outputs.version }}
# Check if version already exists in GitHub Packages
if gh api /orgs/logdash-io/packages/maven/io.logdash.logdash/versions --jq '.[] | .name' | grep -q "^$VERSION$"; then
echo "::warning::Version $VERSION already exists in GitHub Packages"
@@ -346,6 +343,21 @@ jobs:
exit 1
fi
+ - name: Check if version exists on Maven Central
+ run: |
+ VERSION=${{ steps.version.outputs.version }}
+ # Check if version exists on Maven Central using search API
+ SEARCH_RESULT=$(curl -s "https://search.maven.org/solrsearch/select?q=g:io.logdash+AND+a:logdash+AND+v:$VERSION&rows=1&wt=json")
+ COUNT=$(echo "$SEARCH_RESULT" | jq -r '.response.numFound')
+
+ if [[ "$COUNT" != "0" ]]; then
+ echo "::error::Version $VERSION already exists on Maven Central"
+ echo "Cannot redeploy existing version to Maven Central"
+ exit 1
+ else
+ echo "✅ Version $VERSION is available for Maven Central release"
+ fi
+
- name: Build and Deploy to Maven Central
run: |
mvn clean deploy -B --no-transfer-progress \
@@ -356,12 +368,24 @@ jobs:
CENTRAL_TOKEN_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
CENTRAL_TOKEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
+
+ - name: Verify Maven Central Deployment
+ run: |
+ VERSION=${{ steps.version.outputs.version }}
+ # Check if artifact appears in Central Portal (deployment confirmation)
+ if curl -s -f "https://central.sonatype.com/api/internal/browse/component?namespace=io.logdash&name=logdash&version=$VERSION" > /dev/null; then
+ echo "✅ Deployment confirmed via Central Portal API"
+ else
+ echo "⚠️ Not yet visible via Central Portal API (may take time to propagate)"
+ fi
- name: Notify Maven Central Success
run: |
echo "✅ Successfully deployed logdash:${{ steps.version.outputs.version }} to Maven Central"
echo "📦 Will be available at: https://central.sonatype.com/artifact/io.logdash/logdash/${{ steps.version.outputs.version }}"
echo "🌐 Maven Central: https://search.maven.org/artifact/io.logdash/logdash/${{ steps.version.outputs.version }}"
+ echo "⏱️ Note: It may take up to 2 hours for the artifact to be searchable via Maven Central"
create-github-release:
name: Create GitHub Release
@@ -469,6 +493,14 @@ jobs:
_See below for full release notes and change log._
+ - name: Deployment Summary
+ run: |
+ echo "📋 Deployment Status Summary:"
+ echo "✅ GitHub Packages: ${{ needs.github-release.result }}"
+ echo "✅ Maven Central: ${{ needs.maven-central-release.result }}"
+ echo "📦 Version: ${{ steps.version.outputs.version }}"
+ echo "🔗 Maven Central: https://central.sonatype.com/artifact/io.logdash/logdash/${{ steps.version.outputs.version }}"
+
notification:
name: Notification
runs-on: ubuntu-latest
diff --git a/.github/workflows/develop-snapshots.yml b/.github/workflows/develop-snapshots.yml
new file mode 100644
index 0000000..d80fa41
--- /dev/null
+++ b/.github/workflows/develop-snapshots.yml
@@ -0,0 +1,283 @@
+name: Develop Branch Snapshots
+
+on:
+ push:
+ branches: [ develop ]
+ schedule:
+ - cron: '0 2 * * *'
+ workflow_dispatch:
+
+env:
+ JAVA_VERSION: '17'
+ JAVA_DISTRIBUTION: 'temurin'
+
+jobs:
+ validate-develop:
+ runs-on: ubuntu-latest
+ if: github.ref == 'refs/heads/develop'
+ timeout-minutes: 15
+ outputs:
+ snapshot-version: ${{ steps.version.outputs.snapshot-version }}
+
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v4
+ with:
+ ref: develop
+ fetch-depth: 0
+
+ - name: Setup JDK ${{ env.JAVA_VERSION }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.JAVA_VERSION }}
+ distribution: ${{ env.JAVA_DISTRIBUTION }}
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-develop-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-develop-
+ ${{ runner.os }}-maven-
+
+ - name: Verify snapshot version
+ id: version
+ run: |
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ if [[ ! $VERSION =~ .*-SNAPSHOT$ ]]; then
+ echo "❌ Error: Develop branch must have SNAPSHOT version, found: $VERSION"
+ exit 1
+ fi
+ echo "✅ Snapshot version verified: $VERSION"
+ echo "snapshot-version=$VERSION" >> $GITHUB_OUTPUT
+
+ - name: Run quick validation
+ run: |
+ mvn clean validate compile -B --no-transfer-progress \
+ -DskipTests=true \
+ -Dspotless.check.skip=true
+
+ maven-central-snapshot:
+ runs-on: ubuntu-latest
+ needs: validate-develop
+ timeout-minutes: 25
+ if: github.ref == 'refs/heads/develop'
+
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v4
+ with:
+ ref: develop
+ fetch-depth: 0
+
+ - name: Setup JDK ${{ env.JAVA_VERSION }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.JAVA_VERSION }}
+ distribution: ${{ env.JAVA_DISTRIBUTION }}
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-develop-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-develop-
+ ${{ runner.os }}-maven-
+
+ - name: Configure Maven Central Snapshots
+ uses: whelk-io/maven-settings-xml-action@v22
+ with:
+ servers: |
+ [
+ {
+ "id": "central",
+ "username": "${{ secrets.CENTRAL_TOKEN_USERNAME }}",
+ "password": "${{ secrets.CENTRAL_TOKEN_PASSWORD }}"
+ }
+ ]
+
+ - name: Display snapshot version
+ run: |
+ echo "📦 Deploying snapshot version: ${{ needs.validate-develop.outputs.snapshot-version }}"
+
+ - name: Run comprehensive tests
+ run: |
+ mvn clean verify -B --no-transfer-progress \
+ -Dmaven.test.failure.ignore=false \
+ -Dfailsafe.rerunFailingTestsCount=2
+
+ - name: Deploy to Maven Central Snapshots
+ run: |
+ mvn deploy -B --no-transfer-progress \
+ -Pmaven-central-release \
+ -DskipTests=true \
+ -Dcentral.autoPublish=true
+ env:
+ SONATYPE_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
+
+ - name: Verify Maven Central snapshot deployment
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+ echo "⏳ Waiting for snapshot propagation..."
+ sleep 10
+
+ # Check if snapshot metadata is available
+ METADATA_URL="https://central.sonatype.com/repository/maven-snapshots/io/logdash/logdash/$VERSION/maven-metadata.xml"
+ if curl -s -f "$METADATA_URL" > /dev/null; then
+ echo "✅ Snapshot metadata confirmed at Maven Central"
+ else
+ echo "⚠️ Snapshot metadata not yet available (may take time to propagate)"
+ fi
+
+ - name: Display deployment info
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+ echo "✅ Successfully deployed $VERSION to Maven Central Snapshots"
+ echo "📦 Available at: https://central.sonatype.com/repository/maven-snapshots/io/logdash/logdash/$VERSION/"
+ echo ""
+ echo "🔍 Maven dependency:"
+ echo ""
+ echo " io.logdash"
+ echo " logdash"
+ echo " $VERSION"
+ echo ""
+ echo ""
+ echo "🔍 Gradle dependency:"
+ echo "implementation 'io.logdash:logdash:$VERSION'"
+
+ - name: Notify deployment status
+ if: failure()
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+ echo "❌ Snapshot deployment failed for version $VERSION"
+ echo "Check logs above for deployment errors"
+
+ github-packages-snapshot:
+ runs-on: ubuntu-latest
+ needs: validate-develop
+ timeout-minutes: 15
+ if: github.ref == 'refs/heads/develop'
+
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v4
+ with:
+ ref: develop
+
+ - name: Setup JDK ${{ env.JAVA_VERSION }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.JAVA_VERSION }}
+ distribution: ${{ env.JAVA_DISTRIBUTION }}
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-develop-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-develop-
+ ${{ runner.os }}-maven-
+
+ - name: Configure Maven Settings for GitHub Packages
+ uses: whelk-io/maven-settings-xml-action@v22
+ with:
+ servers: |
+ [
+ {
+ "id": "github",
+ "username": "${env.GITHUB_ACTOR}",
+ "password": "${env.GITHUB_TOKEN}"
+ }
+ ]
+
+ - name: Deploy to GitHub Packages (Backup)
+ run: |
+ mvn deploy -B --no-transfer-progress \
+ -Pgithub-release \
+ -DperformRelease=true \
+ -DskipTests=true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Verify GitHub Packages deployment
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+ echo "✅ Backup snapshot deployed to GitHub Packages: $VERSION"
+ echo "📦 Available at: https://github.com/logdash-io/java-sdk/packages"
+
+ verify-deployments:
+ runs-on: ubuntu-latest
+ needs: [ maven-central-snapshot, github-packages-snapshot ]
+ timeout-minutes: 10
+ if: github.ref == 'refs/heads/develop'
+
+ steps:
+ - name: Final verification
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+ echo "🔍 Final verification for snapshot: $VERSION"
+
+ # Wait a bit more for propagation
+ echo "⏳ Waiting for complete propagation..."
+ sleep 10
+
+ # Check Maven Central snapshots
+ METADATA_URL="https://central.sonatype.com/repository/maven-snapshots/io/logdash/logdash/$VERSION/maven-metadata.xml"
+ if curl -s -f "$METADATA_URL" > /dev/null; then
+ echo "✅ Maven Central snapshot confirmed and accessible"
+ else
+ echo "⚠️ Maven Central snapshot metadata not accessible yet"
+ fi
+
+ echo ""
+ echo "📋 Deployment Summary:"
+ echo "🏗️ Maven Central Snapshots: ${{ needs.maven-central-snapshot.result }}"
+ echo "📦 GitHub Packages: ${{ needs.github-packages-snapshot.result }}"
+ echo "🔢 Version: $VERSION"
+ echo "🌐 Repository: https://central.sonatype.com/repository/maven-snapshots/"
+
+ notification:
+ runs-on: ubuntu-latest
+ needs: [ validate-develop, maven-central-snapshot, github-packages-snapshot, verify-deployments ]
+ if: always() && github.ref == 'refs/heads/develop'
+
+ steps:
+ - name: Workflow status summary
+ run: |
+ VERSION="${{ needs.validate-develop.outputs.snapshot-version }}"
+
+ echo "📊 Develop Branch Snapshot Workflow Summary"
+ echo "============================================="
+ echo "🔢 Version: $VERSION"
+ echo "📅 Triggered: ${{ github.event_name }}"
+ echo ""
+ echo "Job Results:"
+ echo "✓ Validation: ${{ needs.validate-develop.result }}"
+ echo "✓ Maven Central: ${{ needs.maven-central-snapshot.result }}"
+ echo "✓ GitHub Packages: ${{ needs.github-packages-snapshot.result }}"
+ echo "✓ Verification: ${{ needs.verify-deployments.result }}"
+ echo ""
+
+ # Check overall success
+ if [[ "${{ needs.validate-develop.result }}" == "success" &&
+ "${{ needs.maven-central-snapshot.result }}" == "success" &&
+ "${{ needs.github-packages-snapshot.result }}" == "success" ]]; then
+ echo "🎉 All snapshot deployments completed successfully!"
+ echo "📦 $VERSION is now available for testing"
+ else
+ echo "⚠️ Some deployments may have failed. Check individual job results above."
+ fi
+
+ echo ""
+ echo "🔗 Quick Access:"
+ echo "- Maven Central: https://central.sonatype.com/repository/maven-snapshots/io/logdash/logdash/$VERSION/"
+ echo "- GitHub Packages: https://github.com/logdash-io/java-sdk/packages"
+ echo "- Workflow: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
\ No newline at end of file
diff --git a/README.md b/README.md
index 65372e7..af05125 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,19 @@
# Logdash Java SDK
[](https://github.com/logdash-io/java-sdk/releases)
+[](https://central.sonatype.com/artifact/io.logdash/logdash)
[](https://opensource.org/licenses/MIT)
[](https://openjdk.java.net/projects/jdk/17/)
[](https://github.com/logdash-io/java-sdk/actions)
+[](https://codecov.io/gh/logdash-io/java-sdk)
-Official Java SDK for [Logdash.io](https://logdash.io/) - Zero-configuration observability platform designed for developers working on side projects and prototypes.
+> **Official Java SDK for [Logdash.io](https://logdash.io/) - Zero-configuration observability platform designed for developers working on side projects and prototypes.**
## Why Logdash?
-Most observability solutions feel overwhelming for hobby projects. Logdash provides instant logging and real-time
-metrics without complex configurations. Just add the SDK and start monitoring your application immediately.
+Most observability solutions feel overwhelming for small projects and prototypes. Logdash provides **instant logging and real-time metrics** without complex configurations. Just add the SDK and start monitoring your application immediately.
-## Features
+## Key Features
- **🚀 Zero Configuration**: Start logging and tracking metrics in seconds
- **📊 Real-time Dashboard**: Cloud-hosted interface with live data updates
@@ -23,41 +24,39 @@ metrics without complex configurations. Just add the SDK and start monitoring yo
- **🔧 Framework Agnostic**: Works with Spring Boot, Quarkus, Micronaut, or standalone apps
- **☕ Java 17+ Compatible**: Supports Java 17, 21, and all newer versions
-## Quick Start
-
-### Installation
+## Pre-requisites
-**Maven Central**
+Setup your free project in less than 2 minutes at [logdash.io](https://logdash.io/)
-Add the dependency to your project:
+## Installation
-**Maven:**
+### Maven Central
```xml
io.logdash
logdash
- 0.1.3
+ 0.2.0
```
-**Gradle:**
+### Gradle
-```gradle
+```groovy
dependencies {
- implementation 'io.logdash:logdash:0.1.3'
+ implementation 'io.logdash:logdash:0.2.0'
}
```
-**Gradle (Kotlin DSL):**
+### Gradle (Kotlin DSL)
```kotlin
dependencies {
- implementation("io.logdash:logdash:0.1.3")
+ implementation("io.logdash:logdash:0.2.0")
}
```
-**Local Installation**
+### Local Installation
```bash
# Clone and install locally
@@ -72,10 +71,12 @@ Then use in your project:
io.logdash
logdash
- 0.1.3
+ 0.2.0
```
+## Quick Start
+
### Basic Usage
```java
@@ -270,10 +271,10 @@ Track business and technical metrics to monitor application performance:
var metrics = logdash.metrics();
// Counters - track events and occurrences
-metrics.mutate("page_views",1);
-metrics.mutate("api_requests",5);
-metrics.mutate("response_time_ms",-100);
-metrics.mutate("available_licenses",-1);
+metrics.mutate("page_views", 1);
+metrics.mutate("api_requests", 5);
+metrics.mutate("response_time_ms", -100);
+metrics.mutate("available_licenses", -1);
// Gauges - track current values
metrics.set("active_users", 1_250);
@@ -281,6 +282,14 @@ metrics.set("memory_usage_mb", 512.5);
metrics.set("queue_size", 0);
```
+## View Your Data
+
+To see the logs or metrics, go to your project dashboard at [logdash.io](https://logdash.io/app/clusters)
+
+
+
+
+
## Configuration
### Development Configuration
@@ -306,9 +315,11 @@ Logdash logdash = Logdash.builder()
.build();
```
+## Configuration
+
### All Configuration Options
-| Option | Default | Description |
+| Parameter | Default | Description |
|-------------------------|--------------------------|----------------------------------|
| `apiKey` | `null` | Your Logdash API key (required) |
| `baseUrl` | `https://api.logdash.io` | Logdash API endpoint |
@@ -371,7 +382,7 @@ Check out the [`examples/`](./examples) directory for a complete sample applicat
- **Java 17 or higher** (tested on Java 17, 21, 22)
- **Internet connection** for sending data to Logdash
-- **Valid API key** from [Logdash.io](https://logdash.io)
+- **Valid API key** from [logdash.io](https://logdash.io)
## Contributing
@@ -383,7 +394,6 @@ We welcome contributions! Here's how to get started:
git clone https://github.com/logdash-io/java-sdk.git
cd java-sdk
mvn clean compile
-mvn test
```
### Running Tests
@@ -399,9 +409,24 @@ mvn failsafe:integration-test
mvn clean verify
```
+### GitFlow Workflow
+
+```bash
+# Start new feature
+git checkout dev && git pull origin dev
+git checkout -b feat/awesome-feature
+
+# Development and testing
+mvn clean verify
+git add . && git commit -m "feat: add awesome feature"
+git push origin feat/awesome-feature
+
+# Create pull request to dev branch
+```
+
### Code Quality
-- Follow existing code style (checkstyle configuration included)
+- Follow existing code style
- Ensure all tests pass
- Add tests for new features
- Update documentation as needed
diff --git a/check-deployed-package/Check.java b/check-deployed-package/Check.java
index 4b2229b..48e50c3 100644
--- a/check-deployed-package/Check.java
+++ b/check-deployed-package/Check.java
@@ -2,28 +2,27 @@
import io.logdash.sdk.Logdash;
-import java.util.Map;
public class Check {
public static void main(String[] args) {
System.out.println("=== LogDash Java SDK Demo ===");
-
+
// Get package version (would need to be handled differently in a real scenario)
System.out.println("Using logdash package version: " + getLogdashVersion());
System.out.println();
-
+
String apiKey = System.getenv("LOGDASH_API_KEY");
String logsSeed = System.getenv().getOrDefault("LOGS_SEED", "default");
String metricsSeed = System.getenv().getOrDefault("METRICS_SEED", "1");
-
+
System.out.println("Using API Key: " + apiKey);
System.out.println("Using Logs Seed: " + logsSeed);
System.out.println("Using Metrics Seed: " + metricsSeed);
-
+
try (Logdash logdash = Logdash.create(apiKey)) {
var logger = logdash.logger();
var metrics = logdash.metrics();
-
+
// Log some messages with seed appended
logger.info("This is an info log " + logsSeed);
logger.error("This is an error log " + logsSeed);
@@ -33,7 +32,7 @@ public static void main(String[] args) {
logger.silly("This is a silly log " + logsSeed);
logger.info("This is an info log " + logsSeed);
logger.verbose("This is a verbose log " + logsSeed);
-
+
// Set and mutate metrics with seed
int seedValue = Integer.parseInt(metricsSeed);
metrics.set("users", seedValue);
@@ -47,7 +46,7 @@ public static void main(String[] args) {
}
}
}
-
+
private static String getLogdashVersion() {
try {
return Check.class.getPackage().getImplementationVersion();
@@ -55,4 +54,4 @@ private static String getLogdashVersion() {
return "unknown";
}
}
-}
\ No newline at end of file
+}
diff --git a/check-deployed-package/pom.xml b/check-deployed-package/pom.xml
index 5c00c13..9bed4ed 100644
--- a/check-deployed-package/pom.xml
+++ b/check-deployed-package/pom.xml
@@ -1,13 +1,16 @@
4.0.0
+
io.logdash
check
- 1.0.0
+ 0.2.0
- 17
- 17
+ 17
+ ${java.version}
+ ${java.version}
+ ${java.version}
UTF-8
@@ -15,7 +18,7 @@
io.logdash
logdash
- 0.1.3
+ 0.2.0
@@ -38,16 +41,22 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.11.0
+ 3.12.1
- 17
- 17
+ ${java.version}
+
+ -parameters
+ -Xlint:unchecked
+ -Xlint:deprecation
+ -Werror
+
+
org.codehaus.mojo
exec-maven-plugin
- 3.1.0
+ 3.1.1
io.logdash.check.Check
diff --git a/docs/images/delta.png b/docs/images/delta.png
new file mode 100644
index 0000000..b311d3d
Binary files /dev/null and b/docs/images/delta.png differ
diff --git a/docs/images/logs.png b/docs/images/logs.png
new file mode 100644
index 0000000..62f6872
Binary files /dev/null and b/docs/images/logs.png differ
diff --git a/examples/example-simple-java/pom.xml b/examples/example-simple-java/pom.xml
index ad72762..ff4b4f7 100644
--- a/examples/example-simple-java/pom.xml
+++ b/examples/example-simple-java/pom.xml
@@ -1,10 +1,10 @@
-
+
4.0.0
com.example
logdash-example
- 0.1.3
+ 0.2.0
jar
Logdash Example Application
@@ -16,21 +16,23 @@
UTF-8
21
- 0.1.3
3.12.1
3.1.1
+
+ 0.2.0
io.logdash
logdash
- ${logdash-sdk.version}
+ ${logdash.version}
+
org.apache.maven.plugins
maven-compiler-plugin
@@ -39,6 +41,9 @@
${maven.compiler.release}
-parameters
+ -Xlint:unchecked
+ -Xlint:deprecation
+ -Werror
@@ -53,4 +58,25 @@
+
+
+
+ snapshot-repo
+
+
+ logdash.version
+ *-SNAPSHOT
+
+
+
+
+
+ true
+
+ github-snapshots
+ https://maven.pkg.github.com/logdash-io/java-sdk
+
+
+
+
diff --git a/examples/example-springboot/Dockerfile b/examples/example-springboot/Dockerfile
new file mode 100644
index 0000000..536bdbc
--- /dev/null
+++ b/examples/example-springboot/Dockerfile
@@ -0,0 +1,7 @@
+FROM eclipse-temurin:21-jre-alpine
+
+WORKDIR /app
+COPY target/springboot-example-*.jar app.jar
+
+EXPOSE 8080
+ENTRYPOINT ["java", "-jar", "app.jar"]
diff --git a/examples/example-springboot/docker-compose.yml b/examples/example-springboot/docker-compose.yml
new file mode 100644
index 0000000..a86a90e
--- /dev/null
+++ b/examples/example-springboot/docker-compose.yml
@@ -0,0 +1,14 @@
+version: '3.8'
+services:
+ app:
+ build: .
+ ports:
+ - '8080:8080'
+ environment:
+ - LOGDASH_API_KEY=${LOGDASH_API_KEY:-your-api-key}
+ - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-dev}
+ healthcheck:
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]
+ interval: 30s
+ timeout: 10s
+ retries: 3
\ No newline at end of file
diff --git a/examples/example-springboot/pom.xml b/examples/example-springboot/pom.xml
new file mode 100644
index 0000000..79788d6
--- /dev/null
+++ b/examples/example-springboot/pom.xml
@@ -0,0 +1,155 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.6
+
+
+
+ io.logdash.example
+ springboot-example
+ 0.2.0
+ jar
+
+ Logdash Spring Boot Example
+ Spring Boot application demonstrating Logdash Java SDK usage
+
+
+ 21
+ ${java.version}
+ ${java.version}
+ ${java.version}
+ UTF-8
+
+
+ 0.2.0
+
+
+ 3.5.1
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ io.logdash
+ logdash
+ ${logdash-sdk.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ false
+
+
+ true
+ interval:60
+
+ maven-central-snapshots
+ Maven Central Snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ io.logdash.example.SpringBootExampleApplication
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${java.version}
+
+ -parameters
+ -Xlint:unchecked
+ -Xlint:deprecation
+ -Werror
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-help-plugin
+ ${maven-help-plugin.version}
+
+
+
+
+
+
+ local-dev
+
+ true
+
+
+
+
+ never
+
+ maven-central-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+
+
+
+ force-remote
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ purge-local-logdash
+
+ purge-local-repository
+
+ initialize
+
+
+ io.logdash:logdash
+
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/example-springboot/requests.http b/examples/example-springboot/requests.http
new file mode 100644
index 0000000..69eed80
--- /dev/null
+++ b/examples/example-springboot/requests.http
@@ -0,0 +1,23 @@
+### Variables
+@baseUrl = http://localhost:8080
+
+### Get all pets
+GET {{baseUrl}}/api/pets
+
+### Get pet by ID
+GET {{baseUrl}}/api/pets/1
+
+### Create a new pet
+POST {{baseUrl}}/api/pets
+Content-Type: application/json
+
+{
+ "name": "Fluffy",
+ "type": "DOG"
+}
+
+### Create a random pet
+POST {{baseUrl}}/api/pets/random
+
+### Delete a pet by ID
+DELETE {{baseUrl}}/api/pets/3
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/SpringBootExampleApplication.java b/examples/example-springboot/src/main/java/io/logdash/example/SpringBootExampleApplication.java
new file mode 100644
index 0000000..7b570be
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/SpringBootExampleApplication.java
@@ -0,0 +1,14 @@
+package io.logdash.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@EnableScheduling
+public class SpringBootExampleApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringBootExampleApplication.class, args);
+ }
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/config/LogdashConfiguration.java b/examples/example-springboot/src/main/java/io/logdash/example/config/LogdashConfiguration.java
new file mode 100644
index 0000000..f77b6fd
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/config/LogdashConfiguration.java
@@ -0,0 +1,51 @@
+package io.logdash.example.config;
+
+import io.logdash.sdk.Logdash;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static io.logdash.example.config.LogdashConfiguration.LogdashProperties;
+
+@Configuration
+@EnableConfigurationProperties(LogdashProperties.class)
+public class LogdashConfiguration {
+
+ @Bean
+ public Logdash logdash(LogdashProperties properties) {
+ return Logdash.builder()
+ .apiKey(properties.apiKey())
+ .baseUrl(properties.baseUrl())
+ .enableConsoleOutput(properties.enableConsoleOutput())
+ .enableVerboseLogging(properties.enableVerboseLogging())
+ .requestTimeoutMs(properties.requestTimeoutMs())
+ .maxConcurrentRequests(properties.maxConcurrentRequests())
+ .build();
+ }
+
+ @ConfigurationProperties(prefix = "logdash")
+ public record LogdashProperties(
+ String apiKey,
+ String baseUrl,
+ boolean enableConsoleOutput,
+ boolean enableVerboseLogging,
+ long requestTimeoutMs,
+ int maxConcurrentRequests
+ ) {
+ public LogdashProperties {
+ if (apiKey == null || apiKey.isBlank()) {
+ throw new IllegalArgumentException("Logdash API key is required");
+ }
+ if (baseUrl == null || baseUrl.isBlank()) {
+ baseUrl = "https://api.logdash.io";
+ }
+ if (requestTimeoutMs <= 0) {
+ requestTimeoutMs = 10000L;
+ }
+ if (maxConcurrentRequests <= 0) {
+ maxConcurrentRequests = 20;
+ }
+ }
+ }
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/controller/PetController.java b/examples/example-springboot/src/main/java/io/logdash/example/controller/PetController.java
new file mode 100644
index 0000000..adc240f
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/controller/PetController.java
@@ -0,0 +1,133 @@
+package io.logdash.example.controller;
+
+import io.logdash.example.domain.Pet;
+import io.logdash.example.domain.PetType;
+import io.logdash.example.service.PetService;
+import io.logdash.sdk.Logdash;
+import io.logdash.sdk.log.LogdashLogger;
+import io.logdash.sdk.metrics.LogdashMetrics;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/pets")
+public class PetController {
+
+ private final PetService petService;
+ private final LogdashLogger logger;
+ private final LogdashMetrics metrics;
+
+ public PetController(PetService petService, Logdash logdash) {
+ this.petService = petService;
+ this.logger = logdash.logger();
+ this.metrics = logdash.metrics();
+ }
+
+ @GetMapping
+ public ResponseEntity> getAllPets() {
+ var pets = petService.getAllPets();
+
+ logger.info("Retrieved all pets", Map.of(
+ "count", pets.size(),
+ "endpoint", "/api/pets",
+ "timestamp", LocalDateTime.now()
+ ));
+
+ metrics.mutate("api.requests.total", 1);
+ metrics.mutate("api.pets.list.requests", 1);
+ metrics.set("pets.total.count", pets.size());
+
+ return ResponseEntity.ok(pets);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity getPet(@PathVariable Long id) {
+ var pet = petService.findById(id);
+
+ if (pet.isPresent()) {
+ logger.debug("Pet retrieved successfully", Map.of(
+ "petId", id,
+ "name", pet.get().name(),
+ "type", pet.get().type().name()
+ ));
+ metrics.mutate("api.pets.get.success", 1);
+ return ResponseEntity.ok(pet.get());
+ } else {
+ logger.warn("Pet not found", Map.of(
+ "petId", id
+ ));
+ metrics.mutate("api.pets.get.not_found", 1);
+ return ResponseEntity.notFound().build();
+ }
+ }
+
+ @PostMapping
+ public ResponseEntity createPet(@RequestBody CreatePetRequest request) {
+ try {
+ var pet = petService.addPet(request.name(), request.type());
+
+ logger.info("Pet created successfully", Map.of(
+ "petId", pet.id(),
+ "name", pet.name(),
+ "type", pet.type().name()
+ ));
+
+ metrics.mutate("pets.created.total", 1);
+ metrics.mutate("api.pets.create.success", 1);
+ metrics.set("pets.total.count", petService.getTotalCount());
+
+ return ResponseEntity.ok(pet);
+
+ } catch (IllegalArgumentException e) {
+ logger.error("Pet creation failed", Map.of(
+ "error", e.getMessage(),
+ "requestName", request.name(),
+ "requestType", request.type()
+ ));
+ metrics.mutate("api.pets.create.validation_error", 1);
+ return ResponseEntity.badRequest().build();
+ }
+ }
+
+ @PostMapping("/random")
+ public ResponseEntity createRandomPet() {
+ var pet = petService.createRandomPet();
+
+ logger.info("Random pet created", Map.of(
+ "petId", pet.id(),
+ "name", pet.name(),
+ "type", pet.type().name()
+ ));
+
+ metrics.mutate("pets.created.random", 1);
+ metrics.mutate("api.requests.total", 1);
+ metrics.set("pets.total.count", petService.getTotalCount());
+
+ return ResponseEntity.ok(pet);
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deletePet(@PathVariable Long id) {
+ boolean deleted = petService.deletePet(id);
+
+ if (deleted) {
+ logger.info("Pet deleted successfully", Map.of(
+ "petId", id
+ ));
+ metrics.mutate("pets.deleted.total", 1);
+ metrics.set("pets.total.count", petService.getTotalCount());
+ return ResponseEntity.noContent().build();
+ } else {
+ logger.warn("Delete failed - pet not found", Map.of("petId", id));
+ metrics.mutate("api.pets.delete.not_found", 1);
+ return ResponseEntity.notFound().build();
+ }
+ }
+
+ public record CreatePetRequest(String name, PetType type) {
+ }
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/domain/Pet.java b/examples/example-springboot/src/main/java/io/logdash/example/domain/Pet.java
new file mode 100644
index 0000000..004fbd5
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/domain/Pet.java
@@ -0,0 +1,16 @@
+package io.logdash.example.domain;
+
+public record Pet(
+ Long id,
+ String name,
+ PetType type
+) {
+ public Pet {
+ if (name == null || name.isBlank()) {
+ throw new IllegalArgumentException("Pet name is required");
+ }
+ if (type == null) {
+ throw new IllegalArgumentException("Pet type is required");
+ }
+ }
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/domain/PetType.java b/examples/example-springboot/src/main/java/io/logdash/example/domain/PetType.java
new file mode 100644
index 0000000..20cda4b
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/domain/PetType.java
@@ -0,0 +1,5 @@
+package io.logdash.example.domain;
+
+public enum PetType {
+ DOG, CAT, BIRD, RABBIT, FISH
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/service/MonitoringService.java b/examples/example-springboot/src/main/java/io/logdash/example/service/MonitoringService.java
new file mode 100644
index 0000000..c640ae5
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/service/MonitoringService.java
@@ -0,0 +1,88 @@
+package io.logdash.example.service;
+
+import io.logdash.sdk.Logdash;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.lang.management.ManagementFactory;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+@Service
+public class MonitoringService {
+
+ private final Logdash logdash;
+ private final PetService petService;
+ private int monitoringCycles = 0;
+
+ public MonitoringService(Logdash logdash, PetService petService) {
+ this.logdash = logdash;
+ this.petService = petService;
+ }
+
+ @Scheduled(fixedRate = 10000) // Every 10 seconds
+ public void recordSystemMetrics() {
+ var logger = logdash.logger();
+ var metrics = logdash.metrics();
+
+ monitoringCycles++;
+
+ // JVM metrics
+ var memoryBean = ManagementFactory.getMemoryMXBean();
+ var heapUsage = memoryBean.getHeapMemoryUsage();
+
+ var heapUsedMB = heapUsage.getUsed() / (1024 * 1024);
+
+ // Simulated application metrics
+ var activeUsers = ThreadLocalRandom.current().nextInt(50, 300);
+ var errorRate = ThreadLocalRandom.current().nextDouble(0.1, 8.0);
+
+ logger.verbose("System monitoring cycle", Map.of(
+ "cycle", monitoringCycles,
+ "activeUsers", activeUsers
+ ));
+
+ // Send metrics to Logdash
+ metrics.set("jvm.memory.heap.used_mb", heapUsedMB);
+ metrics.set("app.users.active", activeUsers);
+
+ // Business metrics
+ var petCount = petService.getTotalCount();
+ metrics.set("pets.total.count", petCount);
+
+ // Increment counters
+ metrics.mutate("monitoring.cycles.total", 1);
+
+ // Simulate random events
+ if (ThreadLocalRandom.current().nextDouble() < 0.3) { // 30% chance
+ logger.info("Random application event", Map.of(
+ "activeUsers", activeUsers
+ ));
+ metrics.mutate("app.events.user_activity", 1);
+ }
+
+ if (errorRate > 3.0) {
+ logger.warn("High error rate detected", Map.of(
+ "errorRate", errorRate
+ ));
+ metrics.mutate("alerts.performance.slow_response", 1);
+ }
+ }
+
+ @Scheduled(fixedRate = 30000) // Every 30 seconds
+ public void simulateBusinessEvents() {
+ var logger = logdash.logger();
+ var metrics = logdash.metrics();
+
+ // Simulate random pet creation
+ if (ThreadLocalRandom.current().nextBoolean()) {
+ var pet = petService.createRandomPet();
+
+ logger.info(String.format("Background pet creation: petId=%s, name=%s, type=%s", pet.id(), pet.name(),
+ pet.type().name()));
+
+ metrics.mutate("pets.created.background", 1);
+ metrics.set("pets.total.count", petService.getTotalCount());
+ }
+ }
+}
diff --git a/examples/example-springboot/src/main/java/io/logdash/example/service/PetService.java b/examples/example-springboot/src/main/java/io/logdash/example/service/PetService.java
new file mode 100644
index 0000000..c346210
--- /dev/null
+++ b/examples/example-springboot/src/main/java/io/logdash/example/service/PetService.java
@@ -0,0 +1,58 @@
+package io.logdash.example.service;
+
+import io.logdash.example.domain.Pet;
+import io.logdash.example.domain.PetType;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+
+@Service
+public class PetService {
+
+ private final Map pets = new ConcurrentHashMap<>();
+ private final AtomicLong idGenerator = new AtomicLong(1);
+
+ public PetService() {
+ addPet("Burek", PetType.DOG);
+ addPet("Mruczek", PetType.CAT);
+ addPet("Olek", PetType.BIRD);
+ }
+
+ public Pet addPet(String name, PetType type) {
+ var pet = new Pet(idGenerator.getAndIncrement(), name, type);
+ pets.put(pet.id(), pet);
+ return pet;
+ }
+
+ public List getAllPets() {
+ return new ArrayList<>(pets.values());
+ }
+
+ public Optional findById(Long id) {
+ return Optional.ofNullable(pets.get(id));
+ }
+
+ public Pet createRandomPet() {
+ var names = List.of("Azor", "Bella", "Charlie", "Luna", "Max", "Molly", "Oscar");
+ var types = PetType.values();
+
+ var randomName = names.get(ThreadLocalRandom.current().nextInt(names.size()));
+ var randomType = types[ThreadLocalRandom.current().nextInt(types.length)];
+
+ return addPet(randomName, randomType);
+ }
+
+ public int getTotalCount() {
+ return pets.size();
+ }
+
+ public boolean deletePet(Long id) {
+ return pets.remove(id) != null;
+ }
+}
diff --git a/examples/example-springboot/src/main/resources/application.yml b/examples/example-springboot/src/main/resources/application.yml
new file mode 100644
index 0000000..e4339c1
--- /dev/null
+++ b/examples/example-springboot/src/main/resources/application.yml
@@ -0,0 +1,28 @@
+server:
+ port: 8080
+
+spring:
+ application:
+ name: logdash-petclinic-demo
+
+logdash:
+ api-key: ${LOGDASH_API_KEY:your-api-key}
+ base-url: https://api.logdash.io
+ enable-console-output: true
+ enable-verbose-logging: false
+ request-timeout-ms: 10000
+ max-concurrent-requests: 20
+
+management:
+ endpoints:
+ web:
+ exposure:
+ include: health,info,metrics
+ endpoint:
+ health:
+ show-details: always
+
+logging:
+ level:
+ io.logdash.example: INFO
+ root: WARN
\ No newline at end of file
diff --git a/examples/example-springboot/src/test/java/io/logdash/example/SpringBootExampleApplicationTest.java b/examples/example-springboot/src/test/java/io/logdash/example/SpringBootExampleApplicationTest.java
new file mode 100644
index 0000000..a996ba5
--- /dev/null
+++ b/examples/example-springboot/src/test/java/io/logdash/example/SpringBootExampleApplicationTest.java
@@ -0,0 +1,72 @@
+package io.logdash.example;
+
+import io.logdash.sdk.Logdash;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@TestPropertySource(properties = {
+ "logdash.api-key=test-key",
+ "logdash.enable-console-output=false",
+ "logdash.enable-verbose-logging=false"
+})
+class SpringBootExampleApplicationTest {
+
+ @LocalServerPort
+ private int port;
+
+ @Autowired
+ private TestRestTemplate restTemplate;
+
+ @Autowired
+ private Logdash logdash;
+
+ @Test
+ void should_start_application_context() {
+ assertThat(logdash).isNotNull();
+ assertThat(logdash.logger()).isNotNull();
+ assertThat(logdash.metrics()).isNotNull();
+ }
+
+ @Test
+ void should_get_all_pets() {
+ var response = restTemplate.getForEntity(
+ "http://localhost:" + port + "/api/pets",
+ String.class
+ );
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).contains("Burek", "Mruczek", "Olek");
+ }
+
+ @Test
+ void should_create_random_pet() {
+ var response = restTemplate.postForEntity(
+ "http://localhost:" + port + "/api/pets/random",
+ null,
+ String.class
+ );
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).isNotBlank();
+ }
+
+ @Test
+ void should_expose_actuator_health_endpoint() {
+ var response = restTemplate.getForEntity(
+ "http://localhost:" + port + "/actuator/health",
+ String.class
+ );
+
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
+ assertThat(response.getBody()).contains("\"status\":\"UP\"");
+ }
+
+}
diff --git a/examples/example-springboot/src/test/java/io/logdash/example/controller/PetControllerTest.java b/examples/example-springboot/src/test/java/io/logdash/example/controller/PetControllerTest.java
new file mode 100644
index 0000000..09ac17a
--- /dev/null
+++ b/examples/example-springboot/src/test/java/io/logdash/example/controller/PetControllerTest.java
@@ -0,0 +1,167 @@
+package io.logdash.example.controller;
+
+import io.logdash.example.domain.Pet;
+import io.logdash.example.domain.PetType;
+import io.logdash.example.service.PetService;
+import io.logdash.sdk.log.LogdashLogger;
+import io.logdash.sdk.metrics.LogdashMetrics;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@WebMvcTest(PetController.class)
+@TestPropertySource(properties = {
+ "logdash.api-key=test-key",
+ "logdash.enable-console-output=false"
+})
+class PetControllerTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockitoBean
+ private PetService petService;
+
+ @MockitoBean
+ private LogdashLogger logger;
+
+ @MockitoBean
+ private LogdashMetrics metrics;
+
+ @Test
+ void should_get_all_pets() throws Exception {
+ // Given
+ var pets = List.of(
+ new Pet(1L, "Burek", PetType.DOG),
+ new Pet(2L, "Mruczek", PetType.CAT)
+ );
+ given(petService.getAllPets()).willReturn(pets);
+
+ // When & Then
+ mockMvc.perform(get("/api/pets"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$").isArray())
+ .andExpect(jsonPath("$.length()").value(2))
+ .andExpect(jsonPath("$[0].name").value("Burek"))
+ .andExpect(jsonPath("$[1].name").value("Mruczek"));
+
+ // Verify logging and metrics
+ verify(logger).info(eq("Retrieved all pets"), anyMap());
+ verify(metrics).mutate("api.requests.total", 1);
+ verify(metrics).mutate("api.pets.list.requests", 1);
+ verify(metrics).set("pets.total.count", 2);
+ }
+
+ @Test
+ void should_get_pet_by_id() throws Exception {
+ // Given
+ var pet = new Pet(1L, "Burek", PetType.DOG);
+ given(petService.findById(1L)).willReturn(Optional.of(pet));
+
+ // When & Then
+ mockMvc.perform(get("/api/pets/1"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(1))
+ .andExpect(jsonPath("$.name").value("Burek"))
+ .andExpect(jsonPath("$.type").value("DOG"));
+
+ // Verify logging and metrics
+ verify(logger).debug(eq("Fetching pet with id: {}"), anyMap());
+ verify(logger).info(eq("Pet found"), anyMap());
+ verify(metrics).mutate("api.pets.found", 1);
+ }
+
+ @Test
+ void should_return_not_found_for_non_existent_pet() throws Exception {
+ // Given
+ given(petService.findById(999L)).willReturn(Optional.empty());
+
+ // When & Then
+ mockMvc.perform(get("/api/pets/999"))
+ .andExpect(status().isNotFound());
+
+ // Verify logging and metrics
+ verify(logger).debug(eq("Fetching pet with id: {}"), anyMap());
+ verify(logger).warn(eq("Pet not found"), anyMap());
+ verify(metrics).mutate("api.pets.not_found", 1);
+ }
+
+ @Test
+ void should_create_random_pet() throws Exception {
+ // Given
+ var pet = new Pet(3L, "Lucky", PetType.DOG);
+ given(petService.createRandomPet()).willReturn(pet);
+
+ // When & Then
+ mockMvc.perform(post("/api/pets/random"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(3))
+ .andExpect(jsonPath("$.name").value("Lucky"))
+ .andExpect(jsonPath("$.type").value("DOG"));
+
+ // Verify logging and metrics
+ verify(logger).info(eq("Created random pet"), anyMap());
+ verify(metrics).mutate("api.pets.created", 1);
+ verify(metrics).mutate("api.pets.random.created", 1);
+ }
+
+ @Test
+ void should_create_pet_with_valid_request() throws Exception {
+ // Given
+ var pet = new Pet(4L, "Bella", PetType.CAT);
+ given(petService.addPet("Bella", PetType.CAT)).willReturn(pet);
+
+ // When & Then
+ mockMvc.perform(post("/api/pets")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "name": "Bella",
+ "type": "CAT"
+ }
+ """))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id").value(4))
+ .andExpect(jsonPath("$.name").value("Bella"))
+ .andExpect(jsonPath("$.type").value("CAT"));
+
+ // Verify logging and metrics
+ verify(logger).info(eq("Created pet"), anyMap());
+ verify(metrics).mutate("api.pets.created", 1);
+ verify(metrics).mutate("api.pets.manual.created", 1);
+ }
+
+ @Test
+ void should_handle_delete_pet() throws Exception {
+ // Given
+ given(petService.deletePet(1L)).willReturn(true);
+
+ // When & Then
+ mockMvc.perform(delete("/api/pets/1"))
+ .andExpect(status().isNoContent());
+ }
+
+ @Test
+ void should_handle_delete_non_existent_pet() throws Exception {
+ // Given
+ given(petService.deletePet(999L)).willReturn(false);
+
+ // When & Then
+ mockMvc.perform(delete("/api/pets/999"))
+ .andExpect(status().isNotFound());
+ }
+}
diff --git a/examples/example-springboot/src/test/resources/application-test.yml b/examples/example-springboot/src/test/resources/application-test.yml
new file mode 100644
index 0000000..4e99e94
--- /dev/null
+++ b/examples/example-springboot/src/test/resources/application-test.yml
@@ -0,0 +1,17 @@
+spring:
+ main:
+ banner-mode: off
+ output:
+ ansi:
+ enabled: never
+
+logdash:
+ api-key: test-key
+ enable-console-output: false
+ enable-verbose-logging: false
+
+logging:
+ level:
+ io.logdash.example: WARN
+ org.springframework: WARN
+ org.mockito: WARN
diff --git a/pom.xml b/pom.xml
index 505e95e..dbc609e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
io.logdash
logdash
- 0.1.3
+ 0.2.0
jar
Logdash Java SDK
@@ -35,19 +35,6 @@
https://github.com/logdash-io/java-sdk
-
-
- github
- GitHub Packages
- https://maven.pkg.github.com/logdash-io/java-sdk
-
-
- github
- GitHub Packages
- https://maven.pkg.github.com/logdash-io/java-sdk
-
-
-
17
${java.version}
@@ -191,7 +178,7 @@
maven-compiler-plugin
${maven-compiler-plugin.version}
- ${java.version}
+ ${maven.compiler.release}
-parameters
-Xlint:unchecked
@@ -429,6 +416,13 @@
true
4
+
+ src/main/java/**/*.java
+ src/test/java/**/*.java
+ examples/**/src/main/java/**/*.java
+ examples/**/src/test/java/**/*.java
+ check-deployed-package/**/*.java
+
**/target/**
**/generated/**
@@ -438,6 +432,7 @@
**/*.md
+ examples/**/*.md
@@ -459,6 +454,7 @@
pom.xml
**/pom.xml
+ examples/**/pom.xml
false
@@ -483,6 +479,29 @@
+
+ local-dev
+
+ true
+
+
+ false
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven-deploy-plugin.version}
+
+ true
+
+
+
+
+
+
github-release
@@ -492,6 +511,18 @@
true
+
+
+ github
+ GitHub Packages
+ https://maven.pkg.github.com/logdash-io/java-sdk
+
+
+ github
+ GitHub Packages
+ https://maven.pkg.github.com/logdash-io/java-sdk
+
+
@@ -501,6 +532,7 @@
${maven-deploy-plugin.version}
true
+ false
@@ -519,7 +551,7 @@
central
Central Repository Snapshots
- https://central.sonatype.com/api/v1/publisher
+ https://central.sonatype.com/repository/maven-snapshots/
@@ -563,6 +595,15 @@
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven-deploy-plugin.version}
+
+ false
+
+
diff --git a/scripts/dev-cycle.sh b/scripts/dev-cycle.sh
new file mode 100755
index 0000000..4322e7f
--- /dev/null
+++ b/scripts/dev-cycle.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -e
+
+echo "🔧 Building local Logdash SDK..."
+mvn clean install -DskipTests
+
+echo "🚀 Testing SpringBoot example with local snapshot..."
+cd examples/example-springboot
+mvn clean spring-boot:run -Plocal-dev
+
+echo "✅ Development cycle completed!"
diff --git a/src/main/java/io/logdash/sdk/transport/HttpTransport.java b/src/main/java/io/logdash/sdk/transport/HttpTransport.java
index c649b72..217537b 100644
--- a/src/main/java/io/logdash/sdk/transport/HttpTransport.java
+++ b/src/main/java/io/logdash/sdk/transport/HttpTransport.java
@@ -38,7 +38,7 @@ public final class HttpTransport implements LogdashTransport, AutoCloseable {
private static final String METRICS_ENDPOINT = "/metrics";
private static final String API_KEY_HEADER = "project-api-key";
- private static final String USER_AGENT = "logdash-java-sdk/0.1.3";
+ private static final String USER_AGENT = "logdash-java-sdk/0.2.0";
private static final String CONTENT_TYPE = "application/json";
private final LogdashConfig config;
diff --git a/src/test/java/io/logdash/sdk/transport/HttpTransportTest.java b/src/test/java/io/logdash/sdk/transport/HttpTransportTest.java
index ed5c620..1f73fd0 100644
--- a/src/test/java/io/logdash/sdk/transport/HttpTransportTest.java
+++ b/src/test/java/io/logdash/sdk/transport/HttpTransportTest.java
@@ -82,7 +82,7 @@ void should_successfully_send_log_entry() {
postRequestedFor(urlEqualTo("/logs"))
.withHeader("Content-Type", equalTo("application/json"))
.withHeader("project-api-key", equalTo("test-api-key"))
- .withHeader("User-Agent", equalTo("logdash-java-sdk/0.1.3")));
+ .withHeader("User-Agent", equalTo("logdash-java-sdk/0.2.0")));
}
@Test
@@ -105,7 +105,7 @@ void should_successfully_send_metric_entry() {
putRequestedFor(urlEqualTo("/metrics"))
.withHeader("Content-Type", equalTo("application/json"))
.withHeader("project-api-key", equalTo("test-api-key"))
- .withHeader("User-Agent", equalTo("logdash-java-sdk/0.1.3")));
+ .withHeader("User-Agent", equalTo("logdash-java-sdk/0.2.0")));
}
@ParameterizedTest