-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
prime-1.13.0 - Pseudo MSISDN. Metrics.
- Loading branch information
Showing
112 changed files
with
1,612 additions
and
668 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,4 +35,5 @@ secrets/* | |
.nb-gradle | ||
.swagger_gen_dir | ||
|
||
api_descriptor.pb | ||
ocs_descriptor.pb | ||
metrics_descriptor.pb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Analytics API |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 2 additions & 3 deletions
5
ocs-api/src/main/proto/analytics.proto → ...s-grpc-api/src/main/proto/analytics.proto
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
syntax = "proto3"; | ||
|
||
package org.ostelco.prime.metrics.api; | ||
|
||
option java_multiple_files = true; | ||
option java_package = "org.ostelco.prime.metrics.api"; | ||
|
||
// This is used to report Analytics events from OCSgw to Prime | ||
|
||
service OcsgwAnalyticsService { | ||
rpc OcsgwAnalyticsEvent (stream OcsgwAnalyticsReport) returns (OcsgwAnalyticsReply) {} | ||
} | ||
|
||
message OcsgwAnalyticsReport { | ||
uint32 activeSessions = 1; | ||
} | ||
|
||
message OcsgwAnalyticsReply { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
plugins { | ||
id "org.jetbrains.kotlin.jvm" version "1.2.61" | ||
id "java-library" | ||
} | ||
|
||
dependencies { | ||
implementation project(":prime-api") | ||
|
||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" | ||
implementation "io.dropwizard:dropwizard-core:$dropwizardVersion" | ||
implementation "com.google.cloud:google-cloud-pubsub:$googleCloudVersion" | ||
|
||
testImplementation "io.dropwizard:dropwizard-testing:$dropwizardVersion" | ||
testImplementation 'org.mockito:mockito-core:2.18.3' | ||
testImplementation 'org.assertj:assertj-core:3.10.0' | ||
} | ||
|
||
apply from: '../jacoco.gradle' |
56 changes: 56 additions & 0 deletions
56
analytics-module/src/main/kotlin/org/ostelco/prime/analytics/AnalyticsGrpcServer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package org.ostelco.prime.analytics | ||
|
||
import io.dropwizard.lifecycle.Managed | ||
import io.grpc.BindableService | ||
import io.grpc.Server | ||
import io.grpc.ServerBuilder | ||
import org.ostelco.prime.logger | ||
import java.io.IOException | ||
|
||
/** | ||
* This is Analytics Server running on gRPC protocol. | ||
* Its startup and shutdown are managed by Dropwizard's lifecycle | ||
* through the Managed interface. | ||
* | ||
*/ | ||
class AnalyticsGrpcServer(private val port: Int, service: BindableService) : Managed { | ||
|
||
private val logger by logger() | ||
|
||
// may add Transport Security with Certificates if needed. | ||
// may add executor for control over number of threads | ||
private val server: Server = ServerBuilder.forPort(port).addService(service).build() | ||
|
||
/** | ||
* Startup is managed by Dropwizard's lifecycle. | ||
* | ||
* @throws IOException ... sometimes, perhaps. | ||
*/ | ||
override fun start() { | ||
server.start() | ||
logger.info("Analytics Server started, listening for incoming gRPC traffic on {}", port) | ||
} | ||
|
||
/** | ||
* Shutdown is managed by Dropwizard's lifecycle. | ||
* | ||
* @throws InterruptedException When something goes wrong. | ||
*/ | ||
override fun stop() { | ||
logger.info("Stopping Analytics Server listening for gRPC traffic on {}", port) | ||
server.shutdown() | ||
blockUntilShutdown() | ||
} | ||
|
||
/** | ||
* Used for unit testing | ||
*/ | ||
fun forceStop() { | ||
logger.info("Stopping forcefully Analytics Server listening for gRPC traffic on {}", port) | ||
server.shutdownNow() | ||
} | ||
|
||
private fun blockUntilShutdown() { | ||
server.awaitTermination() | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
analytics-module/src/main/kotlin/org/ostelco/prime/analytics/AnalyticsGrpcService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package org.ostelco.prime.analytics | ||
|
||
import io.grpc.stub.StreamObserver | ||
import org.ostelco.prime.analytics.PrimeMetric.ACTIVE_SESSIONS | ||
import org.ostelco.prime.analytics.metrics.CustomMetricsRegistry | ||
import org.ostelco.prime.logger | ||
import org.ostelco.prime.metrics.api.OcsgwAnalyticsReply | ||
import org.ostelco.prime.metrics.api.OcsgwAnalyticsReport | ||
import org.ostelco.prime.metrics.api.OcsgwAnalyticsServiceGrpc | ||
import java.util.* | ||
|
||
|
||
/** | ||
* Serves incoming GRPC analytcs requests. | ||
* | ||
* It's implemented as a subclass of [OcsServiceGrpc.OcsServiceImplBase] overriding | ||
* methods that together implements the protocol described in the analytics protobuf | ||
* file: ocs_analytics.proto | ||
*` | ||
* service OcsgwAnalyticsService { | ||
* rpc OcsgwAnalyticsEvent (stream OcsgwAnalyticsReport) returns (OcsgwAnalyticsReply) {} | ||
* } | ||
*/ | ||
|
||
class AnalyticsGrpcService : OcsgwAnalyticsServiceGrpc.OcsgwAnalyticsServiceImplBase() { | ||
|
||
private val logger by logger() | ||
|
||
/** | ||
* Handles the OcsgwAnalyticsEvent message. | ||
*/ | ||
override fun ocsgwAnalyticsEvent(ocsgwAnalyticsReply: StreamObserver<OcsgwAnalyticsReply>): StreamObserver<OcsgwAnalyticsReport> { | ||
val streamId = newUniqueStreamId() | ||
return StreamObserverForStreamWithId(streamId) | ||
} | ||
|
||
/** | ||
* Return an unique ID based on Java's UUID generator that uniquely | ||
* identifies a stream of values. | ||
* @return A new unique identifier. | ||
*/ | ||
private fun newUniqueStreamId(): String { | ||
return UUID.randomUUID().toString() | ||
} | ||
|
||
private inner class StreamObserverForStreamWithId internal constructor(private val streamId: String) : StreamObserver<OcsgwAnalyticsReport> { | ||
|
||
/** | ||
* This method gets called every time a new active session count is sent | ||
* from the OCS GW. | ||
* @param request provides current active session as a counter with a timestamp | ||
*/ | ||
override fun onNext(request: OcsgwAnalyticsReport) { | ||
CustomMetricsRegistry.updateMetricValue(ACTIVE_SESSIONS, request.activeSessions.toLong()) | ||
} | ||
|
||
override fun onError(t: Throwable) { | ||
// TODO vihang: handle onError for stream observers | ||
} | ||
|
||
override fun onCompleted() { | ||
logger.info("AnalyticsGrpcService with streamId: {} completed", streamId) | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
analytics-module/src/main/kotlin/org/ostelco/prime/analytics/AnalyticsModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package org.ostelco.prime.analytics | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
import com.fasterxml.jackson.annotation.JsonTypeName | ||
import io.dropwizard.setup.Environment | ||
import org.hibernate.validator.constraints.NotEmpty | ||
import org.ostelco.prime.analytics.metrics.CustomMetricsRegistry | ||
import org.ostelco.prime.analytics.publishers.DataConsumptionInfoPublisher | ||
import org.ostelco.prime.module.PrimeModule | ||
|
||
@JsonTypeName("analytics") | ||
class AnalyticsModule : PrimeModule { | ||
|
||
@JsonProperty("config") | ||
fun setConfig(config: AnalyticsConfig) { | ||
ConfigRegistry.config = config | ||
} | ||
|
||
override fun init(env: Environment) { | ||
|
||
CustomMetricsRegistry.init(env.metrics()) | ||
|
||
val server = AnalyticsGrpcServer(8083, AnalyticsGrpcService()) | ||
|
||
env.lifecycle().manage(server) | ||
|
||
// dropwizard starts Analytics events publisher | ||
env.lifecycle().manage(DataConsumptionInfoPublisher) | ||
} | ||
} | ||
|
||
class AnalyticsConfig { | ||
@NotEmpty | ||
@JsonProperty("projectId") | ||
lateinit var projectId: String | ||
|
||
@NotEmpty | ||
@JsonProperty("topicId") | ||
lateinit var topicId: String | ||
} | ||
|
||
object ConfigRegistry { | ||
lateinit var config: AnalyticsConfig | ||
} |
19 changes: 19 additions & 0 deletions
19
analytics-module/src/main/kotlin/org/ostelco/prime/analytics/AnalyticsServiceImpl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.ostelco.prime.analytics | ||
|
||
import org.ostelco.prime.analytics.metrics.CustomMetricsRegistry | ||
import org.ostelco.prime.analytics.publishers.DataConsumptionInfoPublisher | ||
import org.ostelco.prime.logger | ||
|
||
class AnalyticsServiceImpl : AnalyticsService { | ||
|
||
private val logger by logger() | ||
|
||
override fun reportTrafficInfo(msisdn: String, usedBytes: Long, bundleBytes: Long) { | ||
logger.info("reportTrafficInfo : msisdn {} usedBytes {} bundleBytes {}", msisdn, usedBytes, bundleBytes) | ||
DataConsumptionInfoPublisher.publish(msisdn, usedBytes, bundleBytes) | ||
} | ||
|
||
override fun reportMetric(primeMetric: PrimeMetric, value: Long) { | ||
CustomMetricsRegistry.updateMetricValue(primeMetric, value) | ||
} | ||
} |
74 changes: 74 additions & 0 deletions
74
...ytics-module/src/main/kotlin/org/ostelco/prime/analytics/metrics/CustomMetricsRegistry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package org.ostelco.prime.analytics.metrics | ||
|
||
import com.codahale.metrics.Counter | ||
import com.codahale.metrics.Gauge | ||
import com.codahale.metrics.MetricRegistry | ||
import org.ostelco.prime.analytics.MetricType.COUNTER | ||
import org.ostelco.prime.analytics.MetricType.GAUGE | ||
import org.ostelco.prime.analytics.PrimeMetric | ||
|
||
/** | ||
* Singleton wrapper dropwizard metrics. | ||
*/ | ||
object CustomMetricsRegistry { | ||
|
||
private lateinit var registry: MetricRegistry | ||
// boolean flag to avoid access to late init registry if it not yet initialized | ||
private var isInitialized = false | ||
|
||
// map of long values which will act as cache for Gauge | ||
private val gaugeValueMap: MutableMap<PrimeMetric, Long> = mutableMapOf() | ||
|
||
// map of counters | ||
private val counterMap: MutableMap<PrimeMetric, Counter> = mutableMapOf() | ||
|
||
@Synchronized | ||
fun init(registry: MetricRegistry) { | ||
this.registry = registry | ||
isInitialized = true | ||
counterMap.keys.forEach { registerCounter(it) } | ||
gaugeValueMap.keys.forEach { registerGauge(it) } | ||
} | ||
|
||
/** | ||
* Update metric value. | ||
* | ||
* If metric is of type COUNTER, then the counter is increment by that value. | ||
* If metric is of type GAUGE, then the gauge source is set to that value. | ||
* | ||
* @param primeMetric | ||
* @param value | ||
*/ | ||
@Synchronized | ||
fun updateMetricValue(primeMetric: PrimeMetric, value: Long) { | ||
when (primeMetric.metricType) { | ||
COUNTER -> { | ||
val counterExists = counterMap.containsKey(primeMetric) | ||
counterMap.getOrPut(primeMetric) { Counter() }.inc(value) | ||
if (isInitialized && !counterExists) { | ||
registerCounter(primeMetric) | ||
} | ||
} | ||
GAUGE -> { | ||
val existingGaugeValue = gaugeValueMap.put(primeMetric, value) | ||
if (isInitialized && existingGaugeValue == null) { | ||
registerGauge(primeMetric) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Register counter with value from counterMap | ||
*/ | ||
private fun registerCounter(primeMetric: PrimeMetric) { | ||
registry.register(primeMetric.metricName, counterMap[primeMetric]) | ||
} | ||
|
||
/** | ||
* Register gauge with value from gaugeValueMap as its source | ||
*/ | ||
private fun registerGauge(primeMetric: PrimeMetric) { | ||
registry.register(primeMetric.metricName, Gauge<Long> { gaugeValueMap[primeMetric] }) | ||
} | ||
} |
Oops, something went wrong.