Skip to content

Commit 200222e

Browse files
committed
add sample to emit client metrics for prometheus
1 parent d540ddf commit 200222e

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ dependencies {
3838
compile group: 'com.uber.cadence', name: 'cadence-client', version: '2.7.8'
3939
compile group: 'commons-configuration', name: 'commons-configuration', version: '1.9'
4040
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
41+
compile group: 'com.uber.m3', name: 'tally-core', version: '0.10.0'
42+
compile group: 'com.uber.m3', name: 'tally-prometheus', version: '0.10.0'
43+
compile group: 'io.prometheus', name: 'simpleclient', version: '0.10.0'
44+
compile group: 'io.prometheus', name: 'simpleclient_httpserver', version: '0.10.0'
4145
testCompile group: 'junit', name: 'junit', version: '4.12'
4246
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19'
4347
testCompile group: 'org.powermock', name: 'powermock-api-mockito', version: '1.7.3'
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.samples.hello;
19+
20+
import static com.uber.cadence.samples.common.SampleConstants.DOMAIN;
21+
22+
import com.uber.cadence.activity.ActivityMethod;
23+
import com.uber.cadence.client.WorkflowClient;
24+
import com.uber.cadence.worker.Worker;
25+
import com.uber.cadence.worker.WorkerOptions;
26+
import com.uber.cadence.workflow.Workflow;
27+
import com.uber.cadence.workflow.WorkflowMethod;
28+
import com.uber.m3.tally.Buckets;
29+
import com.uber.m3.tally.Capabilities;
30+
import com.uber.m3.tally.Counter;
31+
import com.uber.m3.tally.Gauge;
32+
import com.uber.m3.tally.Histogram;
33+
import com.uber.m3.tally.RootScopeBuilder;
34+
import com.uber.m3.tally.Scope;
35+
import com.uber.m3.tally.ScopeCloseException;
36+
import com.uber.m3.tally.Timer;
37+
import com.uber.m3.tally.prometheus.PrometheusReporter;
38+
import com.uber.m3.util.Duration;
39+
import com.uber.m3.util.ImmutableMap;
40+
import io.prometheus.client.CollectorRegistry;
41+
import io.prometheus.client.exporter.HTTPServer;
42+
import java.io.IOException;
43+
import java.net.InetSocketAddress;
44+
import java.util.Map;
45+
46+
/**
47+
* Hello World Cadence workflow that executes a single activity. Requires a local instance the
48+
* Cadence service to be running.
49+
*/
50+
public class HelloMetric {
51+
52+
static final String TASK_LIST = "HelloActivity";
53+
54+
/** Workflow interface has to have at least one method annotated with @WorkflowMethod. */
55+
public interface GreetingWorkflow {
56+
/** @return greeting string */
57+
@WorkflowMethod(executionStartToCloseTimeoutSeconds = 10, taskList = TASK_LIST)
58+
String getGreeting(String name);
59+
}
60+
61+
/** Activity interface is just a POJI. */
62+
public interface GreetingActivities {
63+
@ActivityMethod(scheduleToCloseTimeoutSeconds = 2)
64+
String composeGreeting(String greeting, String name);
65+
}
66+
67+
/** GreetingWorkflow implementation that calls GreetingsActivities#composeGreeting. */
68+
public static class GreetingWorkflowImpl implements GreetingWorkflow {
69+
70+
/**
71+
* Activity stub implements activity interface and proxies calls to it to Cadence activity
72+
* invocations. Because activities are reentrant, only a single stub can be used for multiple
73+
* activity invocations.
74+
*/
75+
private final GreetingActivities activities =
76+
Workflow.newActivityStub(GreetingActivities.class);
77+
78+
@Override
79+
public String getGreeting(String name) {
80+
// This is a blocking call that returns only after the activity has completed.
81+
return activities.composeGreeting("Hello", name);
82+
}
83+
}
84+
85+
static class GreetingActivitiesImpl implements GreetingActivities {
86+
@Override
87+
public String composeGreeting(String greeting, String name) {
88+
return greeting + " " + name + "!";
89+
}
90+
}
91+
92+
public static void main(String[] args) throws IOException {
93+
// Start a worker that hosts both workflow and activity implementations.
94+
Worker.Factory factory = new Worker.Factory(DOMAIN);
95+
PrometheusReporter.builder().registry(CollectorRegistry.defaultRegistry).build();
96+
final WorkerOptions workerOptions =
97+
new WorkerOptions.Builder().setMetricsScope(createMetricScope()).build();
98+
Worker worker = factory.newWorker(TASK_LIST, workerOptions);
99+
// Workflows are stateful. So you need a type to create instances.
100+
worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);
101+
// Activities are stateless and thread safe. So a shared instance is used.
102+
worker.registerActivitiesImplementations(new GreetingActivitiesImpl());
103+
// Start listening to the workflow and activity task lists.
104+
factory.start();
105+
106+
// Start a workflow execution. Usually this is done from another program.
107+
WorkflowClient workflowClient = WorkflowClient.newInstance(DOMAIN);
108+
// Get a workflow stub using the same task list the worker uses.
109+
GreetingWorkflow workflow = workflowClient.newWorkflowStub(GreetingWorkflow.class);
110+
// Execute a workflow waiting for it to complete.
111+
String greeting = workflow.getGreeting("World");
112+
System.out.println(greeting);
113+
}
114+
115+
private static Scope createMetricScope() throws IOException {
116+
CollectorRegistry registry = CollectorRegistry.defaultRegistry;
117+
HTTPServer httpServer = new HTTPServer(new InetSocketAddress(9098), registry);
118+
PrometheusReporter reporter = PrometheusReporter.builder().registry(registry).build();
119+
Scope scope = new RootScopeBuilder().reporter(reporter).reportEvery(Duration.ofSeconds(1));
120+
return new PrometheusScope(scope);
121+
}
122+
}
123+
124+
125+
/**
126+
* PrometheusScope will replace all "-"(dash) into "_"(underscore) so that it meets the requirement
127+
* in https://prometheus.io/docs/concepts/data_model/
128+
*/
129+
class PrometheusScope implements Scope {
130+
131+
private Scope scope;
132+
133+
PrometheusScope(Scope scope) {
134+
this.scope = scope;
135+
}
136+
137+
private String fixName(String name) {
138+
String newName = name.replace('-', '_');
139+
return newName;
140+
}
141+
142+
private Map<String, String> fixTags(Map<String, String> tags) {
143+
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
144+
tags.forEach((key, value) -> builder.put(fixName(key), fixName(value)));
145+
return builder.build();
146+
}
147+
148+
@Override
149+
public Counter counter(final String name) {
150+
String newName = fixName(name);
151+
return scope.counter(newName);
152+
}
153+
154+
@Override
155+
public Gauge gauge(final String name) {
156+
String newName = fixName(name);
157+
return scope.gauge(newName);
158+
}
159+
160+
@Override
161+
public Timer timer(final String name) {
162+
String newName = fixName(name);
163+
return scope.timer(newName);
164+
}
165+
166+
@Override
167+
public Histogram histogram(final String name, final Buckets buckets) {
168+
String newName = fixName(name);
169+
return scope.histogram(newName, buckets);
170+
}
171+
172+
@Override
173+
public Scope tagged(final Map<String, String> tags) {
174+
return new PrometheusScope(scope.tagged(fixTags(tags)));
175+
}
176+
177+
@Override
178+
public Scope subScope(final String name) {
179+
String newName = fixName(name);
180+
return new PrometheusScope(scope.subScope(newName));
181+
}
182+
183+
@Override
184+
public Capabilities capabilities() {
185+
return scope.capabilities();
186+
}
187+
188+
@Override
189+
public void close() throws ScopeCloseException {
190+
scope.close();
191+
}
192+
}

0 commit comments

Comments
 (0)