Skip to content

Repository files navigation

feddi Usage Reporter

Java 21 modules for sending protobuf usage reports to the feddi Platform.

Modules

  • usage-proto: protobuf schema and generated Java contract classes.
  • usage-reporter: GraphQL Java based API usage reporter, reactive client facade, and pluggable reactive HTTP transport API.

feddi-platform depends only on usage-proto. Applications should depend on usage-reporter, provide their own ReactiveHttpClient implementation, and configure ApiUsageReporter with a feddi graph variant key via feddiGraphVariantKey(...). The reporter sends gzipped protobuf requests to https://feddi.dev by default. Tests and self-hosted deployments can override the host, but the endpoint paths are fixed to /api/usage-proto/known-operation-hashes, /api/usage-proto/operations, and /api/usage-proto/usage.

ApiUsageReporter receives a GraphQL Java Document, operation name, and GraphQLSchema for each completed API call. It generates an input-aware canonical operation document with AstSignature, extracts field coordinates, field argument coordinates, and input object field coordinates, optionally samples high-throughput traffic, and periodically flushes protobuf batches to the feddi Platform. Operation definitions are registered separately from usage events. Each reporter preloads hashes already registered for the graph variant and keeps an in-memory cache of hashes it registers itself, so repeated requests send only the hash and request-specific usage metadata.

Usage Guide

Add the usage client to the API process that executes GraphQL requests:

dependencies {
    implementation 'dev.feddi:feddi-usage-reporter:<release-version>'
}

Published versions are listed in the Maven Central repository index at https://repo1.maven.org/maven2/dev/feddi/. Release versions are timestamps generated by the feddi Platform release pipeline, for example 2026-05-15-1229; use the newest version listed there unless you need to stay on a specific feddi Platform release.

The library does not bundle an HTTP implementation. Provide a ReactiveHttpClient adapter for the HTTP client already used by the host application. For example, with Spring WebClient:

import dev.feddi.api.usage.http.ReactiveHttpClient;
import dev.feddi.api.usage.http.ReactiveHttpRequest;
import dev.feddi.api.usage.http.ReactiveHttpResponse;
import org.springframework.http.HttpMethod;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

final class WebClientReactiveHttpClient implements ReactiveHttpClient {

    private final WebClient webClient = WebClient.builder().build();

    @Override
    public Mono<ReactiveHttpResponse> exchange(ReactiveHttpRequest request) {
        var spec = webClient
                .method(HttpMethod.valueOf(request.method()))
                .uri(request.uri());

        request.headers().forEach((name, values) ->
                values.forEach(value -> spec.header(name, value)));

        return spec.body(request.body(), byte[].class)
                .exchangeToMono(response -> response.bodyToMono(byte[].class)
                        .defaultIfEmpty(new byte[0])
                        .map(body -> new ReactiveHttpResponse(
                                response.statusCode().value(),
                                response.headers().asHttpHeaders(),
                                Mono.just(body))));
    }
}

Create one reporter for the process and reuse it for all requests that belong to the configured graph variant:

import dev.feddi.api.usage.ApiUsageReporter;

var reporter = ApiUsageReporter.builder(new WebClientReactiveHttpClient())
        .feddiGraphVariantKey(System.getenv("FEDDI_GRAPH_VARIANT_KEY"))
        .build();

The default host is https://feddi.dev. Self-hosted deployments and tests can override only the host; the operation-registration and usage-ingestion paths remain fixed:

var reporter = ApiUsageReporter.builder(httpClient)
        .feddiGraphVariantKey(feddiGraphVariantKey)
        .host("https://platform.example.com")
        .build();

Report each completed GraphQL request after execution. The Document should be the parsed GraphQL Java document for the request, and the GraphQLSchema should be the executable schema used to run it.

Pass the runtime variables map when the operation used variables. This lets the reporter detect which optional input object fields were actually present in the request. Inline input object literals are analyzed from the GraphQL document.

import dev.feddi.api.usage.ApiUsageInvocation;

long startedAt = System.nanoTime();

// Execute the GraphQL request with your GraphQL Java runtime.

boolean queued = reporter.report(ApiUsageInvocation.builder()
        .document(document)
        .operationName(operationName)
        .schema(graphQLSchema)
        .variables(variables)
        .durationNanos(System.nanoTime() - startedAt)
        .httpError(httpStatusCode >= 500)
        .graphqlError(!executionResult.getErrors().isEmpty())
        .clientName("orders-api")
        .clientVersion("1.0.0")
        .build());

report(...) is non-blocking. It returns false when the reporter is closed, the event was sampled out after adaptive sampling was explicitly enabled, or the in-memory queue is full.

Flush and close the reporter during application shutdown:

reporter.close();

Reactive hosts can use closeAsync() instead:

reporter.closeAsync().subscribe();

Protobuf Transport

The reporter uses three protobuf endpoints:

  • /api/usage-proto/known-operation-hashes fetches operation hashes already registered for the graph variant so new reporters can avoid duplicate operation-definition uploads.
  • /api/usage-proto/operations registers operation hashes, canonical documents, field coordinates, and input usage coordinates.
  • /api/usage-proto/usage ingests request usage events by operation hash.

Request bodies are gzip-compressed and sent with Content-Encoding: gzip.

Configuration

  • feddiGraphVariantKey(...) is required and is used as the bearer token.
  • host(...) is optional. It must be an absolute host URI with no path other than an optional trailing slash, query, or fragment.
  • batchWindow(min, max) controls the randomized scheduled background flush window. Each background flush samples a new delay between the two durations. The default window is 20-40 seconds.
  • Each flush sends all usage records that are queued when draining starts.
  • maxQueueSize(...) controls the pending in-memory queue size and therefore bounds the largest automatic flush request. The default is 22,222 records, derived from a 1 MB compressed request budget and about 45 compressed bytes per usage record. The absolute maximum is 44,444 records, derived from a 2 MB compressed request budget. Lower values can be configured; higher values are rejected.
  • samplingEnabled(...) controls adaptive sampling and defaults to false.
  • flushErrorHandler(...) receives background flush failures and per-record analysis failures.

When adaptive sampling is enabled, sampling is recalculated on every flush from the request count observed during the batch window. Traffic below 100 requests per second sends every event. Higher traffic is sampled and sent with a multiplier so aggregate counts remain representative.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages