+ * Custom error call constants, specific for
+ */
+public enum ErrorType {
+ /**
+ * Any thrift errors (protocol, transport, etc).
+ */
+ PROVIDER_ERROR,
+
+ /**
+ * Error which is registered in service method declaration.
+ */
+ APPLICATION_KNOWN_ERROR,
+
+ /**
+ * Any other error which is not registered for calling method and doesn't refer to thrift errors.
+ */
+ APPLICATION_UNKNOWN_ERROR,
+
+ /**
+ * Any other error
+ */
+ OTHER
+
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/event/Event.java b/woody-api/src/main/java/com/rbkmoney/woody/api/event/Event.java
new file mode 100644
index 0000000..c9da41b
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/event/Event.java
@@ -0,0 +1,81 @@
+package com.rbkmoney.woody.api.event;
+
+import com.rbkmoney.woody.api.trace.*;
+
+/**
+ * Created by vpankrashkin on 06.05.16.
+ */
+public abstract class Event {
+ private final TraceData traceData;
+
+ public Event(TraceData traceData) {
+ this.traceData = traceData;
+ }
+
+ public TraceData getTraceData() {
+ return traceData;
+ }
+
+ public Object getEventType() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.EVENT_TYPE);
+ }
+
+ public CallType getCallType() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.CALL_TYPE);
+ }
+
+ public String getCallName() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.CALL_NAME);
+ }
+
+ public Object[] getCallArguments() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.CALL_ARGUMENTS);
+ }
+
+ public Object getCallResult() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.CALL_RESULT);
+ }
+
+ public ErrorType getErrorType() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.ERROR_TYPE);
+ }
+
+ public String getErrorName() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.ERROR_NAME);
+ }
+
+ public String getErrorMessage() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.ERROR_MESSAGE);
+ }
+
+ public String getSpanId() {
+ return getActiveSpan().getSpan().getId();
+ }
+
+ public String getParentId() {
+ return getActiveSpan().getSpan().getParentId();
+ }
+
+ public String getTraceId() {
+ return getActiveSpan().getSpan().getTraceId();
+ }
+
+ public long getTimeStamp() {
+ return getActiveSpan().getSpan().getTimestamp();
+ }
+
+ public long getDuration() {
+ return getActiveSpan().getSpan().getDuration();
+ }
+
+ public Endpoint getEndpoint() {
+ return getActiveSpan().getMetadata().getValue(MetadataProperties.CALL_ENDPOINT);
+ }
+
+ public boolean isSuccessfullCall() {
+ return !ContextUtils.hasCallErrors(getActiveSpan());
+
+ }
+
+ public abstract ContextSpan getActiveSpan();
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/event/EventListener.java b/woody-api/src/main/java/com/rbkmoney/woody/api/event/EventListener.java
new file mode 100644
index 0000000..68fb91a
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/event/EventListener.java
@@ -0,0 +1,8 @@
+package com.rbkmoney.woody.api.event;
+
+/**
+ * Created by vpankrashkin on 25.04.16.
+ */
+public interface EventListener
+ * S | C
+ * -----
+ * 0 | 0
+ * 0 | 1
+ * 1 | 0
+ * 1 | 1
+ *
+ * 0,0 and 0,1 combinations don't have server span and context in the state can't be server by default (no server span is set) - it's client state -> true
+ * 1,0 means that server span is created and no client span exists - it's server state -> false
+ * 1,1 means that both spans exist and child client call is active now because for any client request client span is cleared after call completion, so after child call state returns to (1,0) case - (1,1) is child client state -> true
+ *
+ * This allows to eliminate the necessity for call processing code to be explicitly configured with expected call state. This can be figured out directly from the context in runtime.
+ * The only exclusion is {@link com.rbkmoney.woody.api.trace.context.TraceContext} itself. It uses already filled trace id field for server state initiazation
+ *
+ * @return true - if call is running as root client or child client call for server request handling; false - if call is running in server request handing
+ */
+ public boolean isClient() {
+ return serviceSpan.isFilled() ? clientSpan.isFilled() : true;
+ }
+
+ public ContextSpan getActiveSpan() {
+ return isClient() ? clientSpan : serviceSpan;
+ }
+
+ public ContextSpan getSpan(boolean isClient) {
+ return isClient ? clientSpan : serviceSpan;
+ }
+
+ public void reset() {
+ clientSpan.reset();
+ serviceSpan.reset();
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/CompositeTracer.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/CompositeTracer.java
new file mode 100644
index 0000000..af9ff37
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/CompositeTracer.java
@@ -0,0 +1,43 @@
+package com.rbkmoney.woody.api.trace.context;
+
+import com.rbkmoney.woody.api.proxy.InstanceMethodCaller;
+import com.rbkmoney.woody.api.proxy.MethodCallTracer;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Created by vpankrashkin on 04.05.16.
+ */
+public class CompositeTracer implements MethodCallTracer {
+ private final MethodCallTracer[] tracers;
+
+ public CompositeTracer(MethodCallTracer... callTracers) {
+ this(Arrays.asList(callTracers));
+ }
+
+ public CompositeTracer(Collection extends MethodCallTracer> tracers) {
+ this.tracers = tracers.stream().toArray(MethodCallTracer[]::new);
+ }
+
+ @Override
+ public void beforeCall(Object[] args, InstanceMethodCaller caller) {
+ for (int i = 0; i < tracers.length; ++i) {
+ tracers[i].beforeCall(args, caller);
+ }
+ }
+
+ @Override
+ public void afterCall(Object[] args, InstanceMethodCaller caller, Object result) {
+ for (int i = 0; i < tracers.length; ++i) {
+ tracers[i].afterCall(args, caller, result);
+ }
+ }
+
+ @Override
+ public void callError(Object[] args, InstanceMethodCaller caller, Throwable error) {
+ for (int i = 0; i < tracers.length; ++i) {
+ tracers[i].callError(args, caller, error);
+ }
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/ContextTracer.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/ContextTracer.java
new file mode 100644
index 0000000..ff67da8
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/ContextTracer.java
@@ -0,0 +1,43 @@
+package com.rbkmoney.woody.api.trace.context;
+
+import com.rbkmoney.woody.api.proxy.InstanceMethodCaller;
+import com.rbkmoney.woody.api.proxy.MethodCallTracer;
+
+/**
+ * Created by vpankrashkin on 26.04.16.
+ *
+ * Used to control context lifecycle on interface method call and return
+ */
+public class ContextTracer implements MethodCallTracer {
+ private final TraceContext traceContext;
+ private final MethodCallTracer targetTracer;
+
+ public ContextTracer(TraceContext traceContext, MethodCallTracer targetTracer) {
+ this.traceContext = traceContext;
+ this.targetTracer = targetTracer;
+ }
+
+ @Override
+ public void beforeCall(Object[] args, InstanceMethodCaller caller) {
+ traceContext.init();
+ targetTracer.beforeCall(args, caller);
+ }
+
+ @Override
+ public void afterCall(Object[] args, InstanceMethodCaller caller, Object result) {
+ try {
+ targetTracer.afterCall(args, caller, result);
+ } finally {
+ traceContext.destroy();
+ }
+ }
+
+ @Override
+ public void callError(Object[] args, InstanceMethodCaller caller, Throwable error) {
+ try {
+ targetTracer.callError(args, caller, error);
+ } finally {
+ traceContext.destroy(true);
+ }
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EmptyTracer.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EmptyTracer.java
new file mode 100644
index 0000000..38aa2a4
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EmptyTracer.java
@@ -0,0 +1,24 @@
+package com.rbkmoney.woody.api.trace.context;
+
+import com.rbkmoney.woody.api.proxy.InstanceMethodCaller;
+import com.rbkmoney.woody.api.proxy.MethodCallTracer;
+
+/**
+ * Created by vpankrashkin on 04.05.16.
+ */
+public class EmptyTracer implements MethodCallTracer {
+ @Override
+ public void beforeCall(Object[] args, InstanceMethodCaller caller) {
+
+ }
+
+ @Override
+ public void afterCall(Object[] args, InstanceMethodCaller caller, Object result) {
+
+ }
+
+ @Override
+ public void callError(Object[] args, InstanceMethodCaller caller, Throwable error) {
+
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EventTracer.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EventTracer.java
new file mode 100644
index 0000000..261be1c
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/EventTracer.java
@@ -0,0 +1,43 @@
+package com.rbkmoney.woody.api.trace.context;
+
+
+import com.rbkmoney.woody.api.proxy.InstanceMethodCaller;
+import com.rbkmoney.woody.api.proxy.MethodCallTracer;
+
+/**
+ * Created by vpankrashkin on 25.04.16.
+ */
+public class EventTracer implements MethodCallTracer {
+
+ private final Runnable beforeCallListener;
+ private final Runnable afterCallListener;
+ private final Runnable errListener;
+
+ public EventTracer() {
+ this(null, null, null);
+ }
+
+ public EventTracer(Runnable beforeCallListener, Runnable afterCallListener, Runnable errListener) {
+ this.beforeCallListener = beforeCallListener != null ? beforeCallListener : () -> {
+ };
+ this.afterCallListener = afterCallListener != null ? afterCallListener : () -> {
+ };
+ this.errListener = errListener != null ? errListener : () -> {
+ };
+ }
+
+ @Override
+ public void beforeCall(Object[] args, InstanceMethodCaller caller) {
+ beforeCallListener.run();
+ }
+
+ @Override
+ public void afterCall(Object[] args, InstanceMethodCaller caller, Object result) {
+ afterCallListener.run();
+ }
+
+ @Override
+ public void callError(Object[] args, InstanceMethodCaller caller, Throwable error) {
+ errListener.run();
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/MetadataTracer.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/MetadataTracer.java
new file mode 100644
index 0000000..25d1db3
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/MetadataTracer.java
@@ -0,0 +1,88 @@
+package com.rbkmoney.woody.api.trace.context;
+
+import com.rbkmoney.woody.api.event.ClientEventType;
+import com.rbkmoney.woody.api.event.ServiceEventType;
+import com.rbkmoney.woody.api.proxy.InstanceMethodCaller;
+import com.rbkmoney.woody.api.proxy.MethodCallTracer;
+import com.rbkmoney.woody.api.trace.ContextSpan;
+import com.rbkmoney.woody.api.trace.ContextUtils;
+import com.rbkmoney.woody.api.trace.Metadata;
+import com.rbkmoney.woody.api.trace.MetadataProperties;
+
+/**
+ * Created by vpankrashkin on 25.04.16.
+ */
+public class MetadataTracer implements MethodCallTracer {
+ private final boolean isClient;
+ private final boolean isAuto;
+
+ public static MetadataTracer forClient() {
+ return new MetadataTracer(true);
+ }
+
+ public static MetadataTracer forServer() {
+ return new MetadataTracer(false);
+ }
+
+ public static MetadataTracer forAuto() {
+ return new MetadataTracer();
+ }
+
+ public MetadataTracer() {
+ this.isAuto = true;
+ this.isClient = false;
+ }
+
+ public MetadataTracer(boolean isClient) {
+ this.isAuto = false;
+ this.isClient = isClient;
+ }
+
+ @Override
+ public void beforeCall(Object[] args, InstanceMethodCaller caller) {
+ boolean isClient = isClient();
+ setBeforeCall(isClient ?
+ TraceContext.getCurrentTraceData().getClientSpan().getMetadata() :
+ TraceContext.getCurrentTraceData().getServiceSpan().getMetadata(),
+ args, caller, isClient);
+
+ }
+
+ @Override
+ public void afterCall(Object[] args, InstanceMethodCaller caller, Object result) {
+ boolean isClient = isClient();
+ setAfterCall(isClient ?
+ TraceContext.getCurrentTraceData().getClientSpan().getMetadata() :
+ TraceContext.getCurrentTraceData().getServiceSpan().getMetadata(),
+ args, caller, result, isClient);
+ }
+
+ @Override
+ public void callError(Object[] args, InstanceMethodCaller caller, Throwable error) {
+ boolean isClient = isClient();
+ setCallError(isClient ?
+ TraceContext.getCurrentTraceData().getClientSpan() :
+ TraceContext.getCurrentTraceData().getServiceSpan(),
+ args, caller, error, isClient);
+ }
+
+ private void setBeforeCall(Metadata metadata, Object[] args, InstanceMethodCaller caller, boolean isClient) {
+ metadata.putValue(MetadataProperties.CALL_ARGUMENTS, args);
+ metadata.putValue(MetadataProperties.INSTANCE_METHOD_CALLER, caller);
+ metadata.putValue(MetadataProperties.EVENT_TYPE, isClient ? ClientEventType.CALL_SERVICE : ServiceEventType.CALL_HANDLER);
+ }
+
+ private void setAfterCall(Metadata metadata, Object[] args, InstanceMethodCaller caller, Object result, boolean isClient) {
+ metadata.putValue(MetadataProperties.CALL_RESULT, result);
+ metadata.putValue(MetadataProperties.EVENT_TYPE, isClient ? ClientEventType.SERVICE_RESULT : ServiceEventType.HANDLER_RESULT);
+ }
+
+ private void setCallError(ContextSpan contextSpan, Object[] args, InstanceMethodCaller caller, Throwable error, boolean isClient) {
+ ContextUtils.setCallError(contextSpan, error);
+ contextSpan.getMetadata().putValue(MetadataProperties.EVENT_TYPE, isClient ? ClientEventType.ERROR : ServiceEventType.ERROR);
+ }
+
+ private boolean isClient() {
+ return isAuto ? TraceContext.getCurrentTraceData().isClient() : isClient;
+ }
+}
diff --git a/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/TraceContext.java b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/TraceContext.java
new file mode 100644
index 0000000..325a8f2
--- /dev/null
+++ b/woody-api/src/main/java/com/rbkmoney/woody/api/trace/context/TraceContext.java
@@ -0,0 +1,178 @@
+package com.rbkmoney.woody.api.trace.context;
+
+import com.rbkmoney.woody.api.generator.IdGenerator;
+import com.rbkmoney.woody.api.trace.Span;
+import com.rbkmoney.woody.api.trace.TraceData;
+
+import static com.rbkmoney.woody.api.generator.IdGenerator.NO_PARENT_ID;
+
+/**
+ * Created by vpankrashkin on 25.04.16.
+ */
+public class TraceContext {
+ private final static ThreadLocal