This projects features the integration of the Hawk features for the Java side. It is written in Kotlin with full compatibility to Java using Spring Boot and communicates with the Hawk Service. The current implementation contains a Spring Boot Web integration for HTTP. In the future more frameworks might be added. To rapidly implement a custom framework there is the common module, which serves as a base of the protocol.
The idea of the Java Integration is to intercept the traffic of the application and extract the atomic data references (Usages) and sample / batch / export them to the Hawk Service. The benefit of using the Java Integration is that you can track encrypted traffic or in the future even traffic to external APIs.
If you have a Spring Boot application, and you are using Spring Boot Web (not WebFlux) the easiest way is this module.
You need to add the following dependency to your build management tool:
Maven:
<dependency>
<groupId>org.datausagetracing.integration</groupId>
<artifactId>spring</artifactId>
<version>VERSION</version>
</dependency>Gradle (Groovy):
implementation 'org.datausagetracing.integration:spring:VERSION'Gradle (Kotlin):
implementation("org.datausagetracing.integration:spring:VERSION")Just replace VERSION
with the version you like
.
Annotate your main class / Spring Boot Class with @EnableHawk
package org.datausagetracing.integration.spring.example
import org.datausagetracing.integration.spring.EnableHawk
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@EnableHawk
@SpringBootApplication
class ExampleApplicationThis module uses Spring Configuration to configure itself. There are the following options:
| Name in *.properties | Env Name | Default | Description |
|---|---|---|---|
| hawk.usage.url | HAWK_USAGE_URL | http://datausagetracing |
The URL of the Hawk Service |
| hawk.usage.batch | HAWK_USAGE_BATCH | true |
Buffers usages and sends them in one large request |
| hawk.usage.sampler.ratio | HAWK_USAGE_SAMPLER_RATIO | 1.0 |
Percent of Usage's to send. 1.0 = all, 0.0 = nothing |
For details, see Common Section.
For custom UsageSampler, UsageExporter, UsageProcessor implementations, just mark the class as
a Spring
component and add a javax.annotation.Priority annotation. When overriding UsageProcessor you may
break some configuration options. You are also required to obtain the UsageSampler
/ UsageExporter yourself.
The common module declares the structure of the Usage in an object-oriented fashion.
These objects are to be serialized with some JSON ORM
like Jackson and can not be used for
deserialization. The Usage class consists of the following fields: id: UUID metadata: Metadata
, endpoint: Endpoint, initatior: Initiator, fields: List<Field>, tags: Tags. Every field
except for id and fields have their own build, which should be used to instantiate them.
To build the Usage, the UsageBuilder is used. All of these Builder-classes are mergeable with
other builders of the same type. Some classes are backed by a map, which allows them to take
properties. This offers flexibility in filling the UsageBuilder. The Usage has
support for Kotlin DSL.
An example usage might look like this:
val usage = usage {
metadata {
side = "server"
phase = "request"
timestamp(ZonedDateTime.now())
}
endpoint {
host = "test"
protocol = "HTTP/1.1"
method = "POST"
path = "/api/user/4"
status = "200"
add("custom1", "custom_xy")
}
field {
format = "json"
namespace = "body"
path = "$.[*].name"
count = 3
}
propertiesField {
namespace = "header"
path = "name"
count = 1
}
tags {
add("custom2", "yx")
}
}To make the creation of big Usage's cleaner and more modular the UsageFactory comes into place.
The UsageContext can be implemented to pass custom data into the Usage-Creation process. Default
implementations are JavaEEHttpRequestUsageContext and JavaEEHttpResponseUsageContext which wrap
around the javax.servlet Request / Response implementations.
The modularity aspect comes from the UsageExtractor which allows to fill the UsageBuilder
instance
incrementally, by getting the UsageContext and the mutable UsageBuilder instance.
There are many default implementations for the HTTP Protocol, which can be used when
the UsageContext
implements either HttpRequestUsageContext or HttpResponseUsageContext. Just look inside
the org.datausagetracing.integration.common.usage.factory.extractor package for implementations.
The UsageFactory instance takes a list of extractors and builds a Usage instance based on the
passed context. It is recommended to use the UsageFactoryBuilder to create the UsageFactory in a
cleaner way. The UsageFactoryBuilder also supports Kotlin DSL.
Here is an example of creating a Usage:
// In the initializer
val usageFactory = requestUsageFactory {
install(HttpRequestEndpointUsageExtractor())
install(HttpRequestInitiatorUsageExtractor())
install(HttpFieldUsageExtractor(bodyFieldExtractor = JacksonFieldExtractor()))
install(MetadataUsageExtractor())
}
// Every request (can be async)
val reference = UUID.random() // Or take it from the request header
val usage = requestUsageFactory.invoke(
JavaEEHttpRequestUsageContext(
request,
reference,
LocalDateTime.now()
)
)To sample, batch and export the generated Usages, the UsageSampler, UsageProcessor
and UsageExporter classes are used.
The UsageSampler can be implemented to determine whether a Usage should be included or not.
Default implementations are the IdRatioUsageSampler, which takes in how many percent of Usages
should be exported (between 0.0 = none and 1.0 = all) and the AlwaysOnUsageSampler, which exports
every Usage.
The business-logic to export the Usages and send them to the Hawk-Service, is implemented inside the
implementation of the UsageExporter interface. The UsageProcessor receives every Usage and
samples and exports it, using the other classes. Default implementations are
the BatchingUsageProcessor, which buffer's multiple Usage's and exports them in one 'Batch' and
the SimpleUsageProcessor which simply samples and export every Usage.
See the spring module for an example.