Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stats: introduce stats client #992

Merged
merged 33 commits into from Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c7d99af
wip
goaway Jul 23, 2020
8c3c0cd
update
Jul 23, 2020
b804494
update interfaces and implementations
Jul 30, 2020
fab1505
more files
Jul 30, 2020
0213f89
update interface
Jul 30, 2020
b54bd6a
comments
Jul 31, 2020
09c6cc1
wip updating Swift interfaces
goaway Aug 1, 2020
4ac676d
cleanup
goaway Aug 1, 2020
4801a0b
add strong type of element for android
Aug 3, 2020
7f71fe5
doc for Element
Aug 3, 2020
843175a
format
goaway Aug 4, 2020
7d363fb
add Swift Element
goaway Aug 4, 2020
3ded59b
edit after rebase, forqak android
Aug 5, 2020
2d8aa5a
fix ci (mostly) for android
Aug 6, 2020
356390c
fix up swift Element
goaway Aug 6, 2020
8ab5ad2
formatting/lint
goaway Aug 6, 2020
de28910
fix iOS example apps
goaway Aug 6, 2020
352fd28
fix test
goaway Aug 6, 2020
d977737
comments
Aug 7, 2020
ec8f26a
Merge branch 'main' into jh-ms/stats-poc
Aug 8, 2020
bad8422
examples
Aug 8, 2020
a8d0bbb
lint
Aug 8, 2020
e75dbeb
add Swift tests for StatsClientImpl
goaway Aug 10, 2020
3962e96
swiftlint fixes
goaway Aug 10, 2020
827d693
fix swift example app
goaway Aug 10, 2020
0a216a5
update and align docstrings and comments
goaway Aug 10, 2020
e89dfa0
rename stat directory to stats
goaway Aug 10, 2020
9fdaaca
don't override CustomStringConvertible
goaway Aug 10, 2020
d3b575b
couple more updates for comments
goaway Aug 10, 2020
b623969
make clients be properties on swift Engine
goaway Aug 10, 2020
6ae7be3
add experimental callout
goaway Aug 10, 2020
7fc3ec0
comments
Aug 11, 2020
c089d69
andr
Aug 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.*'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer having a more restrictive rule so that people have to think before their application level stats get emitted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what rules do you have in mind?
right now all the elements used for the stats are checked against the regex: ^[A-Za-z_]+\$

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked with @junr03, long-term it might make sense to make people specify this in configuration, so that some thought goes into the stats they emit. For now, as a new, experimental interface, this is probably good enough to move forward.

- 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we cover this in tests?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a ticket to cover testing generally at this layer, will link here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@goaway don't forget to add this ticket

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created (but it's internal).

if (server_) {
server_->dispatcher().post([this, elements, count]() -> void {
static const std::string client = "client";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No non-pod statics (static init fiasco). Moreover what you really want here since this string is known at compile-time is to save a StatName for "client" using either a StatNamePool or StatNameManagedStorage. You want to do that in a context object that is created once at process startup and then re-used. Then you have no thread contention issues.

absl::string_view prefix{client};
goaway marked this conversation as resolved.
Show resolved Hide resolved
absl::string_view dynamic_elements{elements};
Stats::Utility::counterFromElements(server_->serverFactoryContext().scope(),
goaway marked this conversation as resolved.
Show resolved Hide resolved
{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);
goaway marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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",
Comment on lines +57 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these be in stats/ which is captured below with globbing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these ideally live here, basically in the same level as StreamClient, as the interface Engine.kt exposes a StatsClient and a StreamClient.

"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
}