Download PDF
Download page Use the C/C++ SDK.
Use the C/C++ SDK
On this page:
Related pages:
This page provides an overview of how to use the C/C++ SDK.
The SDK provides routines for creating/managing business transactions, transaction snapshots, backends, exit points, and custom metrics.
To instrument your applications with the C/C++ SDK, obtain the most current version of the C/C++ SDK. See C/C++ SDK.
Add the Cisco AppDynamics Header File to the Application
After downloading the SDK, you are ready to add Splunk AppDynamicsinstrumentation to your C/C++ application. The first step is to include the Splunk AppDynamicsheader 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 C/C++ SDK Reference for a 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;
To create the default application context, declare an appd_config struct
with the Controller settings.
For example:
struct 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 SDK
In your main
function, call these initialization functions:
- Initialize the
config
structure as shown in the previous section. - Initialize the SDK by passing the configuration structure to
appd_sdk_init()
.
If appd_sdk_init()
returns zero, the SDK is initialized successfully. Non-zero indicates failure because the SDK 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 << std::endl;
return -1;
}
All SDK calls including appd_sdk_init()
and fork()
should only occur in the forked process, never in the parent process. Each child call should behave independently and cannot share handles between the process and other SDK-instrumented child processes.
Create Alternate Application Contexts
The SDK presumes a default application context that has no name associated with it.
To create the default application context, use the appd_config_init()
command which returns a pointer to the default configuration structure. Then, use setter commands to modify the fields within the structure. Once all relevant fields are set, use the appd_sdk_init()
call to initialize the SDK with the default application context.
All other application contexts are created with an associated name and a collection of separate setter methods.
To create an alternate application context, pass the appd_context_config_init()
command the name of your alternate application context. This will return a pointer to the alternate application context configuration structure. Then, use the _context_
setter commands to modify the fields within the structure. Once all relevant fields are set, use the appd_sdk_add_app_context()
call to initialize the SDK, passing it the alternate application context structure pointer.
// create the alternate application context
appd_context_config* cfg2 = appd_context_config_init("My_Context");
...
// add additional setter commands to initialize the alternate application context
appd_context_config_set_controller_account(cfg2, ac.second.ac_account_get().c_str());
appd_context_config_set_controller_access_key(cfg2, ac.second.ac_access_key_get().c_str());
appd_context_config_set_controller_host(cfg2, ac.second.ac_host_get().c_str());
appd_context_config_set_controller_port(cfg, ac.second.ac_port_get());
appd_context_config_set_controller_use_ssl(cfg2, ac.second.ac_use_ssl_get() ? 1 : 0);
appd_context_config_set_app_name(cfg2, ac.second.ac_app_get().c_str());
appd_context_config_set_tier_name(cfg2, ac.second.ac_tier_get().c_str());
appd_context_config_set_node_name(cfg2, ac.second.ac_node_get().c_str());
// initialize the alternate application context
appd_sdk_add_app_context(cfg2);
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 C/C++ 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 example demonstrates 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.
Create Business Transactions With an Alternate Application Context
You can create a business transaction with an alternate application context using appd_bt_begin_with_app_context()
and appd_bt_end()
calls. In addition to the parameters passed to appd_bt_begin()
, the appd_bt_begin_with_app_context()
also takes an application context name string, as defined by the call to appd_context_config_init()
.
appd_bt_begin_with_app_context()
returns a handle to use in subsequent routines that affect that business transaction.
The following example demonstrates setting a business transaction with an alternate application context:
// create the alternate application context
appd_context_config* cfg2 = appd_context_config_init("My_Context");
...
// add additional setter commands to initialize the alternate application context
appd_context_config_set_controller_account(cfg2, ac.second.ac_account_get().c_str());
appd_context_config_set_controller_access_key(cfg2, ac.second.ac_access_key_get().c_str());
appd_context_config_set_controller_host(cfg2, ac.second.ac_host_get().c_str());
appd_context_config_set_controller_port(cfg, ac.second.ac_port_get());
appd_context_config_set_controller_use_ssl(cfg2, ac.second.ac_use_ssl_get() ? 1 : 0);
appd_context_config_set_app_name(cfg2, ac.second.ac_app_get().c_str());
appd_context_config_set_tier_name(cfg2, ac.second.ac_tier_get().c_str());
appd_context_config_set_node_name(cfg2, ac.second.ac_node_get().c_str());
// initialize the alternate application context
appd_sdk_add_app_context(cfg2);
// start the "Checkout" transaction with the alternate application context
appd_bt_handle btHandle = appd_bt_begin_with_app_context("My_Context", "Checkout", NULL);
// end the transaction
appd_bt_end(btHandle);
When the business transaction ends, via a call to appd_bt_end()
, the agent stops reporting metrics for the business transaction. 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.
Add Business Transaction Errors
Use appd_bt_add_error()
to report business transaction errors. If you set this function parameter, markBTAsError
the transaction is reported as an error transaction when the error occurs. For the function {{appd_bt_add_error,}},
the Controller database truncates log messages that exceed 5,000 characters.
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 does not constantly collect screenshot due to high cost. By default, it collects a snapshot every ten minutes, but this schedule is configurable. See Configure Snapshot Periodic Collection 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, from which regions users are experiencing slow response times or which methods are slow. In the Controller 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. For the function {{appd_bt_add_user_data,}}
, the Controller database truncates log messages that exceed 5,000 characters.
Add Snapshot URL
A snapshot URL allows Controller users to 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 Controller 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 C/C++ SDK 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
By default, the Controller doesn't display a detected backend as a separate entity in flowmaps, but the agent reports its metrics as part of the downstream tier. If you want to display the backend as a separate component and not resolved to the tier, use appd_backend_prevent_agent_resolution()
. See Resolve Remote Services to Tiers.
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 with appd_exitcall_store()
. Retrieve the handle from the global handle registry with 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 can correlate with other SDKs as well as other Splunk AppDynamicsagents—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:
Using a third-party
http_get_header()
function, extract the header namedAPPD_CORRELATION_HEADER_NAME
from the incoming HTTP payload.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);
CPP
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:
- Set the name of the correlation header using
APPD_CORRELATION_HEADER_NAME
. - Begin the exit call.
- Retrieve the correlation header from the exit call using the
appd_exitcall_get_correlation_header()
function. - Using third-party HTTP functions, prepare an HTTP POST request.
- 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. 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); ...
CPP
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 to supplement built-in metrics such as Business Transaction call count and response time. You can also create metrics for multiple application contexts.
Custom metrics are dependent on the application context; in situations with multiple contexts, you must specify the correct context name for custom metrics to apply to the associated application context. See Create Alternate Application Contexts.
If you create a custom metric in a node, the custom metric will appear in every other node in the tier.
For example, the default application context uses a null pointer or empty string as its name. The appd_sdk_init
method tells the SDK to connect to the Controller and create the default application context (i.e. no name is specified).
struct appd_config* cfg = appd_config_init();
appd_config_set_controller_host(cfg, host.c_str());
appd_config_set_controller_port(cfg, port);
appd_config_set_controller_account(cfg, account_name.c_str());
appd_config_set_controller_access_key(cfg, access_key.c_str());
appd_config_set_controller_use_ssl(cfg, useSSL?1:0);
appd_config_set_app_name(cfg, app_name.c_str());
appd_config_set_tier_name(cfg, tier_name.c_str());
appd_config_set_node_name(cfg, node_name.c_str());
appd_sdk_init(cfg);
To specify an alternative application context, you must provide a name string (not a default null pointer). Use the appd_sdk_add_appd_context
method to create an alternative application context. In the example below, we create an alternative application context called "context2." You will use that name string to create custom metrics, not the actual configuration structure.
const char* name = "context2";
struct appd_context_config* cfg2 = appd_context_config_init(name));
appd_context_config_set_controller_account(cfg2, ac.second.ac_account_get().c_str());
appd_context_config_set_controller_access_key(cfg2, ac.second.ac_access_key_get().c_str());
appd_context_config_set_controller_host(cfg2, ac.second.ac_host_get().c_str());
appd_context_config_set_controller_port(cfg, ac.second.ac_port_get());
appd_context_config_set_controller_use_ssl(cfg2, ac.second.ac_use_ssl_get() ? 1 : 0);
appd_context_config_set_app_name(cfg2, ac.second.ac_app_get().c_str());
appd_context_config_set_tier_name(cfg2, ac.second.ac_tier_get().c_str());
appd_context_config_set_node_name(cfg2, ac.second.ac_node_get().c_str());
appd_sdk_add_app_context(cfg2);
The Custom Metrics path may or may not go the same Controller; similar to business transactions in separate nodes, your program can process two business transactions from different nodes: one aligns with the default application context and the other with the alternative application context. You must specify that your method strings begin with Custom Metrics|
, not Custom Metric|
.
You can use the following methods to create a custom metric:
appd_custom_metric_add()
appd_custom_metric_report()
The appd_custom_metric_add()
method registers a custom metric for the default context.
appd_custom_metric_add(nullptr, "Custom Metrics|my_default_custom_metric" , time_rollup_type, cluster_rollup_type, hole_handling_type);
The appd_custom_metric_report()
creates an application context named "my_default_custom_metric
."
appd_custom_metric_report(nullptr, "Custom Metrics|my_default_custom_metric",some_value);
For an alternative context, the appd_custom_metric_add()
method registers a custom metric for the alternative application context.
appd_custom_metric_add(name, "Custom Metrics|my_second_custom_metric" , time_rollup_type, cluster_rollup_type, hole_handling_type);
or
appd_custom_metric_add("context2", "Custom Metrics|my_second_custom_metric" , time_rollup_type, cluster_rollup_type, hole_handling_type);
The appd_custom_metric_report
creates a default application context and an alternative application context named my_second_custom_metric
.
appd_custom_metric_report(name, "Custom Metrics|my_second_custom_metric",some_value);
appd_custom_metric_report("context2", "Custom Metrics|my_second_custom_metric",some_value);
In the Controller, the custom metrics folder appears under the associated tier as "Custom Metrics" in the Metric Browser with two entities: my_default_custom_metric
and my_second_custom_metric
.
The following example illustrates a call with both method signatures.
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);
The method passes the application context ("Total Memory Usage") and the path to the metric (Custom Metrics > Memory in the Metric Browser) to the Controller. The additional parameters define how the metric is processed. 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);
See C/C++ SDK Resource Management to simplify your instrumentation on C++ applications.
Metric Handling Parameters
Like built-in metrics, the Controller applies some standard processing functions to custom metric values. These functions can define, for example, how the metric is rolled up over time. How the Controller processes a custom metric depends on the nature of the metric; for a metric that represents the state of the machine, the Controller captures the last observed value for the metric. For a call rate metric, the Controller uses an average value. The following section provides information about 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 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
, and Number of Stalls
. Built-in metrics that use the rate counter hole-handling type include BT Calls per minute
and E
rrors per minute
.
Generate Call Graphs
You can use the C/C++ SDK to instrument methods so that they are reported and displayed in the call graph. You can instrument methods in one of the following ways:
- Use the
APPD_AUTO_FRAME
macro, C++ only - Use
appd_frame_begin
andappd_frame_end
Instrument Methods with the APPD_AUTO_FRAME
Macro
You can use the APPD_AUTO_FRAME
macro to instrument applications built on C++ frameworks. The macro is equivalent to calling the appd_frame_begin
and appd_frame_end
methods.
Specify the business transaction that you want to instrument calls for, and configure the root of the call graph.
appd::sdk::BT bt("mybt"); APPD_AUTO_FRAME(bt); method1(bt); method2(bt);
CODEInstrument the methods as shown below.
void method1(appd::sdk::BT& bt) { APPD_AUTO_FRAME(bt); // Code for method1... } void method2(appd::sdk::BT& bt) { APPD_AUTO_FRAME(bt); // Code for method2... }
CODEThe methods must have access to the business transaction so the business transaction can be passed into the method as an argument.
Instrument Methods with the appd_frame_begin
and appd_frame_end
Functions
You can instrument methods for applications that are built on non-C++ frameworks. To instrument a method, you call appd_frame_begin
at the beginning of the method, and appd_frame_end
at the end of the method. When you do this, the duration of the method call is automatically calculated and reported in the call graph.
If your method makes an exit call, you can instrument the exit call by wrapping the exit call code between appd_exitcall_begin
and appd_exitcall_end
.
The code samples below show three methods that are each instrumented using appd_frame_begin
and appd_frame_end
, and then called in a root method, main()
. The second sample method, method2()
, contains an exit call.
void method1( )
{
appd_frame_handle frameHandle = appd_frame_begin(btHandle, APPD_FRAME_TYPE_CPP, nullptr, APPD_FUNCTION_NAME, __FILE__, __LINE__);
// code for method1
appd_frame_end(btHandle, frameHandle);
}
void method2()
{
appd_frame_handle frameHandle = appd_frame_begin(btHandle, APPD_FRAME_TYPE_CPP, nullptr, APPD_FUNCTION_NAME, __FILE__, __LINE__);
// code for method2
appd_exitcall_handle exitCallHandle = appd_exitcall_begin(btHandle, BACKEND_NAME);
// code for exit call
appd_exitcall_end(exitCallHandle);
appd_frame_end(btHandle, frameHandle);
}
void method3()
{
// To illustrate instrumenting a method where this SDK cannot be used
// method3 is a wrapper for the call of the actual method which will show up in the call graph
appd_frame_handle frameHandle = appd_frame_begin(btHandle, APPD_FRAME_TYPE_CPP, "Test", "Compute", "C:\\modules\\source\\Test.cs", 143);
// call the wrapped method
appd_frame_end(btHandle, frameHandle);
}
The main()
method below is the root of the call graph. It specifies the business transaction that invokes the three methods above by passing the transaction into appd_bt_begin
and appd_bt_end
.
int main(int argc, char **argv)
{
// initialize AppDynamics
btHandle = appd_bt_begin(TRANSACTION_NAME, "");
appd_frame_handle frameHandle = appd_frame_begin(btHandle, APPD_FRAME_TYPE_CPP, nullptr, APPD_FUNCTION_NAME, __FILE__, __LINE__);
// code for main
method1();
method2();
method3();
appd_frame_end(btHandle, frameHandle);
appd_bt_end(btHandle);
}
The sample methods above are represented by the call graph shown below:
Generate Pre-populated Call Graphs (C++ Only)
The previous section describes instrumenting your C/C++ application by calling the instrumentation methods in your code. However, if you do not have the option of modifying your source code, you can generate call graphs that are populated with data from sources such as log files.
To generate a pre-populated call graph:
Create the root of the call graph by instantiating a
CallGraph
class.
TheCallGraph
class takes the following parameters:bt
: The business transaction.class_name
: The name of the class that contains the root of the call graph.method_name
: The name of the method that represents the root of the call graph.file_path
: The file path to the class.line_number
: The line number of the method.time_msec
: The time taken by this frame (method) in milliseconds.frame_type
: The type of the frame (the language for the method). Currently,APPD_FRAME_TYPE_CPP
is the only option.
The following example demonstrates a
CallGraph
instantiation.appd::sdk::CallGraph callGraph(bt, "Class1", "main", "/src/file1.cc", 276, 100, APPD_FRAME_TYPE_CPP);
CODECreate the call graph tree by calling
add_child
on the root and on any of the added children:callGraph.root() .add_child("Class2", "method1", "/src/file2.cc"), 101, 40, APPD_FRAME_TYPE_CPP) .add_child("Class2", "method2", "/src/file2.cc"), 523, 30, APPD_FRAME_TYPE_CPP); auto& cge1 = callGraph.root() .add_child("Class3", "method1", "/src/file3.cc"), 27, 30, APPD_FRAME_TYPE_CPP); cge1.add_child("Class3", "method2", "/src/file3.cc"), 430, 15, APPD_FRAME_TYPE_CPP);
CODECall
add_to_snapshot
on the call graph. For this to work, the business transaction must be snapshotting.callGraph.add_to_snapshot();
CODE
Analytics Support for C++ SDK
Two of the existing API methods, which were previously only used for adding data to a snapshot, will now send Analytics data if Analytics is enabled for the current Business Transaction on the Controller:
Set URL for a snapshot: the URL is set for a snapshot if one is occurring. The data should be either 7-bit ASCII or UTF-8. You can call this function when a snapshot does not occur. When the given Business Transaction is not snapshotting, this function returns immediately. However, if extracting the data to pass to this function is expensive, you can use
appd_bt_is_snapshotting
to check if the Business Transaction is snapshotting before extracting the data and calling this function.void appd_bt_set_url(appd_bt_handle bt, const char* url);
CODE
- param bt: The business transaction to add the user data to, if it is taking a snapshot.
- param url: The value of the URL for the snapshot as 7-bit ASCII or UTF-8.
Add user data to a snapshot: the data should be either 7-bit ASCII or UTF-8. You can call this function when a snapshot does not occur or if Analytics is not enabled. If the data is only for snapshotting and extracting the data to pass to this function is expensive, you can use
appd_bt_is_snapshotting
to check if the Business Transaction is snapshotting before extracting the data, and calling this function.void appd_bt_add_user_data(appd_bt_handle bt, const char* key, const char* value);
CODE- param bt: The business transaction to add the user data to, if it is taking a snapshot.
- param key: The name of the user data to add to the snapshot as 7-bit ASCII or UTF-8.
- param value: The value of the user data to add to the snapshot as 7-bit ASCII or UTF-8.
These API methods existed previously, but their behavior did not extend to reporting data to Analytics. To correctly connect to Analytics, initialize using the following configuration APIs:
void appd_config_set_analytics_host(struct appd_config* cfg, const char* host);
- param host: The host for the Analytics Agent, which defaults to
localhost
.
void appd_config_set_analytics_port(struct appd_config* cfg, const unsigned short port);
- param port: The port that the Analytics Agent listens on, which defaults to 9090.
Terminate the Agent
Just before the application exits, terminate the SDK:
appd_sdk_term();