All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.apm4all.tracy.Tracy Maven / Gradle / Ivy
/*
* Copyright 2014 Joao Vicente
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.apm4all.tracy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* A Trace utility to capture capture timing application flow meta data
* Tracy is designed to provide a minimalist API for ease of use
* @author [email protected]
*/
public class Tracy {
static final String TRACY_DEFAULT_TASK_ID = "NA";
static final String TRACY_DEFAULT_PARENT_OPT_ID = "NA";
static List EMPTY_STRING_LIST = new ArrayList();
static List EMPTY_TRACY_EVENT_LIST = new ArrayList();
static List> EMPTY_LIST_OF_MAPS = new ArrayList>();
private final static ThreadLocal threadContext = new ThreadLocal ();
/**
* Tracy allows to trace execution flow and later represent it as a Directed Acyclic Graph (DAG)
* A Tracy identifier (taskId) starts at the most outbound endpoint exposed by a system
* The taskId is to be propagated across components, JVMs and hosts
* If the endpoint is outside this JVM/component you should have received a taskId and optId from a client.
* @param taskId is a string which allows correlating Tracy events resulting of a endpoint being hit
* @param parentOptId is a string identifying the parent operation which invoked some logic on a local component
*/
public static void setContext(String taskId, String parentOptId) {
threadContext.set(new TracyThreadContext(taskId, parentOptId));
}
public static void setContext(String taskId, String parentOptId, String componentName) {
threadContext.set(new TracyThreadContext(taskId, parentOptId, componentName));
}
/**
* Attaches existing context to ThreadLocal
*/
protected static void setContext(TracyThreadContext ctx) {
threadContext.set(ctx);
}
/**
* Setting context in this manner is highly discouraged.
* taskId is fundamental to correlate Tracy events.
* optId is fundamental to place the parent node in the Tracy DAG
*/
public static void setContext() {
setContext(TracyThreadContext.generateRandomTaskId(), TracyThreadContext.generateRandomOptId());
}
/**
* Clearing context ensures there is no residual context sticking to a recycled thread.
*/
public static void clearContext() {
threadContext.set(null);
}
/**
* Clearing context ensures there is no residual context sticking to a recycled thread.
* Currently just calls clearContext but may change in future so creating a distinction in the API
*/
public static void clearWorkerContext() {
clearContext();
}
/**
* Call before starting an operation you want to capture elapsed time for.
* You can nest before() calls if you want to trace both caller and callee methods,
* but make sure you call after() for every before().
* @param label is the name you will see in the trace event report of graph node representation or timeline
*/
public static void before(String label) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.push(label);
}
}
/**
* Call after finishing an operation you want to capture elapsed time for.
* @param label is the name you will see in the trace event report of graph node representation or timeline
*/
public static void after(String label) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.pop();
}
}
/**
* before() and after() will capture timing information and hostname in a TracyEvent.
* annotate() allows capturing other information you want to see in TracyEvent (e.g. bytesReceived, bytesSent, etc)
* for the TracyEvent for which you last called before() for
* @param keyValueSequence is the sequence key,value strings you want on the TracyEvent
* (e.g. annotate("bytesSent", "103", "bytesReceived", "15432")
*/
public static void annotate(String... keyValueSequence) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.annotate(keyValueSequence);
}
}
/**
* Convenience annotate() method to save user from converting int to String
*/
public static void annotate(String intName, int intValue) {
annotate(intName, new Integer(intValue).toString());
}
/**
* Convenience annotate() method to save user from converting long to String
*/
public static void annotate(String longName, long longValue) {
annotate(longName, new Long(longValue).toString());
}
/**
* Once all work has been done, and TracyEvents are ready to be collected you can collect them using this method
* @return list of TracyEvents
*/
public static List getEvents() {
TracyThreadContext ctx = threadContext.get();
List events = EMPTY_TRACY_EVENT_LIST;
if (isValidContext(ctx)) {
events = ctx.getPoppedList();
}
return events;
}
/**
* Once all work has been done, and TracyEvents are ready to be collected you can collect them using this method
* This method differs from getEvents() in the sense that the client does not need to know the structure of
* TracyEvents to be able to consume them.
* @return list of TracyEvent maps
*/
public static List> getEventsAsMaps() {
List> list = EMPTY_LIST_OF_MAPS;
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
list = new ArrayList>(20);
for (TracyEvent event : ctx.getPoppedList()) {
list.add(event.toMap());
}
}
return list;
}
/**
* Gets List of Tracy events in JSON format
* @return list of Tracy JSONified events
*/
public static List getEventsAsJson() {
List list = Tracy.EMPTY_STRING_LIST;
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
list = new ArrayList(20);
for (TracyEvent event : ctx.getPoppedList()) {
list.add(event.toJsonString());
}
}
return list;
}
public static String getTaskId() {
TracyThreadContext ctx = threadContext.get();
return ctx.getTaskId();
}
public static final TracyThreadContext getTracyThreadContext() {
return threadContext.get();
}
public static String getParentOptId() {
TracyThreadContext ctx = threadContext.get();
return ctx.getParentOptId();
}
/**
* Allows user to set a custom optId (usually automatically created)
* This method can be safely called any time between a 'before' and 'after'
*
* @param customOptId is the String which will be recorded as Tracy optId. Best practice is to use a 4 character string outside of 32bit hex pattern [0000..1111]
* e.g. setOptId("U001")
*/
public static void setOptId(String customOptId) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.setOptId(customOptId);
}
}
/**
* Creates Tracy worker thread context to be bound to the worker thread
* The context returned contains only parentage information
* Note: This needs to be called from the requester thread
* @return Context to be bound to the worker thread
*/
public static TracyThreadContext createWorkerTheadContext() {
TracyThreadContext currentCtx = threadContext.get();
TracyThreadContext workerCtx = null;
if (isValidContext(currentCtx)) {
workerCtx = new TracyThreadContext(
currentCtx.getTaskId(), currentCtx.getOptId());
}
return workerCtx;
}
/**
* Attaches parentage context created by createWorkerThread()
* Once this context is attached before() after() will create TracyEvents
* within the worker thread.
* @return Context to be bound to the worker thread
*/
public static void setWorkerContext(TracyThreadContext ctx) {
threadContext.set(ctx);
}
/**
* When called from the worker thread, will return the worker TracyThreadContext
* It is the responsibility of the caller to propagate this context using
* TracyableData.setTracyContext()
* Note: This is to be called from the worker thread
* @return worker TracyThreadContext
*/
public static TracyThreadContext getWorkerContext() {
return threadContext.get();
}
/**
* When called from the requester thread, will merge worker TracyThreadContext into the
* requester TracyThreadContext
* @return worker TracyThreadContext
*/
public static void mergeWorkerContext(TracyThreadContext workerTracyThreadContext) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.mergeChildContext(workerTracyThreadContext);
}
}
private static boolean isValidContext(TracyThreadContext ctx) {
return (null != ctx);
}
public static boolean isEnabled() {
TracyThreadContext ctx = threadContext.get();
return(isValidContext(ctx));
}
public static boolean isEnabled(TracyThreadContext ctx) {
return(isValidContext(ctx));
}
/**
* In case where an exception is thrown Tracy.frameError(errorString) can be used to
* unwind all Tracy stack frames with a particular user error message.
* This is useful when one wants to signal an custom error for a task when one does not
* know (or does not care) how deep the the Tracy stack is.
*/
public static void outerError(String error) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.popAllWithError(error);
}
}
/**
* In case where an exception is thrown Tracy.frameError(errorString) can be used to
* close Tracy stack frame with a user error message while still flow to continue.
* This can be useful when in a retry exception where the task may be recoverable.
* If the current frame is not the only Tracy stack frame the next frame will resume normally.
* In case the user wants to raise an error all the way up to the inner Tracy stack frame,
* then outerError(errorString) should be used instead
*/
public static void frameError(String error) {
TracyThreadContext ctx = threadContext.get();
if (isValidContext(ctx)) {
ctx.popFrameWithError(error);
}
}
}