Skip to content

Commit

Permalink
stats: introduce stats client (#992)
Browse files Browse the repository at this point in the history
Description: Introduces a StatsClient capable of giving out Counters that, when incremented, emit stats from Envoy's internal stats engine. Also refactors the top-level platform Engine interface to make both the StreamClient and the StatsClient available.
Risk Level: Low
Testing: Pending

Co-authored-by: Mike Schore <mike.schore@gmail.com>
Signed-off-by: Jingwei Hao <jingweih@lyft.com>
  • Loading branch information
Jingwei and goaway committed Aug 11, 2020
1 parent 3bd4282 commit b9a4334
Show file tree
Hide file tree
Showing 48 changed files with 655 additions and 225 deletions.
4 changes: 2 additions & 2 deletions envoy-mobile.tulsiproj/Configs/all.tulsigen
Expand Up @@ -23,8 +23,8 @@
"//library/swift/test:retry_policy_mapper_tests_lib",
"//library/swift/test:retry_policy_tests",
"//library/swift/test:retry_policy_tests_lib",
"//library/swift/test:stream_client_builder_tests",
"//library/swift/test:stream_client_builder_tests_lib"
"//library/swift/test:engine_builder_tests",
"//library/swift/test:engine_builder_tests_lib"
],
"optionSet" : {
"BazelBuildOptionsDebug" : {
Expand Down
11 changes: 6 additions & 5 deletions examples/java/hello_world/MainActivity.java
Expand Up @@ -5,15 +5,15 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import io.envoyproxy.envoymobile.AndroidStreamClientBuilder;
import io.envoyproxy.envoymobile.AndroidEngineBuilder;
import io.envoyproxy.envoymobile.Engine;
import io.envoyproxy.envoymobile.RequestHeaders;
import io.envoyproxy.envoymobile.RequestHeadersBuilder;
import io.envoyproxy.envoymobile.RequestMethod;
import io.envoyproxy.envoymobile.ResponseHeaders;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import io.envoyproxy.envoymobile.StreamClient;
import io.envoyproxy.envoymobile.shared.Failure;
import io.envoyproxy.envoymobile.shared.ResponseRecyclerViewAdapter;
import io.envoyproxy.envoymobile.shared.Success;
Expand All @@ -29,7 +29,7 @@ public class MainActivity extends Activity {
private static final String REQUEST_PATH = "/ping";
private static final String REQUEST_SCHEME = "https";

private StreamClient streamClient;
private Engine engine;
private RecyclerView recyclerView;

private HandlerThread thread = new HandlerThread(REQUEST_HANDLER_THREAD_NAME);
Expand All @@ -40,7 +40,7 @@ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

streamClient = new AndroidStreamClientBuilder(getApplication()).build();
engine = new AndroidEngineBuilder(getApplication()).build();

recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Expand Down Expand Up @@ -76,7 +76,8 @@ private void makeRequest() {
RequestHeaders requestHeaders = new RequestHeadersBuilder(RequestMethod.GET, REQUEST_SCHEME,
REQUEST_AUTHORITY, REQUEST_PATH)
.build();
streamClient.newStreamPrototype()
engine.streamClient()
.newStreamPrototype()
.setOnResponseHeaders((responseHeaders, endStream) -> {
Integer status = responseHeaders.getHttpStatus();
String message = "received headers with status " + status;
Expand Down
11 changes: 6 additions & 5 deletions examples/kotlin/hello_world/MainActivity.kt
Expand Up @@ -8,10 +8,10 @@ import android.util.Log
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.envoyproxy.envoymobile.AndroidStreamClientBuilder
import io.envoyproxy.envoymobile.AndroidEngineBuilder
import io.envoyproxy.envoymobile.Engine
import io.envoyproxy.envoymobile.RequestHeadersBuilder
import io.envoyproxy.envoymobile.RequestMethod
import io.envoyproxy.envoymobile.StreamClient
import io.envoyproxy.envoymobile.UpstreamHttpProtocol
import io.envoyproxy.envoymobile.shared.Failure
import io.envoyproxy.envoymobile.shared.ResponseRecyclerViewAdapter
Expand All @@ -30,13 +30,13 @@ class MainActivity : Activity() {
private val thread = HandlerThread(REQUEST_HANDLER_THREAD_NAME)
private lateinit var recyclerView: RecyclerView
private lateinit var viewAdapter: ResponseRecyclerViewAdapter
private lateinit var streamClient: StreamClient
private lateinit var engine: Engine

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

streamClient = AndroidStreamClientBuilder(application).build()
engine = AndroidEngineBuilder(application).build()

recyclerView = findViewById(R.id.recycler_view) as RecyclerView
recyclerView.layoutManager = LinearLayoutManager(this)
Expand Down Expand Up @@ -82,7 +82,8 @@ class MainActivity : Activity() {
)
.addUpstreamHttpProtocol(UpstreamHttpProtocol.HTTP2)
.build()
streamClient
engine
.streamClient()
.newStreamPrototype()
.setOnResponseHeaders { responseHeaders, _ ->
val status = responseHeaders.httpStatus ?: 0L
Expand Down
5 changes: 3 additions & 2 deletions examples/objective-c/hello_world/ViewController.m
Expand Up @@ -35,12 +35,13 @@ - (instancetype)init {
- (void)startEnvoy {
NSLog(@"starting Envoy...");
NSError *error;
StreamClientBuilder *builder = [[StreamClientBuilder alloc] init];
self.client = [builder buildAndReturnError:&error];
EngineBuilder *builder = [[EngineBuilder alloc] init];
id<Engine> engine = [builder buildAndReturnError:&error];
if (error) {
NSLog(@"starting Envoy failed: %@", error);
} else {
NSLog(@"started Envoy, beginning requests...");
self.client = [engine streamClient];
[self startRequests];
}
}
Expand Down
3 changes: 2 additions & 1 deletion examples/swift/hello_world/ViewController.swift
Expand Up @@ -15,9 +15,10 @@ final class ViewController: UITableViewController {
super.viewDidLoad()
do {
NSLog("starting Envoy...")
self.client = try StreamClientBuilder()
self.client = try EngineBuilder()
.addFilter(factory: DemoFilter.init)
.build()
.streamClient
} catch let error {
NSLog("starting Envoy failed: \(error)")
}
Expand Down
3 changes: 3 additions & 0 deletions library/common/config_template.cc
Expand Up @@ -212,6 +212,9 @@ stats_flush_interval: {{ stats_flush_interval_seconds }}s
- safe_regex:
google_re2: {}
regex: '^http.dispatcher.*'
- safe_regex:
google_re2: {}
regex: '^client.*'
- safe_regex:
google_re2: {}
regex: '^http.hcm.decompressor.*'
Expand Down
13 changes: 13 additions & 0 deletions library/common/engine.cc
Expand Up @@ -93,6 +93,19 @@ Engine::~Engine() {
main_thread_.join();
}

void Engine::recordCounter(std::string elements, uint64_t count) {
if (server_) {
server_->dispatcher().post([this, elements, count]() -> void {
static const std::string client = "client";
absl::string_view prefix{client};
absl::string_view dynamic_elements{elements};
Stats::Utility::counterFromElements(server_->serverFactoryContext().scope(),
{prefix, dynamic_elements})
.add(count);
});
}
}

void Engine::flushStats() {
// The server will be null if the post-init callback has not been completed within run().
// In this case, we can simply ignore the flush.
Expand Down
5 changes: 5 additions & 0 deletions library/common/engine.h
Expand Up @@ -35,6 +35,11 @@ class Engine {
*/
Http::Dispatcher& httpDispatcher();

/**
* Increment a counter with a given string of elements and by the given count.
*/
void recordCounter(std::string elements, uint64_t count);

/**
* Flush the stats sinks outside of a flushing interval.
* Note: stats flushing may not be synchronous.
Expand Down
7 changes: 7 additions & 0 deletions library/common/jni/jni_interface.cc
Expand Up @@ -77,6 +77,13 @@ Java_io_envoyproxy_envoymobile_engine_AndroidJniLibrary_setPreferredNetwork(JNIE
return set_preferred_network(static_cast<envoy_network_t>(network));
}

extern "C" JNIEXPORT void JNICALL
Java_io_envoyproxy_envoymobile_engine_JniLibrary_recordCounter(JNIEnv* env,
jclass, // class
jstring elements, jint count) {
record_counter(env->GetStringUTFChars(elements, nullptr), count);
}

extern "C" JNIEXPORT void JNICALL
Java_io_envoyproxy_envoymobile_engine_AndroidJniLibrary_flushStats(JNIEnv* env,
jclass // class
Expand Down
6 changes: 6 additions & 0 deletions library/common/main_interface.cc
Expand Up @@ -69,6 +69,12 @@ envoy_status_t set_preferred_network(envoy_network_t network) {
return ENVOY_SUCCESS;
}

void record_counter(const char* elements, uint64_t count) {
if (auto e = engine_.lock()) {
e->recordCounter(std::string(elements), count);
}
}

void flush_stats() {
if (auto e = engine_.lock()) {
e->flushStats();
Expand Down
5 changes: 5 additions & 0 deletions library/common/main_interface.h
Expand Up @@ -96,6 +96,11 @@ envoy_engine_t init_engine();
*/
envoy_status_t set_preferred_network(envoy_network_t network);

/**
* Increment a counter with the given elements and by the given count.
*/
void record_counter(const char* elements, uint64_t count);

/**
* Flush the stats sinks outside of a flushing interval.
* Note: flushing before the engine has started will result in a no-op.
Expand Down
Expand Up @@ -35,4 +35,9 @@ public int runWithConfig(EnvoyConfiguration envoyConfiguration, String logLevel)
// application.registerActivityLifecycleCallbacks(monitor);
return envoyEngine.runWithConfig(envoyConfiguration, logLevel);
}

@Override
public void recordCounter(String elements, int count) {
envoyEngine.recordCounter(elements, count);
}
}
Expand Up @@ -28,4 +28,12 @@ public interface EnvoyEngine {
* @return A status indicating if the action was successful.
*/
int runWithConfig(EnvoyConfiguration envoyConfiguration, String logLevel);

/**
* Increments a counter with the given count.
*
* @param elements Elements of the counter stat.
* @param count Amount to add to the counter.
*/
void recordCounter(String elements, int count);
}
Expand Up @@ -56,4 +56,15 @@ public int runWithConfig(String configurationYAML, String logLevel) {
public int runWithConfig(EnvoyConfiguration envoyConfiguration, String logLevel) {
return runWithConfig(envoyConfiguration.resolveTemplate(JniLibrary.templateString()), logLevel);
}

/**
* Increment a counter with the given count.
*
* @param elements Elements of the counter stat.
* @param count Amount to add to the counter.
*/
@Override
public void recordCounter(String elements, int count) {
JniLibrary.recordCounter(elements, count);
}
}
Expand Up @@ -139,4 +139,9 @@ private static class JavaLoader {
* configurations.
*/
public static native String templateString();

/**
* Increment a counter.
*/
protected static native void recordCounter(String elements, int count);
}
Expand Up @@ -3,10 +3,10 @@ package io.envoyproxy.envoymobile
import android.app.Application
import io.envoyproxy.envoymobile.engine.AndroidEngineImpl

class AndroidStreamClientBuilder @JvmOverloads constructor (
class AndroidEngineBuilder @JvmOverloads constructor(
application: Application,
baseConfiguration: BaseConfiguration = Standard()
) : StreamClientBuilder(baseConfiguration) {
) : EngineBuilder(baseConfiguration) {
init {
addEngineType { AndroidEngineImpl(application) }
}
Expand Down
13 changes: 9 additions & 4 deletions library/kotlin/src/io/envoyproxy/envoymobile/BUILD
Expand Up @@ -20,7 +20,7 @@ android_artifacts(
kt_android_library(
name = "envoy_lib",
srcs = [
"AndroidStreamClientBuilder.kt",
"AndroidEngineBuilder.kt",
],
custom_package = "io.envoyproxy.envoymobile",
manifest = "EnvoyManifest.xml",
Expand All @@ -36,7 +36,9 @@ kt_android_library(
envoy_mobile_kt_library(
name = "envoy_interfaces_lib",
srcs = glob([
"EnvoyClient.kt",
"Engine.kt",
"EngineBuilder.kt",
"EngineImpl.kt",
"EnvoyError.kt",
"Headers.kt",
"HeadersBuilder.kt",
Expand All @@ -52,15 +54,18 @@ envoy_mobile_kt_library(
"ResponseTrailersBuilder.kt",
"RetryPolicy.kt",
"RetryPolicyMapper.kt",
"StatsClient.kt",
"StatsClientImpl.kt",
"Stream.kt",
"StreamClient.kt",
"StreamClientBuilder.kt",
"StreamCallbacks.kt",
"StreamClient.kt",
"StreamClientImpl.kt",
"StreamPrototype.kt",
"UpstreamHttpProtocol.kt",
"filters/*.kt",
"grpc/*.kt",
"mocks/*.kt",
"stats/*.kt",
]),
visibility = ["//visibility:public"],
deps = [
Expand Down
18 changes: 18 additions & 0 deletions library/kotlin/src/io/envoyproxy/envoymobile/Engine.kt
@@ -0,0 +1,18 @@
package io.envoyproxy.envoymobile

/**
* Engine represents a running instance of Envoy Mobile, and provides client interfaces that run on
* that instance.
*/
interface Engine {

/**
* @return a {@link StreamClient} for opening and managing HTTP streams.
*/
fun streamClient(): StreamClient

/**
* @return a {@link StatsClient} for recording time series metrics.
*/
fun statsClient(): StatsClient
}

0 comments on commit b9a4334

Please sign in to comment.