mongoc_application_performance_monitoring
The MongoDB C Driver allows you to monitor all the MongoDB operations the driver executes. This event-notification system conforms to two MongoDB driver specs:
- Command Logging and Monitoring: events related to all application operations.
- SDAM Monitoring: events related to the driver's Server Discovery And Monitoring logic.
To receive notifications, create a mongoc_apm_callbacks_t with mongoc_apm_callbacks_new(), set callbacks on it, then pass it to mongoc_client_set_apm_callbacks() or mongoc_client_pool_set_apm_callbacks().
Command-Monitoring Example
example-command-monitoring.c
/* gcc example-command-monitoring.c -o example-command-monitoring \ * $(pkg-config --cflags --libs libmongoc-1.0) */ /* ./example-command-monitoring [CONNECTION_STRING] */ #include <mongoc/mongoc.h> #include <stdio.h> typedef struct { int started; int succeeded; int failed; } stats_t; void command_started (const mongoc_apm_command_started_t *event) { char *s; s = bson_as_relaxed_extended_json (mongoc_apm_command_started_get_command (event), NULL); printf ("Command %s started on %s:\n%s\n\n", mongoc_apm_command_started_get_command_name (event), mongoc_apm_command_started_get_host (event)->host, s); ((stats_t *) mongoc_apm_command_started_get_context (event))->started++; bson_free (s); } void command_succeeded (const mongoc_apm_command_succeeded_t *event) { char *s; s = bson_as_relaxed_extended_json (mongoc_apm_command_succeeded_get_reply (event), NULL); printf ("Command %s succeeded:\n%s\n\n", mongoc_apm_command_succeeded_get_command_name (event), s); ((stats_t *) mongoc_apm_command_succeeded_get_context (event))->succeeded++; bson_free (s); } void command_failed (const mongoc_apm_command_failed_t *event) { bson_error_t error; mongoc_apm_command_failed_get_error (event, &error); printf ("Command %s failed:\n\"%s\"\n\n", mongoc_apm_command_failed_get_command_name (event), error.message); ((stats_t *) mongoc_apm_command_failed_get_context (event))->failed++; } int main (int argc, char *argv[]) { mongoc_client_t *client; mongoc_apm_callbacks_t *callbacks; stats_t stats = {0}; mongoc_collection_t *collection; bson_error_t error; const char *uri_string = "mongodb://127.0.0.1/?appname=cmd-monitoring-example"; mongoc_uri_t *uri; const char *collection_name = "test"; bson_t *docs[2]; mongoc_init (); if (argc > 1) { uri_string = argv[1]; } uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); callbacks = mongoc_apm_callbacks_new (); mongoc_apm_set_command_started_cb (callbacks, command_started); mongoc_apm_set_command_succeeded_cb (callbacks, command_succeeded); mongoc_apm_set_command_failed_cb (callbacks, command_failed); mongoc_client_set_apm_callbacks (client, callbacks, (void *) &stats /* context pointer */); collection = mongoc_client_get_collection (client, "test", collection_name); mongoc_collection_drop (collection, NULL); docs[0] = BCON_NEW ("_id", BCON_INT32 (0)); docs[1] = BCON_NEW ("_id", BCON_INT32 (1)); mongoc_collection_insert_many (collection, (const bson_t **) docs, 2, NULL, NULL, NULL); /* duplicate key error on the second insert */ mongoc_collection_insert_one (collection, docs[0], NULL, NULL, NULL); mongoc_collection_destroy (collection); mongoc_apm_callbacks_destroy (callbacks); mongoc_uri_destroy (uri); mongoc_client_destroy (client); printf ("started: %d\nsucceeded: %d\nfailed: %d\n", stats.started, stats.succeeded, stats.failed); bson_destroy (docs[0]); bson_destroy (docs[1]); mongoc_cleanup (); return EXIT_SUCCESS; }
This example program prints:
Command drop started on 127.0.0.1: { "drop" : "test" } Command drop succeeded: { "ns" : "test.test", "nIndexesWas" : 1, "ok" : 1.0 } Command insert started on 127.0.0.1: { "insert" : "test", "ordered" : true, "documents" : [ { "_id" : 0 }, { "_id" : 1 } ] } Command insert succeeded: { "n" : 2, "ok" : 1.0 } Command insert started on 127.0.0.1: { "insert" : "test", "ordered" : true, "documents" : [ { "_id" : 0 } ] } Command insert succeeded: { "n" : 0, "writeErrors" : [ { "index" : 0, "code" : 11000, "errmsg" : "duplicate key" } ], "ok" : 1.0 } started: 3 succeeded: 3 failed: 0
The output has been edited and formatted for clarity. Depending on your server configuration, messages may include metadata like database name, logical session ids, or cluster times that are not shown here.
The final "insert" command is considered successful, despite the writeError, because the server replied to the overall command with "ok": 1.
Sdam Monitoring Example
example-sdam-monitoring.c
/* gcc example-sdam-monitoring.c -o example-sdam-monitoring \ * $(pkg-config --cflags --libs libmongoc-1.0) */ /* ./example-sdam-monitoring [CONNECTION_STRING] */ #include <mongoc/mongoc.h> #include <stdio.h> typedef struct { int server_changed_events; int server_opening_events; int server_closed_events; int topology_changed_events; int topology_opening_events; int topology_closed_events; int heartbeat_started_events; int heartbeat_succeeded_events; int heartbeat_failed_events; } stats_t; static void server_changed (const mongoc_apm_server_changed_t *event) { stats_t *context; const mongoc_server_description_t *prev_sd, *new_sd; context = (stats_t *) mongoc_apm_server_changed_get_context (event); context->server_changed_events++; prev_sd = mongoc_apm_server_changed_get_previous_description (event); new_sd = mongoc_apm_server_changed_get_new_description (event); printf ("server changed: %s %s -> %s\n", mongoc_apm_server_changed_get_host (event)->host_and_port, mongoc_server_description_type (prev_sd), mongoc_server_description_type (new_sd)); } static void server_opening (const mongoc_apm_server_opening_t *event) { stats_t *context; context = (stats_t *) mongoc_apm_server_opening_get_context (event); context->server_opening_events++; printf ("server opening: %s\n", mongoc_apm_server_opening_get_host (event)->host_and_port); } static void server_closed (const mongoc_apm_server_closed_t *event) { stats_t *context; context = (stats_t *) mongoc_apm_server_closed_get_context (event); context->server_closed_events++; printf ("server closed: %s\n", mongoc_apm_server_closed_get_host (event)->host_and_port); } static void topology_changed (const mongoc_apm_topology_changed_t *event) { stats_t *context; const mongoc_topology_description_t *prev_td; const mongoc_topology_description_t *new_td; mongoc_server_description_t **prev_sds; size_t n_prev_sds; mongoc_server_description_t **new_sds; size_t n_new_sds; size_t i; mongoc_read_prefs_t *prefs; context = (stats_t *) mongoc_apm_topology_changed_get_context (event); context->topology_changed_events++; prev_td = mongoc_apm_topology_changed_get_previous_description (event); prev_sds = mongoc_topology_description_get_servers (prev_td, &n_prev_sds); new_td = mongoc_apm_topology_changed_get_new_description (event); new_sds = mongoc_topology_description_get_servers (new_td, &n_new_sds); printf ("topology changed: %s -> %s\n", mongoc_topology_description_type (prev_td), mongoc_topology_description_type (new_td)); if (n_prev_sds) { printf (" previous servers:\n"); for (i = 0; i < n_prev_sds; i++) { printf (" %s %s\n", mongoc_server_description_type (prev_sds[i]), mongoc_server_description_host (prev_sds[i])->host_and_port); } } if (n_new_sds) { printf (" new servers:\n"); for (i = 0; i < n_new_sds; i++) { printf (" %s %s\n", mongoc_server_description_type (new_sds[i]), mongoc_server_description_host (new_sds[i])->host_and_port); } } prefs = mongoc_read_prefs_new (MONGOC_READ_SECONDARY); /* it is safe, and unfortunately necessary, to cast away const here */ if (mongoc_topology_description_has_readable_server ((mongoc_topology_description_t *) new_td, prefs)) { printf (" secondary AVAILABLE\n"); } else { printf (" secondary UNAVAILABLE\n"); } if (mongoc_topology_description_has_writable_server ((mongoc_topology_description_t *) new_td)) { printf (" primary AVAILABLE\n"); } else { printf (" primary UNAVAILABLE\n"); } mongoc_read_prefs_destroy (prefs); mongoc_server_descriptions_destroy_all (prev_sds, n_prev_sds); mongoc_server_descriptions_destroy_all (new_sds, n_new_sds); } static void topology_opening (const mongoc_apm_topology_opening_t *event) { stats_t *context; context = (stats_t *) mongoc_apm_topology_opening_get_context (event); context->topology_opening_events++; printf ("topology opening\n"); } static void topology_closed (const mongoc_apm_topology_closed_t *event) { stats_t *context; context = (stats_t *) mongoc_apm_topology_closed_get_context (event); context->topology_closed_events++; printf ("topology closed\n"); } static void server_heartbeat_started (const mongoc_apm_server_heartbeat_started_t *event) { stats_t *context; context = (stats_t *) mongoc_apm_server_heartbeat_started_get_context (event); context->heartbeat_started_events++; printf ("%s heartbeat started\n", mongoc_apm_server_heartbeat_started_get_host (event)->host_and_port); } static void server_heartbeat_succeeded (const mongoc_apm_server_heartbeat_succeeded_t *event) { stats_t *context; char *reply; context = (stats_t *) mongoc_apm_server_heartbeat_succeeded_get_context (event); context->heartbeat_succeeded_events++; reply = bson_as_canonical_extended_json (mongoc_apm_server_heartbeat_succeeded_get_reply (event), NULL); printf ( "%s heartbeat succeeded: %s\n", mongoc_apm_server_heartbeat_succeeded_get_host (event)->host_and_port, reply); bson_free (reply); } static void server_heartbeat_failed (const mongoc_apm_server_heartbeat_failed_t *event) { stats_t *context; bson_error_t error; context = (stats_t *) mongoc_apm_server_heartbeat_failed_get_context (event); context->heartbeat_failed_events++; mongoc_apm_server_heartbeat_failed_get_error (event, &error); printf ( "%s heartbeat failed: %s\n", mongoc_apm_server_heartbeat_failed_get_host (event)->host_and_port, error.message); } int main (int argc, char *argv[]) { mongoc_client_t *client; mongoc_apm_callbacks_t *cbs; stats_t stats = {0}; const char *uri_string = "mongodb://127.0.0.1/?appname=sdam-monitoring-example"; mongoc_uri_t *uri; bson_t cmd = BSON_INITIALIZER; bson_t reply; bson_error_t error; mongoc_init (); if (argc > 1) { uri_string = argv[1]; } uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", uri_string, error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); cbs = mongoc_apm_callbacks_new (); mongoc_apm_set_server_changed_cb (cbs, server_changed); mongoc_apm_set_server_opening_cb (cbs, server_opening); mongoc_apm_set_server_closed_cb (cbs, server_closed); mongoc_apm_set_topology_changed_cb (cbs, topology_changed); mongoc_apm_set_topology_opening_cb (cbs, topology_opening); mongoc_apm_set_topology_closed_cb (cbs, topology_closed); mongoc_apm_set_server_heartbeat_started_cb (cbs, server_heartbeat_started); mongoc_apm_set_server_heartbeat_succeeded_cb (cbs, server_heartbeat_succeeded); mongoc_apm_set_server_heartbeat_failed_cb (cbs, server_heartbeat_failed); mongoc_client_set_apm_callbacks (client, cbs, (void *) &stats /* context pointer */); /* the driver connects on demand to perform first operation */ BSON_APPEND_INT32 (&cmd, "buildinfo", 1); mongoc_client_command_simple (client, "admin", &cmd, NULL, &reply, &error); mongoc_uri_destroy (uri); mongoc_client_destroy (client); printf ("Events:\n" " server changed: %d\n" " server opening: %d\n" " server closed: %d\n" " topology changed: %d\n" " topology opening: %d\n" " topology closed: %d\n" " heartbeat started: %d\n" " heartbeat succeeded: %d\n" " heartbeat failed: %d\n", stats.server_changed_events, stats.server_opening_events, stats.server_closed_events, stats.topology_changed_events, stats.topology_opening_events, stats.topology_closed_events, stats.heartbeat_started_events, stats.heartbeat_succeeded_events, stats.heartbeat_failed_events); bson_destroy (&cmd); bson_destroy (&reply); mongoc_apm_callbacks_destroy (cbs); mongoc_cleanup (); return EXIT_SUCCESS; }
Start a 3-node replica set on localhost with set name "rs" and start the program:
./example-sdam-monitoring "mongodb://localhost:27017,localhost:27018/?replicaSet=rs"
This example program prints something like:
topology opening topology changed: Unknown -> ReplicaSetNoPrimary secondary UNAVAILABLE primary UNAVAILABLE server opening: localhost:27017 server opening: localhost:27018 localhost:27017 heartbeat started localhost:27018 heartbeat started localhost:27017 heartbeat succeeded: { ... reply ... } server changed: localhost:27017 Unknown -> RSPrimary server opening: localhost:27019 topology changed: ReplicaSetNoPrimary -> ReplicaSetWithPrimary new servers: RSPrimary localhost:27017 secondary UNAVAILABLE primary AVAILABLE localhost:27019 heartbeat started localhost:27018 heartbeat succeeded: { ... reply ... } server changed: localhost:27018 Unknown -> RSSecondary topology changed: ReplicaSetWithPrimary -> ReplicaSetWithPrimary previous servers: RSPrimary localhost:27017 new servers: RSPrimary localhost:27017 RSSecondary localhost:27018 secondary AVAILABLE primary AVAILABLE localhost:27019 heartbeat succeeded: { ... reply ... } server changed: localhost:27019 Unknown -> RSSecondary topology changed: ReplicaSetWithPrimary -> ReplicaSetWithPrimary previous servers: RSPrimary localhost:27017 RSSecondary localhost:27018 new servers: RSPrimary localhost:27017 RSSecondary localhost:27018 RSSecondary localhost:27019 secondary AVAILABLE primary AVAILABLE topology closed Events: server changed: 3 server opening: 3 server closed: 0 topology changed: 4 topology opening: 1 topology closed: 1 heartbeat started: 3 heartbeat succeeded: 3 heartbeat failed: 0
The driver connects to the mongods on ports 27017 and 27018, which were specified in the URI, and determines which is primary. It also discovers the third member, "localhost:27019", and adds it to the topology.
Author
MongoDB, Inc
Copyright
2009-present, MongoDB, Inc.