PDFs

On this page:

Related pages:

Your Rating:
Results:
PatheticBadOKGoodOutstanding!
45 rates

Task Flow to Use the SDK

These are the tasks your application needs to perform to get instrumented:

Add the AppDynamics Header File to the Application

After downloading the SDK, you're ready to add AppDynamics instrumentation to your C/C++ application. The first step is to include the AppDynamics header file at the top of your main function file:

#include <path_to_SDK>/sdk_lib/appdynamics.h

Initialize the Controller Configuration

Controller information settings permit the agent to connect to the Controller. Some settings are required for all applications, while others are needed only for certain types of application environments. For example, if the agent needs to connect to the Controller via an internal proxy in your network, you need to set up the connection settings for the proxy.

See Agent Configuration Settings in C/C++ Agent SDK Reference for a complete list of the settings and information about which ones are required.

In your application, assign values to the required settings. For example, to set the Controller connection information:

const char APP_NAME[] = "SampleC";
const char TIER_NAME[] = "SampleCTier1";
const char NODE_NAME[] = "SampleCNode1";
const char CONTROLLER_HOST[] = "controller.somehost.com";
const int CONTROLLER_PORT = 8080;
const char CONTROLLER_ACCOUNT[] = "customer1";
const char CONTROLLER_ACCESS_KEY[] = "MyAccessKey";
const int CONTROLLER_USE_SSL = 0; 

Declare an appd_config struct with the controller settings. For example: 

appd_config* cfg = appd_config_init(); // appd_config_init() resets the configuration object and pass back an handle/pointer
appd_config_set_app_name(cfg, APP_NAME);
appd_config_set_tier_name(cfg, TIER_NAME);
appd_config_set_node_name(cfg, NODE_NAME);
appd_config_set_controller_host(cfg, CONTROLLER_HOST);
appd_config_set_controller_port(cfg, CONTROLLER_PORT);
appd_config_set_controller_account(cfg, CONTROLLER_ACCOUNT);
appd_config_set_controller_access_key(cfg, CONTROLLER_ACCESS_KEY);
appd_config_set_controller_use_ssl(cfg, CONTROLLER_USE_SSL);

Initialize the Agent

In your main function, call these initialization functions:

  1. Initialize the config structure as shown in the previous section.
  2. Next initialize the agent by passing the configuration structure to  appd_sdk_init()

If appd_sdk_init() returns zero, the agent is initialized successfully. Non-zero indicates failure, probably because the agent could not reach the Controller.

The following example illustrates how to initialize the SDK: 

int initRC = appd_sdk_init(&cfg);

if (initRC) {
      std::cerr <<  "Error: sdk init: " << initRC <<;
      return -1;
} 

It can take several minutes after the agent is initailized before all the business transactions and metrics appear in the Controller.

Create Business Transactions

Define a business transaction by enclosing the code that constitutes the request that you want to monitor between appd_bt_begin() and appd_bt_end()calls. appd_bt_begin() returns a handle to use in subsequent routines that affect that business transaction. 

If you are creating a business transaction that correlates with an upstream business transaction, pass the correlation header of the upstream transaction so the new transaction that you are creating can correlate with it. See appd_exitcall_get_correlation_header()in the C/C++ Agent SDK Reference. If the transaction does not need to correlate with another transaction, pass NULL for the correlation header parameter.

You can optionally store the business transaction handle in the global handle registry with a guid for easy retrieval later using appd_bt_store(). Retrieve the handle from the global handle registry using appd_bt_get().

The following shows an example of setting a business transaction:

// start the "Checkout" transaction
appd_bt_handle btHandle = appd_bt_begin("Checkout", NULL);

// Optionally store the handle in the global registry
appd_bt_store(btHandle, my_bt_guid);
... 

// Retrieve a stored handle from the global registry
appd_bt_handle myBtHandle = appd_bt_get(my_bt_guid);
... 

// end the transaction
appd_bt_end(btHandle);

Between starting and ending the transaction, you can perform operations such as adding errors to the business transaction, defining the transaction snapshot attributes, adding backends and exit calls, and so on. 

When the business transaction ends, via a call to appd_bt_end(), the agent stops reporting metrics for the business transaction.

Add Business Transaction Errors

Use appd_bt_add_error() to report business transaction errors. If you set this function's markBTAsError parameter, the transaction is reported as an error transaction when the error occurs.

