The Relicans

loading...

New Relic's C SDK

wyhaines profile image Kirk Haines ・5 min read

The New Relic C SDK

New Relic supports a number of different languages with comprehensive libraries for monitoring: Go, Java, .NET, Node.js, PHP, Python, and Ruby. In addition, there is a C language SDK that can be used to instrument C and C++ software, as well as the starting block to build language support for any language that is not in the above list that supports binding to a C library.

This enables rather broad software accessibility to New Relic monitoring. The C SDK itself is available from GitHub at https://github.com/newrelic/c-sdk.

So let's take a look at the anatomy of a very simple program to use this SDK, to get a feel for what actually happens at a low level when one of the comprehensive libraries is sending events to New Relic.

The Prerequisites

The C SDK, like the New Relic PHP Agent, requires that an external process, the New Relic Daemon Process, be running, in order to facilitate communication between the local application, and the remote New Relic service. This agent is bundled into the C SDK repository, and it will be built when the SDK itself is compiled if both are built with a simple:

make
Enter fullscreen mode Exit fullscreen mode

This daemon must be running before any program that uses the C SDK is started, so ensure that you have started it either manually, or via some sort of system process manager.

Once it is running, you are ready to instrument your C code and see data from that instrumentation in your New Relic account.

Setting Up The Configuration Structures

The first step in using the C SDK is to call newrelic_create_app_config() to set up the configuration which will be needed in a couple of steps when you connect your application.

This is done with a call that looks something like this:

config = newrelic_create_app_config("APP_NAME", "NEW_RELIC_LICENSE_KEY");
Enter fullscreen mode Exit fullscreen mode

Replace APP_NAME and NEW_RELIC_LICENSE_KEY with the name that your application will have its information recorded under in your New Relic account, and the key that this application will use to access your account, respectively. You can get the key from your New Relic Dashboard.

Setting Up Logging

Once the config is initialized, it is time to set up a log for the New Relic agent code to report to. This is done through the use of the newrelic_configure_log() function. This function can fail if, for example, the destination given for the log is not writeable, so the return value of the function must be checked for a non-zero value and handled as an error.

if (!newrelic_configure_log("log/myapp.log", _LOGLEVEL_)) {
  printf("Failed to initialize logging. Ensure that 'log/myapp.log' is writeable.\n");
  return -1;
}
Enter fullscreen mode Exit fullscreen mode

In this example, _LOGLEVEL_ should be replaced with an appropriate constant indicating the granularity of logging that is desired. The following levels are available:

  • NEWRELIC_LOG_ERROR
  • NEWRELIC_LOG_WARNING
  • NEWRELIC_LOG_INFO
  • NEWRELIC_LOG_DEBUG

Initialization

At this point, it's almost time to start sending data to New Relic! The next step is to initialize the connection to the New Relic Daemon using newrelic_init(). If this fails, treat it as a fatal error, much like the failure to set up the logging in the prior step.

if (!newrelic_init(NULL, 0)) {
  printf("Failed to connect to the New Relic Daemon. Please ensure that it is running.\n");
  return -1;
}
Enter fullscreen mode Exit fullscreen mode

Create the App Structure

Once those steps are complete, you are ready to connect your application to New Relic and to start recording events. To do this, use the newrelic_create_app() function, which will return a pointer to an app that you will use for all further actions involving the events that you wish to record. Once that pointer is returned, the config that you created in an earlier step can be destroyed. The newrelic_create_app() function takes a number of milliseconds which is the maximum amount of time that it will wait for an end-to-end connection to be established. If any data is sent before the connection is established, it will simply be lost.

// Wait a maximum of 8 seconds to establish the connection.
app = newrelic_create_app(config, 8000);
newrelic_destroy_app_config(&config);
Enter fullscreen mode Exit fullscreen mode

Congratulations! Your code is ready to start sending events to New Relic!

Great, you say. Let's do that!

Actually Sending Data

The C SDK supports a couple fundamental operations for sending events to New Relic. These are web transactions and non-web transactions. These are, predictably, created using the newrelic_start_web_transaction() and the newrelic_start_non_web_transaction() functions, respectively.

Either type of transaction is ended with a call to newrelic_end_transction(), which should be placed after the last action in the sequence that is being timed with a transaction.

Thus, for both types of transaction, the pattern is the same. It looks something like this:

newrelic_txn_t *transaction;
transaction = newrelic_start_non_web_transaction(app, "TRANSACTION_LABEL");

/* ... Do stuff that you want to measure ... */

newrelic_end_transaction(transaction);
Enter fullscreen mode Exit fullscreen mode

In this example, replace TRANSACTION_LABEL with an appropriate tag identifying what sort of transaction is being recorded.

Segments

Within a transaction, one may insert multiple segments, in order to measure individual sections of a transaction. There are several different types of segments, and this article is not going to go into each, so you may read more about the details https://docs.newrelic.com/docs/agents/c-sdk/instrumentation/instrument-your-app-c-sdk/.

However, the basic flow for each is to bracket the code that you want to measure within a segment inside a pair of start/end segment function calls. The whole pattern, adding to the example above, is something like this:

newrelic_txn_t *transaction;
transaction = newrelic_start_non_web_transaction(app, "TRANSACTION_LABEL");

segment = newrelic_start_segment(
  transaction,
  "SEGMENT_NAME",
  "SEGMENT_CATEGORY");

/* Do stuff in this segment */

newrelic_end_segment(transaction, &segment);

/* Optionally start and end more transactions */

newrelic_end_transaction(transaction);
Enter fullscreen mode Exit fullscreen mode

In this example, SEGMENT_NAME is a label describing this particular segment, while SEGMENT_CATEGORY is an arbitrary category label. You may group multiple different segment names under a single SEGMENT_CATEGORY, allowing you to logically associate your segments in whatever way makes sense within your application.

Wrapping up -- Errors

If there is an error in the transaction, your code can, and should, send that error to New Relic. The call to do this is newrelic_notice_error(). Only a single error per transaction is allowed, so if there are multiple calls to this function within a transaction, the most recent one with the highest priority (where priority is simply an integer value) is the one that will be sent.

It is used like this:

newrelic_notice_error(
  transaction,
  100,
  "ERROR_MESSAGE",
  "ERROR_CLASS"
);
Enter fullscreen mode Exit fullscreen mode

In this example, ERROR_MESSAGE should be a clear description of the error that occurred, while ERROR_CLASS should be some sort of logical group for the kind of error that occurred.

The TL;DR To Wrap It Up

The C SDK is full of other features, with functions to control every aspect of the instrumentation of your C or C++ programs with New Relic. All of the source code is available on GitHub, while exhaustive documentation of all of the functions and the data structures that they use, as well as complete, compilable examples, are available on the GitHub pages for the project.

Happy Hacking!

Discussion (0)

pic
Editor guide