{"id":15334620,"url":"https://github.com/chonton/apm-client","last_synced_at":"2025-02-26T19:30:40.278Z","repository":{"id":57745041,"uuid":"77809403","full_name":"chonton/apm-client","owner":"chonton","description":"Java client for DataDog APM","archived":true,"fork":false,"pushed_at":"2024-10-16T07:03:29.000Z","size":1210,"stargazers_count":12,"open_issues_count":7,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-08T03:34:22.791Z","etag":null,"topics":["datadog-apm","distributed","distributed-trace","java","trace","up-for-adoption"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chonton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-01-02T03:50:51.000Z","updated_at":"2024-12-23T17:04:28.000Z","dependencies_parsed_at":"2023-12-07T17:44:00.882Z","dependency_job_id":"226ce4ee-2b69-464d-b5aa-4be5e36f965a","html_url":"https://github.com/chonton/apm-client","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chonton%2Fapm-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chonton%2Fapm-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chonton%2Fapm-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chonton%2Fapm-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chonton","download_url":"https://codeload.github.com/chonton/apm-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240677080,"owners_count":19839622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["datadog-apm","distributed","distributed-trace","java","trace","up-for-adoption"],"created_at":"2024-10-01T10:08:12.385Z","updated_at":"2025-02-26T19:30:39.906Z","avatar_url":"https://github.com/chonton.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UP FOR ADOPTION\n\n# apm-client\nThis java client intercepts servlet requests, jax-rs client requests, and bean methods.  The resource\nand method names as well as the wall time and duration of the request are recorded in spans.  These\nspans are queued and sent as REST messages to a [Datadog APM collector](https://www.datadoghq.com/apm/).\n\n[Javadoc](https://chonton.github.io/apm-client/0.0.8/client/apidocs/index.html) and [build reports](https://chonton.github.io/apm-client/0.0.8/client/project-reports.html) are available.\n\n### Requirements\n* Minimal latency in the mainline processing\n* Some, but not extreme, buffering of outgoing messages\n* Thread-safe sender\n* Lack of APM collector will be logged, but not cause failure of mainline processing\n\n### Assumptions\n* Minimum of Java 8\n* Local (on the same host) APM collector\n* [CDI](http://www.cdi-spec.org/) implementation such as [Weld](http://weld.cdi-spec.org/)\n* [Slf4J](https://www.slf4j.org/) compliant logging implementation such as [Logback](http://logback.qos.ch/)\n* [Jax-Rs](https://jax-rs-spec.java.net/) client for optional support of exporting traces\n\n## Maven Coordinates\nTo include apm-client in your maven build, use the following fragment in your pom.\n```xml\n  \u003cbuild\u003e\n    \u003cplugins\u003e\n      \u003cplugin\u003e\n        \u003cgroupId\u003eorg.honton.chas.datadog.apm\u003c/groupId\u003e\n        \u003cartifactId\u003eclient\u003c/artifactId\u003e\n        \u003cversion\u003e0.0.8\u003c/version\u003e\n      \u003c/plugin\u003e\n    \u003c/plugins\u003e\n  \u003c/build\u003e\n```\n\n## Configuration\nTo configure apm-client, you must supply a CDI factory method which produces a TraceConfiguration\ninstance.  Three attributes are configured:\n* The service name reported with each span sent to Datadog APM collector.\n* The URL of the local Datadog APM collector.  If null or empty, this will prevent sending traces.\n* The number of milliseconds to backoff.\n\nAfter any communication failure, the apm-client logs the failure and will not further attempt to \nsend span information for the backoff period.  During this period all spans are dropped.\n\n```java\npublic class TraceConfigurationFactory {\n  \n  /**\n   * Get the configuration.\n   * @return The configuration\n   */\n  @Produces\n  static TraceConfiguration getDefault() {\n    return new TraceConfiguration(\n      \"service-name\",       // service name\n      \"http://localhost:8126\",  // apm collector url\n      TimeUnit.MINUTES.toMillis(1));  // backoff period\n  }\n}\n```\n\n## Servlet or Container Filter\nOn the server side, you can either use TraceServletFilter or TraceContainerFilter to trace incoming requests.\nTraceServletFilter can trace any servlet request and annotates the trace with the incoming URI.\nTraceContainerFilter can trace any jax-rs request and annotates the trace with the serving class and method.\n\n## TraceServletFilter\nThe TraceServletFilter traces every incoming request.  The http request host/port is\nreported as the trace resource and the http request method and url are reported as the trace name. \nIf the client request includes the\n**x-ddtrace-parent_trace_id** and **x-ddtrace-parent_span_id** headers, that indicated span is used as\nthe parent trace and span.  Otherwise, a new trace is created.  Once the request is complete, the\nnew span or trace is closed and sent to Datadog APM.\n\n### Registration in War\nRegister TraceServletFilter, weld, and Jax-Rs application in the web.xml:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"ISO-8859-1\"?\u003e\n\u003cweb-app xmlns=\"http://xmlns.jcp.org/xml/ns/javaee\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd\"\n  version=\"3.1\"\u003e\n  \n  \u003c!-- servlet filter --\u003e\n  \u003cfilter\u003e\n    \u003cfilter-class\u003eorg.honton.chas.datadog.apm.servlet.TraceServletFilter\u003c/filter-class\u003e\n  \u003c/filter\u003e\n  \n  \u003c!-- CDI implementation --\u003e\n  \u003clistener\u003e\n    \u003clistener-class\u003eorg.jboss.weld.environment.servlet.Listener\u003c/listener-class\u003e\n  \u003c/listener\u003e\n  \n  \u003c!-- Application --\u003e\n  \u003cservlet\u003e\n    \u003cservlet-class\u003eorg.glassfish.jersey.servlet.ServletContainer\u003c/servlet-class\u003e\n    \u003cinit-param\u003e\n      \u003cparam-name\u003ejavax.ws.rs.Application\u003c/param-name\u003e\n      \u003cparam-value\u003eorg.honton.chas.datadog.apm.example.server.HelloApplication\u003c/param-value\u003e\n    \u003c/init-param\u003e\n  \u003c/servlet\u003e\n  \n\u003c/web-app\u003e\n```\n\n### or Registration with embedded Jetty\n```java\n  public HelloMain(int port) {\n    this.port = port;\n  \n    context = new ServletContextHandler();\n    context.setContextPath(\"/\");\n  \n    // Use Weld to inject into servlets\n    context.addEventListener(new org.jboss.weld.environment.servlet.Listener());\n  \n    // Add the Tracing Filter\n    context.addFilter(TraceServletFilter.class, \"/*\", EnumSet.of(DispatcherType.REQUEST));\n  \n    jettyServer = new Server(port);\n    jettyServer.setHandler(context);\n  \n    addJaxRsApplication(HelloApplication.class);\n    start();\n  }\n```\n\n## TraceContainerFilter\nThe TraceContainerFilter traces every incoming jax-rs request.  The implementation class and method are\nreported as the trace resource and name.  If the client request includes the\n**x-ddtrace-parent_trace_id** and **x-ddtrace-parent_span_id** headers, that indicated span is used as\nthe parent trace and span.  Otherwise, a new trace is created.  Once the request is complete, the\nnew span or trace is closed and sent to Datadog APM.\n\n### Registration\nThe TraceContainerFilter must be registered with the jax-rs runtime.  This can be done during startup as\npart of the Application class.\n```java\n@ApplicationPath(\"/\")\npublic class HellloApplication extends Application {\n\n  @Override\n  public Set\u003cClass\u003c?\u003e\u003e getClasses() {\n    Set\u003cClass\u003c?\u003e\u003e classes = new HashSet\u003c\u003e();\n\n    // Register the service endpoint\n    classes.add(HelloService.class);\n\n    // the Tracing filter\n    classes.add(TraceContainerFilter.class);\n\n    return classes;\n  }\n}\n```\n\n## TraceClientFilter\nTraceClientFilter exports outgoing requests.  Prior to sending the request, a new span is created.\nThe outgoing requests includes **x-ddtrace-parent_trace_id** and **x-ddtrace-parent_span_id** headers\nindicating that new span.  Once the request has completed, the span is closed and sent to Datadog APM.\n\n```java\npublic class ClientProxyFactory {\n\n  private ResteasyClientBuilder clientBuilder;\n\n  @Inject\n  void setTraceFilter(TraceClientFilter filter) {\n    clientBuilder = new ResteasyClientBuilder();\n    clientBuilder.register(filter);\n  }\n\n  public \u003cT\u003e T getProxy(String url, Class\u003cT\u003e cls) {\n    return clientBuilder.build().target(url).proxy(cls);\n  }\n}\n```\n\n## TraceInterceptor\nThe TraceOperation annotation instructs TraceInterceptor to create a new span is before entering a\nCDI bean operation and close the span once the operation is complete.  Completed spans are sent to\nDatadog APM.  The TraceOperation annotation can be placed on the class or the method definition.\nPlacing the annotation on a method will cause any invocation from outside the class to be traced. \nPlacing the annotation on a class will cause all methods in that class to be traced, unless the\nmethod is annotated with **@TraceOperation(false)**.\n```java\n@TraceOperation(type=TraceOperation.DB)\npublic class ExampleBean {\n  \n  /**\n   * Trace turned on at class level\n   */\n  public void methodToTrace() {\n    // ...\n  }\n  \n  /**\n   * Trace turned off at method level\n   */\n  @TraceOperation(false)\n  public void dontTrace() {\n    // ...\n  }\n}\n```\n\n## TraceProxyFactory\nOccasionally, you will need to intercept operations on non-cdi instances.  In this case, you can \ncreate a proxy which will create a new span before invoking any interface method and close the span\nonce the invocation is complete.\n```java\npublic class BeanFactory {\n  \n  @Inject\n  TraceProxyFactory proxyFactory;\n  \n  @Produces\n  public NonCdi factoryMethod() {\n    return proxyFactory.createProxy(new NonCdiImpl(), NonCdi.class);\n  }\n}\n```\n\n## Tracer\nSimilarly, application code can use the Tracer create a new span is before entering a Callable or\nRunnable and close the spans once the calls are complete.  Completed spans are immediately\nqueued to send to Datadog APM.  Inject the Tracer to report spans with application code.\n```java\n  @Inject\n  private Tracer tracer;\n  \n  public String someMethod() {\n    return tracer.executeCallable(\"resource\", \"operation\", () -\u003e {  \n      // code to run inside span\n      return \"returnValue\";\n    });\n  }\n```\n\n# Update Notes\n\n\n## 0.0.7 to 0.0.8\nUpdate jackson dependency\n\n## 0.0.6 to 0.0.7\nUpdate jackson dependency\n\n## 0.0.5 to 0.0.6\nThe URL of the local Datadog APM collector can be set to null or empty; this will prevent sending traces.\n\n## 0.0.4 to 0.0.5\nAn incompatible change was made in the encoding of numbers in the trace and span headers.  Prior to\n0.0.5, the numbers were encoded in hexadecimal.  From 0.0.5 onwards the encoding is in decimal.  Additionally,\nprior to version 0.0.5, the parsing of header did not handle invalid numbers.  To upgrade without downtime, \nrebuild and deploy the more dependent services first.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchonton%2Fapm-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchonton%2Fapm-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchonton%2Fapm-client/lists"}