The SDK provides an enum to classify the error level as APPD_LEVEL_NOTICE, APPD_LEVEL_WARNING or APPD_LEVEL_ERROR.

Manage Business Transaction Snapshots

When the agent is monitoring a business transaction, it automatically creates transaction snapshots, which describe instances of the business transaction at certain points in time. Transaction snapshots are extremely useful for troubleshooting poor performance because they contain a lot of detail.

You do not have to modify anything to create these snapshots, other than create the business transaction, but you can add calls to:

  • Find out if a snapshot is being taken
  • Provide additional data in a snapshot
  • Set the URL for a snapshot

Determine if the Agent is Taking a Snapshot Now

The agent is not always collecting snapshots, because that would be expensive. By default, it collects a snapshot every ten minutes, but this schedule is configurable. See Configure Snapshot Periodic Collection Frequency in Transaction Snapshots for information about the frequency.

You can determine if a snapshot is happening using appd_bt_is_snapshotting(), which returns non-zero if the agent is collecting a snapshot. The main reason to do this is to avoid the wasted overhead for collecting user data for a snapshot or setting the snapshot URL if no snapshot is currently being collected.

Add Business Transaction User Data

You can optionally add data to transaction snapshots. For example, you might want to know which users are getting a lot of errors, or from which regions users are experiencing slow response times, or which methods are slow. In the AppDynamics UI, the data appears in the USER DATA tab of the transaction snapshot.

If a snapshot is occurring, use appd_bt_add_user_data() passing a key and value for the data that you want the snapshot to collect.

Add Snapshot URL

A snapshot URL lets Controller users share a snapshot with others. You can set a URL for the current snapshot using appd_bt_set_url().

Set Snapshot Example

void setSnapshotAttributes(appd_bt_handle btHandle, int minute, int halfsec)
{
    // do this only if the agent is collecting a snapshot
    if (appd_bt_is_snapshotting(btHandle))
    {
        char nameBuf[30];
        char valueBuf[30];
        // add custom data to the snapshot
        snprintf(nameBuf, sizeof(nameBuf), "BT:%p\n", btHandle);
        snprintf(valueBuf, sizeof(valueBuf), "Minute:%d Second:%d\n", minute, halfsec/2);
        appd_bt_add_user_data(btHandle, nameBuf, valueBuf);

        static int snapCount = 0;
        int switchVal = snapCount % 4;

        // report errors, but only ERROR_LEVEL errors are marked as error transactions
        if (switchVal)
        {
            appd_error_level errorLevel;
            bool markBtAsError;
            switch (switchVal)
            {
            case 1:
                errorLevel = APPD_LEVEL_NOTICE;
                markBtAsError = false;
                snprintf(nameBuf, sizeof(nameBuf), "NOTICE BT:%p M:%d S:%d\n", btHandle, minute, halfsec/2);
                break;
            case 2:
                errorLevel = APPD_LEVEL_WARNING;
                markBtAsError = false;
                snprintf(nameBuf, sizeof(nameBuf), "WARNING BT:%p M:%d S:%d\n", btHandle, minute, halfsec/2);
                break;
            case 3:
                errorLevel = APPD_LEVEL_ERROR;
                markBtAsError = true;
                snprintf(nameBuf, sizeof(nameBuf), "ERROR BT:%p M:%d S:%d\n", btHandle, minute, halfsec/2);
                break;
            }
            appd_bt_add_error(btHandle, errorLevel, nameBuf, markBtAsError, markbtaserror);
        }
        snapCount++;
        // set the snapshot url
        snprintf(nameBuf, sizeof(nameBuf), "http://bt-%p.com", btHandle);
        appd_bt_set_url(btHandle, nameBuf);
    }
}

Create Backends

A backend is a database or a remote service such as a message queue, HTTP service or cache service that your application uses. A backend component is not itself monitored by the application agent, but the agent monitors calls to it from instrumented servers. You need to create backends in the instrumented environment so that the agent can discover them. This involves:

  • Declaring the backend
  • Setting its identifying properties
  • Optionally configuring how the backend is presented in the AppDynamics UI
  • Adding the backend to the instrumented application

Declare the Backend

You must declare a backend using appd_backend_declare() before the agent can detect it. After you declare a backend, you don't need to declare it again if the backend is used by other business transactions in a single SDK instance. A backend must be of one of the supported types listed under Exit Call Types in the Agent SDK for C and C++ Reference.

Identify the Backend

