The API Client is a simple tool for interacting with the OverOps public API in Java.
Background
Getting Started
    Installing
    Constructors
    REST Operations
    Generics
Examples
    Get Event Metadata
        With cURL
        With API Client
    Get Event Volume
    Transaction Graphs
    List Views
UDFs
    OverOps Functions UDF library
    Custom UDFs
License
The API Client is divided into two projects: the API Client itself, and a set of utility functions.
The API Client provides methods for GET
, PUT
, POST
, and DELETE
REST operations, as well as plain old Java objects (POJOs) that represent request and result objects for each operation available through the OverOps public API.
Utility functions wrap commonly used API operation sets into a single function. For example, the LabelUtil.createLabelsIfNotExists
method first makes an API call to list all labels for a given environment, then compares that list with a list of new labels, calling the create label API for each label that does not already exist. Several individual API calls are wrapped into a single convenience method.
The OverOps API Client and utility functions make it easy to access data, extend the functionality of OverOps, and integrate OverOps with other platforms without having to manually make and parse HTTP requests.
The API Client and utility functions are both published to the Maven central repository. Simply add one or both to your dependencies to use them in your code.
Maven:
<dependency>
<groupId>com.takipi</groupId>
<artifactId>api-client</artifactId>
<version>2.30.0</version>
</dependency>
<dependency>
<groupId>com.takipi</groupId>
<artifactId>api-client-util</artifactId>
<version>2.30.0</version>
</dependency>
Gradle:
dependencies {
compile (
"com.takipi:api-client:2.30.0",
"com.takipi:api-client-util:2.30.0",
)
}
import com.takipi.api.client.ApiClient;
In order to ensure backwards and forwards compatibility with the API, API Client constructors are purposefully not public. Instead, we use Builders. This enables the underlying implementation to be changed as needed, and additional functionality to be added in the future, without breaking code that depends on the API Client.
// create a new API Client
ApiClient client = RemoteApiClient.newBuilder()
.setHostname("http://localhost:8080") // for SaaS, use https://api.overops.com/
.setApiKey("xxxxx") // find API token in Account Settings
.build();
The API Client makes create, read, update, and delete operations available for endpoints that support these operations. Refer to the API documentation to see which operations are supported for each endpoint.
// create
apiClient.post(request);
// read
apiClient.get(request);
// update
apiClient.put(request);
// delete
apiClient.delete(request);
The API Client leverages generics for ease of use.
Response<T> response = apiClient.get(ApiGetRequest<T> request);
For example, LabelsRequest
yields a LabelsResult
, while EventsSlimVolumeRequest
yields a EventsSlimVolumeResult
.
LabelsRequest labelsRequest = LabelsRequest.newBuilder()
.setServiceId(serviceId)
.build();
Response<LabelsResult> labelsResult = apiClient.get(labelsRequest);
EventsSlimVolumeRequest eventsSlimRequest = EventsSlimVolumeRequest.newBuilder()
.setFrom(from)
.setTo(to)
.setServiceId(serviceId)
.setViewId(viewId)
.setVolumeType(VolumeType.all)
.build();
Response<EventsSlimVolumeResult> eventsSlimResult = apiClient.get(eventsSlimRequest);
Some API calls do not return data, and instead return in an empty response.
CreateLabelRequest createRequest = CreateLabelRequest.newBuilder()
.setServiceId(serviceId)
.setName(name)
.build();
Response<EmptyResult> createResult = apiClient.post(createRequest);
API documentation: Fetch event data
Here we will retrieve details about an event given an event ID. For this request, two parameters are required: environment ID and event ID. The result will contain all event metadata (see EventResult.java).
For this example, we'll use both CURL and the API Client to illustrate how to translate between the two.
API request:
curl -H "x-api-key: xxxxx" --request GET --url https://api.overops.com/api/v1/services/Sxxxxx/events/119
Remember to replace xxxxx with your API key, Sxxxxx with your environment ID, and 119 with your event ID.
API response:
{
"id": "119",
"summary": "SAXParseException in XmlParseService.fireEvent",
"type": "Swallowed Exception",
"first_seen": "2019-01-07T22:34:30.963Z",
"error_location": {
"prettified_name": "XmlParseService.fireEvent",
"class_name": "com.overops.examples.service.XmlParseService",
"method_name": "fireEvent",
"method_desc": "(Z)V",
"method_position": 63
},
"entry_point": {
"prettified_name": "Controller.route",
"class_name": "com.overops.examples.controller.Controller",
"method_name": "route",
"method_desc": "(JLcom/overops/examples/domain/User;)Z"
},
"introduced_by": "v2.1.0",
"labels": [
"Java-Parser.infra",
"Inbox"
],
"similar_event_ids": [
"121",
"123"
],
"error_origin": {
"prettified_name": "SAXParser.parse",
"class_name": "javax.xml.parsers.SAXParser",
"method_name": "parse",
"method_desc": "(Ljava/io/InputStream;Lorg/xml/sax/helpers/DefaultHandler;)V"
},
"name": "SAXParseException",
"message": "XML document structures must start and end within the same entity.",
"is_rethrow": false,
"class_group": "K225",
"call_stack_group": "K226"
}
import com.takipi.api.client.ApiClient;
import com.takipi.api.client.request.event.EventRequest;
import com.takipi.api.client.result.event.EventResult;
import com.takipi.api.core.url.UrlClient.Response;
import com.takipi.api.client.RemoteApiClient;
public class Example {
public static void main(String[] args) {
// construct an event request
EventRequest eventRequest = EventRequest.newBuilder()
.setServiceId("Sxxxxx") // environment ID
.setEventId("119") // event ID
.build();
// construct an API client
ApiClient apiClient = RemoteApiClient.newBuilder()
.setHostname("https://api.overops.com")
.setApiKey("xxxxx")
.build();
// GET event data
Response<EventResult> eventResponse = apiClient.get(eventRequest);
// check for a bad API response (HTTP status code >= 300)
if (eventResponse.isBadResponse())
throw new IllegalStateException("Failed getting events.");
// EventResult is a POJO representation of the JSON object above
EventResult eventResult = eventResponse.data;
System.out.println("ID: " + eventResult.id);
System.out.println("Introduced by: " + eventResult.introduced_by);
System.out.println("Name: " + eventResult.name);
System.out.println("Message: " + eventResult.message);
}
}
Output:
ID: 119
Introduced by: v2.1.0
Name: SAXParseException
Message: XML document structures must start and end within the same entity.
API documentation: Fetch events details
Here we will retrieve a list of events and event details for a given time frame and view.
For this request, four parameters are required: environment ID, view ID, from, and to. The result will contain a list of events (see EventsVolumeResult.java).
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import com.takipi.api.client.ApiClient;
import com.takipi.api.client.data.view.SummarizedView;
import com.takipi.api.client.request.event.EventsVolumeRequest;
import com.takipi.api.client.result.event.EventsVolumeResult;
import com.takipi.api.client.util.validation.ValidationUtil.VolumeType;
import com.takipi.api.client.util.view.ViewUtil;
import com.takipi.api.core.url.UrlClient.Response;
public class Example {
public static void main(String[] args) {
String serviceId = "Sxxxxx";
ApiClient apiClient = RemoteApiClient.newBuilder()
.setHostname("https://api.overops.com")
.setApiKey("xxxxx")
.build();
// get "All Events" view
SummarizedView view = ViewUtil.getServiceViewByName(apiClient, serviceId, "All Events");
// get events that have occurred in the last 5 minutes
DateTime to = DateTime.now();
DateTime from = to.minusMinutes(5);
// date parameter must be properly formatted
DateTimeFormatter fmt = ISODateTimeFormat.dateTime().withZoneUTC();
// get all events within the date range
EventsVolumeRequest eventsVolumeRequest = EventsVolumeRequest.newBuilder().setServiceId(serviceId)
.setFrom(from.toString(fmt)).setTo(to.toString(fmt)).setViewId(view.id).setVolumeType(VolumeType.all)
.build();
// GET event data
Response<EventsVolumeResult> eventsVolumeResponse = apiClient.get(eventsVolumeRequest);
// check for a bad API response (HTTP status code >= 300)
if (eventsVolumeResponse.isBadResponse())
throw new IllegalStateException("Failed getting events.");
EventsVolumeResult eventsVolumeResult = eventsVolumeResponse.data;
System.out.println("Found " + eventsVolumeResult.events.size() + " events");
System.out.println(eventsVolumeResult.events);
}
}
Output:
Found 9 events
[Logged Warning in LoggedWarnService.fireEvent(114), ExampleSwallowedException in CatchAndIgnoreService.fireEvent(113), Logged Error in LoggedErrorService.fireEvent(112), ExampleCaughtException in CatchAndProcessService.fireEvent(111), Logged Error in XmlParseService.fireEvent(124), SAXParseException in XmlParseService.fireEvent(123), HTTP Error: 500 in RestEndpoint.throwError(117), Custom OverOps Event in CustomEventService.fireEvent(116), ExampleUncaughtException in UncaughtExceptionService.lambda$fireEvent$1(115)]
API documentation: Fetch event metrics split by entrypoint
Here we will retrieve detailed statistics for events in a given view and time frame.
For this request, five parameters are required: environment ID, view ID, from, to, and number of points.
Rather than using TransactionsGraphRequest
directly, we'll use TransactionUtil.getTransactionGraphs
, which returns Map<String, TransactionGraph>
. TransactionGraph
contains a list of GraphPoint
, each of which contains a timestamp and Stats
.
import java.util.Map;
import java.util.Set;
import org.joda.time.DateTime;
import com.takipi.api.client.ApiClient;
import com.takipi.api.client.data.transaction.Stats;
import com.takipi.api.client.data.transaction.TransactionGraph;
import com.takipi.api.client.data.transaction.TransactionGraph.GraphPoint;
import com.takipi.api.client.data.view.SummarizedView;
import com.takipi.api.client.util.transaction.TransactionUtil;
import com.takipi.api.client.util.view.ViewUtil;
public class Example {
public static void main(String[] args) {
String serviceId = "Sxxxxx";
ApiClient apiClient = ApiClient.newBuilder()
.setHostname("https://api.overops.com")
.setApiKey("xxxxx")
.build();
// get "All Events" view
SummarizedView view = ViewUtil.getServiceViewByName(apiClient, serviceId, "All Events");
// get events that have occurred in the last 5 minutes
DateTime to = DateTime.now();
DateTime from = to.minusHours(5);
// get transaction graphs with TransactionUtil
Map<String, TransactionGraph> transactionGraphs = TransactionUtil.getTransactionGraphs(apiClient, serviceId, view.id, from, to, 10);
Set<String> keySet = transactionGraphs.keySet();
// for illustration, print a subset of the data
for(String k : keySet) {
TransactionGraph graph = transactionGraphs.get(k);
System.out.println("class name: " + graph.class_name);
System.out.println("method name: " + graph.method_name);
System.out.println();
GraphPoint point = graph.points.get(0);
System.out.println("point 0 timestamp: " + point.time);
Stats stats = point.stats;
System.out.println("point 0 total time: " + stats.total_time);
System.out.println("point 0 average time: " + stats.avg_time);
System.out.println("point 0 average time standard deviation: " + stats.avg_time_std_deviation);
System.out.println("point 0 invocations: " + stats.invocations);
System.out.println();
}
}
}
Output:
class name: com/overops/examples/service/UncaughtExceptionService
method name: lambda$fireEvent$1
point 0 timestamp: 2019-01-09T16:40:00.000Z
point 0 total time: 12.42
point 0 average time: 1.12
point 0 average time standard deviation: 0.0
point 0 invocations: 11
class name: com/overops/examples/controller/Controller
method name: route
point 0 timestamp: 2019-01-09T16:40:00.000Z
point 0 total time: 66202.32
point 0 average time: 141.15
point 0 avgerage time standard deviation: 0.0
point 0 invocations: 469
API documentation: List views
Here we will retrieve a list of all views for a given environment.
For this request, one parameter is required: environment ID. The result will contain a list of SummarizedView
.
import com.takipi.api.client.ApiClient;
import com.takipi.api.client.data.view.SummarizedView;
import com.takipi.api.client.request.view.ViewsRequest;
import com.takipi.api.client.result.view.ViewsResult;
import com.takipi.api.core.url.UrlClient.Response;
public class Example {
public static void main(String[] args) {
String serviceId = "Sxxxxx";
ApiClient apiClient = ApiClient.newBuilder()
.setHostname("https://api.overops.com")
.setApiKey("xxxxx")
.build();
// get all views
ViewsRequest viewsRequest = ViewsRequest.newBuilder().setServiceId(serviceId).build();
// GET view data
Response<ViewsResult> viewsResponse = apiClient.get(viewsRequest);
// check for a bad API response (HTTP status code >= 300)
if (viewsResponse.isBadResponse())
throw new IllegalStateException("Failed getting views.");
ViewsResult viewsResult = viewsResponse.data;
// print all views
for(SummarizedView view : viewsResult.views) {
System.out.println(view.name + "(" + view.id + ")");
}
}
}
Output:
Hibernate(P7156)
Tier Routing(P7092)
Network Errors(P292)
New This Week(P286)
Tiers Routing(P7153)
Logged Warnings(P289)
Log4j(P7155)
Framework Metrics(P294)
My Timers(P5438)
New Today(P285)
Custom Event(P9075)
HTTP Errors(P6344)
DB Errors(P291)
Apache-http(P9283)
Deployment Routing(P7227)
ForceSnapshot(P9287)
EventGenerator(P9018)
Uncaught Exceptions(P290)
Last Hour(P284)
New in v2.1.3(P9291)
Logged Errors(P288)
Resolved Events(P6479)
Java-Parser(P9284)
App Routing(P7226)
All Exceptions(P6230)
Hidden Events(P6478)
Resurfaced(P6480)
Last Day(P283)
Java-lang(P7154)
All Events(P293)
JVM Errors(P287)
Swallowed Exceptions(P6139)
User Defined Functions make extensive use of the API Client and utility functions.
Within a UDF, the API Client is available from ContextArgs, which sets hostname and API key from the context.
// from ContextArgs in a UDF
ApiClient apiClient = contextArgs.apiClient();
Explore the OverOps UDF library for more sophisticated examples on how to use the API Client. These functions are available by default for all users in OverOps.
To write your own UDFs leveraging the API Client, fork the User Defined Functions repository.
Copyright (c) OverOps Inc. All rights reserved.
Licensed under the MIT License.