A backend also has identifying properties, which you set using appd_backend_set_identifying_property(). The properties vary depending on the type of the backend and the types of information that you want to display. The Controller displays identifying properties in backend dashboards. You must set at least one identifying property for the type of any backend that you plan to add. 

The following shows the backend properties in the Controller UI for an ActiveMQ:


Resolve to a Tier

Add to Application

After you declare and configure the backend, add it to the application so the agent can detect it.

Backend Example

The following listing shows an example of setting up a database backend. 

// declare a backend, only once for this SDK instance
const char backendOne[] = "first backend";
appd_backend_declare(APPD_BACKEND_HTTP,  backendOne);

// set the host property
rc = appd_backend_set_identifying_property(backendOne, "HOST", "sqs-us-west-hostname");
if (rc) {
   std:cerr << "Error: appd_backend_set_identifying_property: " << rc << ".";
   return -1;
}

// do not resolve the backend to the tier
rc = appd_backend_prevent_agent_resolution(backendOne);
if (rc) {
   std:cerr << "Error: appd_backend_prevent_agent_resolution: " << rc << ".";
   return -1;
}

// add the backend
rc = appd_backend_add(backendOne);
if (rc)
{
   std:cerr << "Error: appd_backend_add: " << rc << ".";
   return -1;
} 

Manage Exit Calls

When an application makes a call to another component (a detected backend or another application server), the agent reports metrics on those calls.

Define an exit call by enclosing the code that constitutes the exit call between appd_exitcall_begin() and appd_exitcall_end() calls.  appd_exitcall_begin() returns a handle to use in subsequent routines that affect that exit call. An exit call occurs in the context of a business transaction.

You can optionally store the exit call handle in the global handle registry with a guid for easy retrieval later using appd_exitcall_store(). Retrieve the handle from the global handle registry using appd_exitcall_get().

Pass the business transaction handle and the destination backend to appd_exitcall_end().

You can optionally add details to an exit call as any arbitrary string. The details are reported in the exit call details in the transaction snapshots in the Controller UI:

You can also add errors to an exit call using appd_exitcall_add_error(). Use the enum for the error levels. You can also add an error message.

Simple Exit Call Example

// start the exit call to backendOne
appd_exitcall_handle ecHandle = appd_exitcall_begin(btHandle, backendOne);

...

// optionally store the handle in the global registry
appd_exitcall_store(ecHandle, my_ec_guid);

...
// retrieve a stored handle from the global registry
appd_exitcall_handle myEcHandle = appd_exitcall_get(my_ec_guid);
 
// set the exit call details
rc = appd_exitcall_set_details(ecHandle, "backend ONE");
if (rc) {
   std:cerr << "Error: exitcall details1";
   return -1;
}

// add an error to the exit call
appd_exitcall_add_error(ecHandle, APPD_LEVEL_ERROR, "exitcall1 error!", true);

// end the exit call
appd_exitcall_end(ecHandle)

Correlate with Other Business Transactions

A correlation header contains the information that enables the agents to continue the flow of a business transaction across multiple tiers.

An SDK agent can correlate with other SDK agents as well as other AppDynamics agents (such as Java, .NET, or PHP) that perform automatic correlation for certain types of entry and exit points. 

Correlate with an Upstream Tier

When your instrumented process receives a continuing transaction from an upstream agent that supports automatic correlation:

  1. Using a third-party http_get_header() function, extract the header named APPD_CORRELATION_HEADER_NAME from the incoming HTTP payload.

  2. Pass the header to appd_bt_begin(). For example:

    const char* hdr = http_get_header(req, APPD_CORRELATION_HEADER_NAME);
    appd_bt_handle bt = appd_bt_begin("fraud detection", hdr);

If the header retrieved by the http_get_header() function is valid, the business transaction started by the appd_bt_begin() call will be a continuation of the business transaction started by the upstream service.

Correlate with a Downstream Tier

The downstream agent is watching for a correlation header named "singularityheader" in the HTTP payload.

If your SDK agent is making an exit call to a downstream agent that supports automatic correlation:

  1. Set the name of the correlation header using APPD_CORRELATION_HEADER_NAME.
  2. Begin the exit call.
  3. Retrieve the correlation header from the exit call using the appd_exitcall_get_correlation_header() function.
  4. Using third-party HTTP functions, prepare an HTTP POST request.
  5. Inject a header named APPD_CORRELATION_HEADER_NAME with the value of the correlation header retrieved in step 3 into the outgoing payload of the HTTP request.
  6. Make the request. For example:

    const char* const APPD_CORRELATION_HEADER_NAME = "singularityheader";
    
    appd_exitcall_handle inventory = appd_exitcall_begin(bt, "inventory");
    const char* hdr = appd_exitcall_get_correlation_header(inventory);
    http_request req;
    http_init(&req, HTTP_POST, "https: //inventory/holds/%s", sku);
    http_set_header(&req, APPD_CORRELATION_HEADER_NAME, hdr);
    http_perform(&req);
    
    ...

cURL Downstream Correlation Example

The following example uses the cURL library to correlate with a downstream transaction.

#include <curl/curl.h>
. . . 
. . .
// get the correlation header into the response
class LibcurlURLGet
{
public:
    LibcurlURLGet(const std::string& url) : url(url), curlHandle(NULL) {}
    std::string GetResponse(const char* correlationHeader)
    {
        CURLcode res;
        Response response;
        curlHandle = curl_easy_init();
        if (curlHandle)
        {
            curl_easy_setopt(curlHandle, CURLOPT_URL, url.c_str());
            curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, WriteDataCallback);
            curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &response);
            if(correlationHeader && strlen(correlationHeader))
            {
 
// start new added code
 
                std::string singularityHeader(APPD_CORRELATION_HEADER_NAME);
                singularityHeader.append(": ");
                singularityHeader.append(correlationHeader);
 
// end new added code
 
                curl_slist* slistNewHeaders = 0;
                slistNewHeaders = curl_slist_append(slistNewHeaders, correlationHeader);
                curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, slistNewHeaders);
            }
            res = curl_easy_perform(curlHandle);
            if(res != CURLE_OK)
            {
            }
        }
        curl_easy_cleanup(curlHandle);
        return response.GetResponseString();
    }
private:
    class Response
    {
    public:
        std::string GetResponseString()
        {
            return sResponse;
        }
        void AppendResponse(void* ptr, size_t sizeBytes)
        {
            sResponse.append((char*)ptr, sizeBytes);
        }
    private:
        std::string sResponse;
    };
    static size_t WriteDataCallback(void* ptr, size_t size, size_t nmemb, Response* response)
    {
        response->AppendResponse(ptr, size * nmemb);
        return size * nmemb;
    }
    std::string url;
    CURL* curlHandle;
};
 
...
// start an exit call from the current transaction 
appd_exitcall_handle ecHandle1 = appd_exitcall_begin(btHandle1, tier2);

...
// before exiting get the correlation header
const char* corrHeader1 = appd_exitcall_get_correlation_header(ecHandle1);

// put the correlation in the HTTP response
std::string response = urlGet.GetResponse(corrHeader1);

...
// start the next business transaction to correlate with the previous one
appd_bt_handle btHandle = appd_bt_begin("Verify", response)

Create Custom Metrics

You can create custom metrics with the C/C++ SDK. Custom metrics supplement the built-in metrics you get with the C/C++ agent, such as the BT call count and response time.

To create custom metrics, you use two calls in the API:

  • appd_custom_metric_add() 
  • appd_custom_metric_report()

You use the appd_custom_metric_add() to declare a metric. The method signature is:

APPD_API void appd_custom_metric_add (const char *  application_context, 
                                      const char *  metric_path, 
                                      enum appd_time_rollup_type  time_rollup_type, 
                                      enum appd_cluster_rollup_type  cluster_rollup_type, 
                                      enum appd_hole_handling_type  hole_handling_type)

 

The following example illustrates a call that uses this method: 

appd_custom_metric_add("app_context", "Custom Metrics|Memory|Total Memory Usage",
APPD_TIMEROLLUP_TYPE_AVERAGE, APPD_CLUSTERROLLUP_TYPE_INDIVIDUAL,
APPD_HOLEHANDLING_TYPE_RATE_COUNTER);

 

As shown, the method passes an application context for the metric, the name of the metric ("Total Memory Usage", in the example), and the path to the metric in the Metric Browser tree (Custom Metrics > Memory). The other parameters define how the metric is processed by the Controller.  

With the metric declared, your code can then report data to the metric with the appd_custom_metric_report() method, as follows:

appd_custom_metric_report("app_context", "Custom Metrics|Memory|Total Memory Usage", 1234);

The Controller applies some standard processing functions to custom metric values just as it does to built-in metrics. These functions define, for example, how the metric is rolled up over time. The manner in which the Controller should process a custom metric depends on the nature of that metric. For instance, for a metric representing the state of the machine, it may make sense to roll up the value to the last observed value for the metric. For a call rate metric, it may make sense to roll the metric up to an average value.

The following sections provide more information on the parameters you can use to define metric handling by the Controller.

Time Rollup Type

The time_rollup_type parameter tells the Controller how to roll up values for the metric over time. There are three ways in which the Controller can roll up metrics, as follows:

  • It can calculate the average of all reported values in the time period. An example of a built-in metric that uses this is the "Average Response Time" metric.
  • Sum of all reported values in that minute. This operation behaves like a counter. An example metric is the "Calls per Minute" metric.
  • Current is the last reported value in the minute. If no value is reported in that minute, the last reported value is used. An example of a metric that uses this would be a machine state metric, such as "Max Available (MB)" metric.

Cluster Rollup Type

The appd_cluster_rollup_type parameter tells the Controller how to aggregate metric values for the tier (a cluster of nodes).

  • Individual – Aggregates the metric value by averaging the metric values across each node in the tier. For example, "Hardware Resources|Memory|Used %" is a built-in metric that uses the individual rollup type.
  • Collective – Aggregates the metric value by adding up the metric values for all the nodes in the tier. For example, "Agent|Metric Upload|Metrics uploaded" is a built-in metric that uses the collective rollup type. 

Hole Handling Type

A particular metric may not report data for a given minute. The appd_hole_handling_type parameter tells the Controller how to set the metric's count for that time slice. The count is set to zero if the hole handling type is REGULAR_COUNTER, and set to one if RATE_COUNTER. In effect, REGULAR_COUNTER does not affect aggregation while RATE_COUNTER does. 

For example, consider four time slices with the data 9, 3, 0, 12. Notice that the third time slice is zero filled, with no metric being reported for that time slice. The sum of the values is 9+3+0+12 = 24.

If the metric is REGULAR_COUNTER, the count for the third times slice would be zero, and therefore the overall count would be 1+1+0+1 = 3. If the metric is RATE_COUNTER the overall count would be 1+1+1+1 = 4. In the case of the REGULAR_COUNTER, the average would be 24/3 = 8, while for the RATE_COUNTER the average would be 24/4 = 6. 

Built-in metrics that use the regular counter hole-handling type include Average Response Time, Average CPU Used, Number of Stalls. Built-in metrics that use the rate counter hole-handling type include BT Calls per minute and Error per minute. 

APIs for Resource Management (C++ Only)

For C++ applications, you can use the BT and ExitCall classes to simplify your instrumentation.

The advantage of using these classes is that you do not need to manually call appd_bt_end() and appd_exitcall_end(). When the BT or ExitCall object goes out of scope, the SDK calls its destructor and the business transaction or exit call automatically ends. 

For example, the following code using these classes:

{
   appd::sdk::BT bt("mybt");
   appd::sdk::ExitCall ec(bt, "auth");
   if (!make_auth_call())
   {
      bt.add_error(APPD_LEVEL_ERROR, "Authorization failed");
      return -1;
   }
   issue_api_call();
}

is the equivalent of this:

{
    appd_bt_handle bt = appd_bt_begin("mybt", NULL);
    appd_exitcall_handle ec = appd_exitcall_begin(bt, "auth");
    if (!make_auth_call())
    {
       appd_bt_add_error(bt, APPD_LEVEL_ERROR, "Authorization failed", 0);
       appd_exitcall_end(ec);
       appd_bt_end(bt);
       return -1;
    }
    issue_apI_call();
    appd_exitcall_end(ec);
    appd_bt_end(bt);
}

When the BT lifetime depends on the nondeterministic lifetimes of other objects, you can use a shared pointer to a BT to keep the BT alive for the lifetimes of its dependencies. In this case the BT ends when the last reference to it ends. For example:

{
   //Initialize the BT with a shared pointer
   auto bt = std::make_shared<appd::sdk::BT>("compute");
   auto prod = createProducer(bt);
   auto consumers = createConsumers(bt, NUM_WORKERS);
   //Variable BT goes out of scope with no further references.
   //BT ends automatically.
}

For managing exit calls with complex lifetimes, consider using smart pointers std::unique_ptr<appd::sdk::ExitCall> or std::shared_ptr<appd::sdk::ExitCall>.

Terminate the Agent

Just before the application exits, terminate the agent.

appd_sdk_term();
  • No labels